Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

706 lines
25 KiB

;----------------------M O D U L E H E A D E R----------------------------;
; ;
; Module Name: SORT.ASM ;
; ;
; History: SORT.C ;
; Created by Charles Whitmer 12/30/1986 ;
; Modified by Mitchel B. London 08/05/1987 ;
; ;
; SORT.ASM - translation of SORT.C ;
; CreateModule David Weise ;
; Other Modules Amit Chatterjee 08/09/1988 ;
; ;
; Copyright (c) 1985 - 1988 Microsoft Corporation ;
; ;
; General Description: ;
; ;
; The SORT module creates and maintains a tree of nodes, each node ;
; having a KEY value and a TAG field. The KEY field is used to or- ;
; -ganize the tree into a heap. ;
; The heap tree is implemented using an array, where if ;
; parent node occurs in position i, its left child will be at index ;
; (2 * i) and the right chaild at index (2 * i + 1). ;
; The Module ensures that at any instant, the root node ;
; of any subtree has the least key value in the subtree. ;
; First few positions in the array are used for storing ;
; a header for the tree. ;
; ;
; SubModules: ;
; ;
; 1. CreatePQ: ;
; Allocates space for the heaptree and its header ;
; and initializes the header. ;
; 2. InsertPQ: ;
; Inserts a node into the heap ensuring that the heap ;
; property is not violated. ;
; 3. MinPQ: ;
; Returns the tag value associated with the lowest ;
; key in the heap. (node is not deleted) ;
; 4. ExtractPQ: ;
; Return the tag value associated with the lowest key ;
; in the heap and deletes the node. The heap is then ;
; reconstructed with the remaining nodes. ;
; 5. DeletePQ: ;
; Deletes the entire heap by freeing the allocated ;
; area. ;
; 6. SizePQ: ;
; Increases the size of the heap by adding space ;
; for a requested number of entries ;
; Heap Data Structure: ;
; ;
; The heap data structure has two parts, a header and a set of nodes and ;
; these are blocked one after the other. ;
; The header maintains: ;
; (i) A pointer to the next available node slot ;
; relative to start of the node area ;
; (ii) A pointer to the first valid node slot ;
; node slots between the header and this pointer ;
; are actually deleted nodes ;
; (iii) A pointer past the last allocated node slot ;
; (iv) A last key value, which either holds the largest;
; key value as long as the nodes are sequentially ;
; ordered, or a very large value to indicate there;
; is no sequential ordering ;
; ;
; ---------------- ;
; | INDEX | ---- Pointer to next available node slot ;
; ---------------- ;
; | MAXENTRY | ---- Pointer past last allocated node slot ;
; ---------------- ;
; START | LASTKEY | ---- Aslong as possible holds max key ;
; NODE | START | ---- pointer to first active node slot ;
; ---------------- ;
; | KEY | ---- Node 1. ;
; | TAG | ;
; //---------------// ;
; | KEY | ---- Last allocated node slot ;
; | TAG | ;
; ---------------- ;
; ;
; All pointers to nodes are relative to node 1 (pointer to node 1 is ZERO) ;
;----------------------------------------------------------------------------;
;----------------------------------------------------------------------------;
; Include File Section and definitions: ;
; ;
.xlist
include cmacros.inc
include gdimacro.inc
include gdimem.inc
.286p
.list
START equ SIZE PQ - SIZE ENTRY ; The header includes Node 0
VERYLARGE equ 4000h ; assumed to be larger than any key
PQERROR equ -1 ; return value on error
TRUE equ 1
FALSE equ 0
Entry struc
e_key dw ? ; key value of node
e_tag dw ? ; corresponding tag value
Entry ends
PQ struc ; HEAP Header Structure + Start Node
pq_index dw ?
pq_maxentry dw ? ; excludes START NODE
pq_lastkey dw ?
pq_start dw ?
PQ ends
externFP GlobalLock
externFP GlobalUnlock
externFP GlobalReAlloc
externFP GlobalFree
externFP GlobalAlloc ; Defined in HELPER.ASM
createSeg _SORT,SORT,byte,public,CODE
sBegin SORT
;-----------------------------------------------------------------------------;
; ;
;CreatePQ: ;
; Inputs: ;
; Max Number of entries the tree will hold ;
; Outputs: ;
; HANDLE to Heap -- if creation successful ;
; PQERROR if failure ;
; Registers Preserved: ;
; DI,SI ;
; ;
; -by- David Weise [davidw] ;
; ;
;-----------------------------------------------------------------------------;
assumes cs,SORT
assumes ds,nothing
cProc farGDIGlobalAlloc,<FAR,PUBLIC>
parmd amount
cBegin
cCall GlobalAlloc,<GMEM_MOVEABLE+GMEM_SHARE,amount>
cEnd
cProc CreatePQ,<FAR,PUBLIC,NODATA>,<di,si>
parmW cEntries
cBegin
mov ax,cEntries ; max no of nodes the tree will hold
shl ax,1
shl ax,1 ; ax <---- ax * SIZE ENTRY
.errnz (SIZE ENTRY - 4)
mov si,ax ; save number of bytes in node array
add ax, SIZE PQ ; size of header including NODE 0
xor dx,dx
cCall farGDIGlobalAlloc,<dx,ax>
mov bx,ax ; Handle returned
dec ax ; set to -1 if handle returned == 0
.errnz (-1 - PQERROR)
or bx,bx ; test for succesfull memory allocation
jz cPQ_Done ; error return.
push bx
cCall GlobalLock,<bx> ; lock handle get back segment
pop bx
mov es,dx
mov di,ax ; es:di points to start of structure
; now initialize the header part of the structure with values
; si has size of the node array
stosw ; index set to zero
.errnz (0 - pq_index)
mov ax,si ; pointer past end of node array
stosw ; max no of entries
.errnz (2 - pq_maxentry)
xor ax,ax ; last key = 0, as heap empty
stosw
.errnz (4 - pq_lastkey)
stosw ; START = 0, implies no deleted slot
.errnz (6 - pq_start)
push bx
cCall GlobalUnlock,<bx> ; unlock the handle to heap
pop ax ; return the handle
cPQ_Done:
cEnd
;-----------------------------------------------------------------------------;
; ;
; InsertPQ: ;
; Inputs: ;
; hPQ -- handle to heap structure segment ;
; tag -- tag value for new node ;
; key -- key value for new node ;
; Outputs: ;
; return TRUE if insertion was successful ;
; return PQERROR if heap was already packed ;
; Preserves: ;
; DS,SI ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 10:45:25 ;
;-----------------------------------------------------------------------------
assumes cs,SORT
assumes ds,nothing
cProc InsertPQ,<FAR,PUBLIC,NODATA>,<di,si>
parmW hPQ
parmW tag
parmW key
cBegin
mov di,hPQ
cCall GlobalLock,<di> ; lock heap and get back segment addres
or ax,dx ; Invalid handle causes zero return
jz Ins_Cant_Proceed
mov es,dx
xor si,si ; offset in segment always zero
mov ax,es:[si] ; pointer to next available slot
sub ax,es:[si].pq_start ; convert it relative to 1st active node
cmp ax,es:[si].pq_maxentry ; compare with pointer past last slot
jb Insertion_Possible
cCall GlobalUnlock,<di>
Ins_Cant_Proceed:
mov ax,PQERROR ; error return
jmp cInsert_Done
Insertion_Possible:
push es ; HEAP structure segment
smov es,ds ; save local segment in es
pop ds ; change DS to heap segment
mov ax,[si].pq_index ; next available slot in node area
cmp ax,[si].pq_maxentry
jb Enough_Space_Atend ; insertion possible w/o compaction
; Deleted nodes exist near the head of the tree, compaction necessary
call CompactList ; removes all deleted elements
; LASTKEY still holds the max key value in the tree
Enough_Space_Atend:
mov bx,[si].pq_index ; pointer to next available slot
mov dx,bx ; save value in register
add bx,SIZE PQ ; area for header
mov ax,tag
mov [si][bx].e_tag,ax ; insert new tag and key
mov ax,key
mov [si][bx].e_key,ax ; key in ax will be used below
mov bx,dx ; bx points to last occupied slot
add dx,SIZE ENTRY ; available slot points to next node
mov [si].pq_index,dx ; save in the structure
; Now test whether the heap property is valid still.
; ax has key, dx has pointer to next slot after addition
; bx points to last valid node
cmp ax,[si].pq_lastkey ; compare with new key
jb Heap_Violated
mov [si].pq_lastkey,ax ; new key is the largest key in tree
jmp short Heap_Restructured ; Insertion over
comment ~
node i has lchild at 2*i and rchild at 2*i+1. But we maintain their
address relative to start of node array. [ie node 1 has addr 0,
node 2 has 4, node 3 12 and so on.]
so if x happens to be the address of a node, the address of its
parent is (x/2 -2) AND 0fffch.
if x is the address of a parent, the address of its lchild is 2*x + 4
and that of its rchild is 2*x + 8
end comment ~
Heap_Violated:
call CompactList ; make sure heap is compacted first!
mov [si].pq_lastkey,VERYLARGE ; to imply heap nolonger seq. ordered
mov bx,[si].pq_index ; bx = offset of inserted elem.
sub bx,SIZE ENTRY
Heap_Walk_Loop:
cmp bx,[si].pq_start ; traversed to top of heap ?
jz Heap_Restructured
mov cx,bx
shr cx,1 ; cx points to parent of current node
dec cx
dec cx
and cx,0fffch ; refer to comment above
.errnz (SIZE ENTRY - 4)
; Test whether current node has to move up or not, if not it resets carry
; else it swaps the two nodes and sets carry
call TestShuffle
mov bx,cx ; next node to inspect ifnec. is parent
jc Heap_Walk_Loop
Heap_Restructured:
smov ds,es ; get back own segment in ds
cCall GlobalUnlock,<di> ; di still has the handle
mov ax,di ; return true
cInsert_Done:
cEnd
;-----------------------------------------------------------------------------;
; TestShuffle: ;
; ;
; Takes as input the node addresses of a parent and on of it's childs. If the;
; key of the parent is >= key of the child, it returns with carry clear, else;
; it swaps the two nodes and returns with carry set. ;
; ;
; bx has current node address in HEAP, relative to start NODE 1 ;
; cx has address of parent node of bx ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 12:00:00 ;
;-----------------------------------------------------------------------------;
cProc TestShuffle,<NEAR,PUBLIC>,<si,di>
cBegin
lea di,[si][SIZE PQ] ; di points to node 1
add di,cx ; di points to parent
lea si,[bx].SIZE PQ ; si points to child node
mov ax,[si].e_key ; childs key
cmp ax,[di].e_key ; key of parent
jb Nodes_Tobe_Swapped
;
; Carry cleared by comparision, use for return
;
jmp short TestShuffle_Ret
Nodes_Tobe_Swapped:
;
; Carry has been set by comparision, use for return
;
xchg ax,[di].e_key
mov [si].e_key,ax
mov ax,[si].e_tag
xchg ax,[di].e_tag
mov [si].e_tag,ax ; swap complete
TestShuffle_Ret:
cEnd
;-----------------------------------------------------------------------------;
; MinPQ: ;
; Inputs: ;
; hPQ -- Handle to the heap structure ;
; Outputs: ;
; minimum tag value in the tree or PQERROR(if invalid handle) ;
; ;
; Calls Local Procedure GetMinPQ. ;
; GetMinPQ takes the handle and a flag as parameter. ;
; If the flag is TRUE, the node with the least key is deleted ;
; GetMinPQ also returns the tag value of least key in AX ;
; ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 12:46:10 ;
;-----------------------------------------------------------------------------;
assumes cs,SORT
assumes ds,nothing
cProc MinPQ,<FAR,PUBLIC,NODATA>
; parmW hPQ
cBegin nogen
mov cl,FALSE ; to imply node not to be deleted
jmpnext ; fall through trick, refer cmacros
cEnd nogen
;-----------------------------------------------------------------------------;
; ExtractPQ: ;
; Inputs: ;
; hPQ -- Handle to the heap structure ;
; Outputs: ;
; minimum tag value if heap handle is valid and heap not empty ;
; return PQERROR otherwise ;
; The node with min key is deleted ;
; Calls Local Procedure GetMinPQ ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 12:54:00 ;
;-----------------------------------------------------------------------------;
assumes cs,SORT
assumes ds,nothing
cProc ExtractPQ,<FAR,PUBLIC,NODATA>
; parmW hPQ
cBegin nogen
mov cl,TRUE ; to imply that node to be deleted
jmpnext stop ; fall through trick, refer cmacros
cEnd nogen
;-----------------------------------------------------------------------------;
; GetMinPQ: ;
; ;
; One of the inputs is a flag. If the flag is FALSE it simply returns the tag ;
; associated with the lease key value in the heap. If the flag is TRUE besides;
; returnning the above tag value it also deletes the node. ;
; ;
; ;
; hPQ --- handle of HEAP segment ;
; cl --- Deletion Flag ( Delete node if TRUE) ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 13:00:00 ;
;-----------------------------------------------------------------------------;
cProc GetMinPQ,<FAR,PUBLIC,NODATA>,<di,si>
parmW hPQ
cBegin
mov di,hPQ
push cx ; save flag
cCall GlobalLock,<di>
pop cx ; get back flag into cl
or dx,ax ; invalid handle implies zero return
jz Min_Cant_Proceed
mov es,dx
mov si,ax ; ds:si points to heap start
mov bx,es:[si].pq_start ; pointer to 1st. available slot
cmp bx,es:[si].pq_index ; empty if equal to next available node
jb Heap_Not_Empty
cCall GlobalUnlock,<di>
Min_Cant_Proceed:
mov ax,PQERROR
jmp cGetMin_Done ; error return
; bx still has [si].pq_start
Heap_Not_Empty:
push es ; save heap segment
smov es,ds ; save local segment is es
pop ds ; ds:si points to start of heap
lea dx,[si][SIZE PQ]
add dx,bx ; points past deleted nodes
xchg di,dx ; save di in dx and use dx's value
mov ax,[di].e_tag ; get the tag associated with least key
xchg di,dx ; get back values
or cl,cl ; test for bl = FALSE
.errnz (0 - FALSE)
jnz Delete_Node ; TRUE implies get tag and delete node
jmp cGetMin_Ret ; return after unlocking heap
Delete_Node:
; bx retains [si].start
add bx,SIZE ENTRY ; one more node deleted
cmp bx,[si].pq_index ; is tree empty ?
jb Tree_Not_Empty
xor cx,cx
mov [si].pq_lastkey,cx ; initialize for empty tree
mov [si].pq_start,cx ; initialize for empty tree
mov [si].pq_index,cx
jmp cGetMin_Ret ; return after unlocking heap
Tree_Not_Empty:
; ax has return tag value
; bx has [si].pq_start + SIZE ENTRY
cmp [si].pq_lastkey,VERYLARGE ; implies keys in random order
jae Min_Restructure_Heap ; maybe restructuring necessary
mov [si].pq_start,bx ; updates past deleted entry
jmp cGetMin_Ret
Min_Restructure_Heap:
; dx still has offset to NODE 1, because
; if LASTKEY = VERYLARGE, pq_start has to be zero
push ax ; save return tag value
mov bx,dx ; offset to first active node
xchg di,dx ; get pointer into di ,save di
add di,[si].pq_index ; dx points to next available slot
sub di,SIZE ENTRY ; point to last filled node
mov ax,di ; last node being moved upfront
sub ax,SIZE PQ ; point ax one node ahead of last
mov [si].pq_index,ax ; update it
mov cx,[di].e_key
mov [bx].e_key,cx ; move from last position to NODE 1
mov cx,[di].e_tag
mov [bx].e_tag,cx
xchg di,dx ; restore di,dx
xor cx,cx ; start traversing heap from root
Min_Traverse_Heap:
mov bx,cx
shl bx,1
add bx,SIZE ENTRY ; bx has left child addr of parent in cx
cmp bx,[si].pq_index ; compare with next available slot
jae Min_Heap_Fixed ; heap restored
push cx ; save current parent
mov cx,bx ; have lchild in cx
add cx,SIZE ENTRY ; cx now get address of rchild
cmp cx,[si].pq_index ; test against last node
jae Right_Child_Not_Present
call GetLesserChild ; gets child with lesser key in bx
Right_Child_Not_Present:
pop cx ; get back parent
;
; cx has node number of parent node and bx has node no of child node with
; least key. If parents key value is greater it should be swapped
;
call TestShuffle
; swaps the two nodes if necessary.
mov cx,bx ; lesser child is next parent
jmp Min_Traverse_Heap
Min_Heap_Fixed:
pop ax ; get back return tag value
cGetMin_Ret:
push ax ; save return value
smov ds,es ; get back own ds
cCall GlobalUnlock,<di> ; unlock heap
pop ax ; get back return value
cGetMin_Done:
cEnd
;-----------------------------------------------------------------------------;
; GetLesserChild: ;
; ;
; Given two child node numbers, it returns the child which has a lesser key ;
; ;
; cx has RCHILD NODE address ;
; bx has LCHILD NODE address ;
; si points to start of heap ;
; will return node address of lesser child in bx ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 13:50 ;
;-----------------------------------------------------------------------------;
cProc GetLesserChild,<NEAR,PUBLIC,NODATA>,<di,si>
cBegin
lea di,[si][SIZE PQ] ; dx now points to NODE 1
mov si,di ; si also points to start of NODE 1
add di,cx ; di get address of rchild
mov ax,[si+bx].e_key ; rchilds key
cmp ax,[di].e_key ; compare with rchild
jb Right_Child_Lesser ; bx still has the correct child no.
mov bx,cx ; get RCHILD address into bx
Right_Child_Lesser:
cEnd
;
;-----------------------------------------------------------------------------;
; DeletePQ: ;
; Inputs: ;
; hPQ --- handle to a heap structure ;
; OutPuts: nothing ;
; Preserves: DI ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 14:15:45 ;
;-----------------------------------------------------------------------------
;
assumes cs,SORT
assumes ds,nothing
cProc DeletePQ,<FAR,PUBLIC,NODATA>
parmW hPQ
cBegin
cCall GlobalFree,<hPQ> ; free the handle
cEnd
;-----------------------------------------------------------------------------;
;SizePQ: ;
; Input: ;
; hPQ --- Handle to a heap structure ;
; entry --- number of nodes by which heap is to be expanded ;
; Output: ;
; Returns the total number of node slots in new heap, if successful
; else return PQERROR ;
; ;
; -by- Amit Chatterjee [amitc] Tue Aug 9 14:31:40 ;
;-----------------------------------------------------------------------------
assumes cs,SORT
assumes ds,nothing
cProc SizePQ,<FAR,PUBLIC,NODATA>,<si,di>
parmW hPQ
parmW cEntry
cBegin
mov di,hPQ
cCall GlobalLock,<di> ; lock to get back segment address
or ax,dx ; Invalid handle implies NULL return
jz Siz_Cant_Proceed
mov es,dx
xor si,si ; offset will always be zro
mov ax,cEntry ; additional nodes
or ax,ax ; if zero return original numof nodes
jnz Size_Tobe_Increased
mov ax,es:[si].pq_maxentry ; offset past last node
shr ax,1
shr ax,1 ; ax <--- ax / SIZE ENTRY
.errnz (SIZE ENTRY - 4)
jmp short cSize_Ret ; return after unlocking handle
Size_Tobe_Increased:
shl ax,1
shl ax,1 ; ax <-- ax * SIZE ENTRY, = extra bytes
.errnz (SIZE ENTRY - 4)
add ax,es:[si].pq_maxentry ; number of byte for new node array
cmp ax,es:[si].pq_index ; next available slot
jae Valid_Increase
mov ax,PQERROR ; error code
jmp short cSize_Ret ; return after releasing handle
Valid_Increase:
push ax ; save number of bytes in node block
add ax,SIZE PQ ; size of header
push ax
cCall GlobalUnlock,<di> ; unlock handle
xor dx,dx ; high word for size
pop ax ; get back size
cCall GlobalReAlloc,<di,dx,ax,GMEM_MOVEABLE>
or ax,ax
jz Siz_Cant_Proceed
mov di,ax ; new handle
cCall GlobalLock,<ax> ; lock it
mov es,dx ; set new segment
pop cx ; get back total no of nodes into cx
jmp short Reloc_Successful
Siz_Cant_Proceed:
pop cx ; balance stack
dec ax
.errnz (-1 - PQERROR)
jmp short cSize_End
Reloc_Successful:
mov es:[si].pq_maxentry,cx ; total number of slots now
shr cx,1
shr cx,1 ; no of nodes = bytes / SIZE ENTRY
.errnz (SIZE ENTRY - 4)
mov ax,cx ; return value
cSize_Ret:
cCall GlobalUnlock,<di>
cSize_End:
cEnd
;-----------------------------------------------------------------------------;
;CompactList: ;
; Input: ;
; ds:si --- pointer to heap structure ;
; Output: ;
; all deleted elements are removed from heap structure ;
; Registers trashed:
; AX,BX,CX,DX
;
; -by- Ken Sykes [kensy] Tue Nov 12 1991 10:20:00am ;
;-----------------------------------------------------------------------------
CompactList proc near
mov ax,[si].pq_index ; next available slot in node area
sub ax,[si].pq_start ; ax had pointer to available slot
mov [si].pq_index,ax ; next available slot will come up
lea dx,[si][SIZE PQ] ; points to NODE 1
mov ax,[si].pq_start ; pointer to 1st active node rel to 1
add ax,dx ; ax has offset to first valid node.
mov bx,ax
mov cx,[si].pq_maxentry ; pointer past end of node slots
sub cx,[si].pq_start ; pointer to strt of active block
shr cx,1 ; will do a REP MOVSW
.errnz (1 and SIZE ENTRY)
push es ; es has local segment
smov es,ds ; moth es ds point to heap segment
push si
push di ; save start to heap and its handle
mov si,bx ; si points to start of valid nodes
mov di,dx ; dx points to node 1
cld
rep movsw ; Compacted
pop di
pop si
pop es ; restore local segment in es
mov [si].pq_start,cx ; after compaction deleted nodes = 0
ret
CompactList endp
;-----------------------------------------------------------------------------;
sEnd SORT
end