/* Copyright (c) 1992, Microsoft Corporation, all rights reserved ** ** dtl.c ** Double-threaded linked list manipulation core routines ** Listed alphabetically ** ** 06/28/92 Steve Cobb */ #include // Win32 root #include // Heap definitions #include // Our public header #include // debug macros DTLNODE* DtlMoveToTail( IN DTLLIST* pdtllist, IN DTLNODE* pdtlnode ); DTLNODE* DtlAddNodeAfter( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnodeInList, IN OUT DTLNODE* pdtlnode ) /* Adds node 'pdtlnode' to list 'pdtllist' after node 'pdtlnodeInList'. ** If 'pdtlnodeInList' is NULL, 'pdtlnode' is added at the end of the ** list. ** ** Returns the address of the added node, i.e. 'pdtlnode'. */ { //For .Net 665628, add parameter checking for the whole dtl.c file // if( pdtllist && pdtlnode ) { if (!pdtlnodeInList || !pdtlnodeInList->pdtlnodeNext) return DtlAddNodeLast( pdtllist, pdtlnode ); pdtlnode->pdtlnodePrev = pdtlnodeInList; pdtlnode->pdtlnodeNext = pdtlnodeInList->pdtlnodeNext; pdtlnodeInList->pdtlnodeNext->pdtlnodePrev = pdtlnode; pdtlnodeInList->pdtlnodeNext = pdtlnode; ++pdtllist->lNodes; } return pdtlnode; } DTLNODE* DtlAddNodeBefore( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnodeInList, IN OUT DTLNODE* pdtlnode ) /* Adds node 'pdtlnode' to list 'pdtllist' before node 'pdtlnodeInList'. ** If 'pdtlnodeInList' is NULL, 'pdtlnode' is added at the beginning of ** the list. ** ** Returns the address of the added node, i.e. 'pdtlnode'. */ { if( pdtllist && pdtlnode ) { if (!pdtlnodeInList || !pdtlnodeInList->pdtlnodePrev) return DtlAddNodeFirst( pdtllist, pdtlnode ); pdtlnode->pdtlnodePrev = pdtlnodeInList->pdtlnodePrev; pdtlnode->pdtlnodeNext = pdtlnodeInList; pdtlnodeInList->pdtlnodePrev->pdtlnodeNext = pdtlnode; pdtlnodeInList->pdtlnodePrev = pdtlnode; ++pdtllist->lNodes; } return pdtlnode; } DTLNODE* DtlAddNodeFirst( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnode ) /* Adds node 'pdtlnode' at the beginning of list 'pdtllist'. ** ** Returns the address of the added node, i.e. 'pdtlnode'. */ { if ( pdtllist && pdtlnode ) { if (pdtllist->lNodes) { pdtllist->pdtlnodeFirst->pdtlnodePrev = pdtlnode; pdtlnode->pdtlnodeNext = pdtllist->pdtlnodeFirst; } else { pdtllist->pdtlnodeLast = pdtlnode; pdtlnode->pdtlnodeNext = NULL; } pdtlnode->pdtlnodePrev = NULL; pdtllist->pdtlnodeFirst = pdtlnode; ++pdtllist->lNodes; } return pdtlnode; } DTLNODE* DtlAddNodeLast( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnode ) /* Adds 'pdtlnode' at the end of list 'pdtllist'. ** ** Returns the address of the added node, i.e. 'pdtlnode'. */ { if ( pdtllist && pdtlnode ) { if (pdtllist->lNodes) { pdtlnode->pdtlnodePrev = pdtllist->pdtlnodeLast; pdtllist->pdtlnodeLast->pdtlnodeNext = pdtlnode; } else { pdtlnode->pdtlnodePrev = NULL; pdtllist->pdtlnodeFirst = pdtlnode; } pdtllist->pdtlnodeLast = pdtlnode; pdtlnode->pdtlnodeNext = NULL; ++pdtllist->lNodes; } return pdtlnode; } DTLLIST* DtlCreateList( IN LONG lListId ) /* Allocates a list and initializes it to empty. The list is marked with ** the user-defined list identification code 'lListId'. ** ** Returns the address of the list control block or NULL if out of memory. */ { DTLLIST* pdtllist = Malloc( sizeof(DTLLIST) ); if (pdtllist) { pdtllist->pdtlnodeFirst = NULL; pdtllist->pdtlnodeLast = NULL; pdtllist->lNodes = 0; pdtllist->lListId = lListId; } return pdtllist; } DTLNODE* DtlCreateNode( IN VOID* pData, IN LONG_PTR lNodeId ) /* Allocates an unsized node and initializes it to contain the address of ** the user data block 'pData' and the user-defined node identification ** code 'lNodeId'. ** ** Returns the address of the new node or NULL if out of memory. */ { DTLNODE* pdtlnode = DtlCreateSizedNode( 0, lNodeId ); if (pdtlnode) DtlPutData( pdtlnode, pData ); return pdtlnode; } DTLNODE* DtlCreateSizedNode( IN LONG lDataBytes, IN LONG_PTR lNodeId ) /* Allocates a sized node with space for 'lDataBytes' bytes of user data ** built-in. The node is initialized to contain the address of the ** built-in user data block (or NULL if of zero length) and the ** user-defined node identification code 'lNodeId'. The user data block ** is zeroed. ** ** Returns the address of the new node or NULL if out of memory. */ { DTLNODE* pdtlnode = Malloc( sizeof(DTLNODE) + lDataBytes ); if (pdtlnode) { ZeroMemory( pdtlnode, sizeof(DTLNODE) + lDataBytes ); if (lDataBytes) pdtlnode->pData = pdtlnode + 1; pdtlnode->lNodeId = lNodeId; } return pdtlnode; } DTLNODE* DtlDeleteNode( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnode ) /* Destroys node 'pdtlnode' after removing it from list 'pdtllist'. ** ** Returns the address of the node after the deleted node in 'pdtllist' or ** NULL if none. */ { DTLNODE* pdtlnodeNext = NULL; if( NULL == pdtllist || NULL == pdtlnode ) { return NULL; } pdtlnodeNext = pdtlnode->pdtlnodeNext; DtlRemoveNode( pdtllist, pdtlnode ); DtlDestroyNode( pdtlnode ); return pdtlnodeNext; } VOID DtlDestroyList( IN OUT DTLLIST* pdtllist, IN PDESTROYNODE pfuncDestroyNode ) /* Deallocates all nodes in list 'pdtllist' using the node deallocation ** function 'pfuncDestroyNode' if non-NULL or DtlDestroyNode otherwise. ** Won't GP-fault if passed a NULL list, e.g. if 'pdtllist', was never ** allocated. */ { if (pdtllist) { DTLNODE* pdtlnode; while (pdtlnode = DtlGetFirstNode( pdtllist )) { DtlRemoveNode( pdtllist, pdtlnode ); if (pfuncDestroyNode) pfuncDestroyNode( pdtlnode ); else DtlDestroyNode( pdtlnode ); } Free( pdtllist ); } } VOID DtlDestroyNode( IN OUT DTLNODE* pdtlnode ) /* Deallocates node 'pdtlnode'. It is the caller's responsibility to free ** the entry in an unsized node, if necessary. */ { Free0( pdtlnode ); } DTLLIST* DtlDuplicateList( IN DTLLIST* pdtllist, IN PDUPNODE pfuncDupNode, IN PDESTROYNODE pfuncDestroyNode ) /* Duplicates a list 'pdtllist' using 'pfuncDupNode' to duplicate the ** individual nodes. 'PfuncDestroyNode' is used for clean-up before ** returning an error. ** ** Returns the address of the new list or NULL if out of memory. It is ** caller's responsibility to free the returned list. */ { DTLNODE* pdtlnode; DTLLIST* pdtllistDup = NULL; if ( NULL == pdtllist ) { return NULL; } pdtllistDup = DtlCreateList( 0 ); if (!pdtllistDup) return NULL; for (pdtlnode = DtlGetFirstNode( pdtllist ); pdtlnode; pdtlnode = DtlGetNextNode( pdtlnode )) { DTLNODE* pdtlnodeDup; pdtlnodeDup = pfuncDupNode( pdtlnode ); if (!pdtlnodeDup) { DtlDestroyList( pdtllist, pfuncDestroyNode ); break; } DtlAddNodeLast( pdtllistDup, pdtlnodeDup ); } return pdtllistDup; } DTLNODE* DtlNodeFromIndex( IN DTLLIST* pdtllist, IN LONG lToFind ) /* Returns the node associated with 0-based index 'lToFind' in the linked ** list of nodes, 'pdtllist', or NULL if not found. */ { DTLNODE* pdtlnode; if (!pdtllist || lToFind < 0) return NULL; pdtlnode = DtlGetFirstNode( pdtllist ); while (pdtlnode && lToFind--) pdtlnode = DtlGetNextNode( pdtlnode ); return pdtlnode; } VOID DtlMergeSort( IN DTLLIST* pdtllist, IN PCOMPARENODE pfnCompare ) /* Sorts the list 'pdtllist' in-place using merge-sort. ** The comparison-function 'pfnCompare' is passed 'DTLNODE' pointers ** when entries in the list need to be compared. ** ** This implementation is a bottom-up iterative merge-sort. ** The list is sorted by merging adjacent pairs of lists of length i ** where i starts as 1 and doubles on each iteration. ** Thus for the list (3 1 4 1 5 9 2 6), the following pairs of sublists ** are merged, with the intermediate results on the right: ** ** (3)-(1), (4)-(1), (5)-(9), (2)-(6) ==> (1 3 1 4 5 9 2 6) ** ** (1 3)-(1 4), (5 9)-(2 6) ==> (1 1 3 4 2 5 6 9) ** ** (1 1 3 4)-(2 5 6 9) ==> (1 1 2 3 4 5 6 9) ** ** Mergesort is a stable sort (i.e. the order of equal items is preserved) ** and it never does more than N lg N comparisons. */ { DTLNODE* a, *b; INT N, Ncmp = 0, Nsub; if ( NULL == pdtllist ) { TRACE("Null pdtllist"); return; } N = DtlGetNodes(pdtllist); TRACE1("DtlMergeSort: N=%d", N); // // sort and merge all adjacent sublists of length 'Nsub', // where 'Nsub' goes from 1 to N^lg('N'), by doubling on each iteration // for (Nsub = 1; Nsub < N; Nsub *= 2) { INT Nremnant; INT aLength, bLength; // // get the head of the first (left) sublist // a = DtlGetFirstNode(pdtllist); // // as long as there is a right sublist, sort // for (Nremnant = N; Nremnant > 0; Nremnant -= Nsub * 2) { // // get the head of the right sublist; // it's just the tail of the left sublist // INT i, an, bn; aLength = min(Nremnant, Nsub); for (i = aLength, b = a; i; i--, b = DtlGetNextNode(b)) { } // // compute the length of the right sublist; // in the case where there is no right sublist // set the length to zero and the loop below just moves // the left sublist // bLength = min(Nremnant - Nsub, Nsub); if (bLength < 0) { bLength = 0; } // // now merge the left and right sublists in-place; // we merge by building a sorted list at the tail of // the unsorted list // an = aLength; bn = bLength; // // as long as both sublists are non-empty, merge them // by moving the entry with the smallest key to the tail. // while (an && bn) { ++Ncmp; if (pfnCompare(a, b) <= 0) { a = DtlMoveToTail(pdtllist, a); --an; } else { b = DtlMoveToTail(pdtllist, b); --bn; } } // // one of the sublists is empty; move all the entries // in the other sublist to the end of our sorted list // if (an) do { a = DtlMoveToTail(pdtllist, a); } while(--an); else if (bn) do { b = DtlMoveToTail(pdtllist, b); } while(--bn); // // 'b' now points to the end of the right sublist, // meaning that the item after 'b' is the one which will be // the head of the left sublist on our next iteration; // we therefore update 'a' here // a = b; } } TRACE1("DtlMergeSort: Ncmp=%d", Ncmp); } DTLNODE* DtlMoveToTail( IN DTLLIST* pdtllist, IN DTLNODE* pdtlnode ) /* Moves a DTLNODE to the end of a list; ** Takes the list and the node to be moved, and returns the next node. */ { DTLNODE* pdtltemp = NULL; if( NULL == pdtllist || NULL == pdtlnode ) { TRACE("NULL input pointers in DtlMovetoTail()"); return NULL; } pdtltemp = DtlGetNextNode(pdtlnode); DtlRemoveNode(pdtllist, pdtlnode); DtlAddNodeLast(pdtllist, pdtlnode); return pdtltemp; } DTLNODE* DtlRemoveNode( IN OUT DTLLIST* pdtllist, IN OUT DTLNODE* pdtlnodeInList ) /* Removes node 'pdtlnodeInList' from list 'pdtllist'. ** ** Returns the address of the removed node, i.e. 'pdtlnodeInList'. */ { if ( pdtllist && pdtlnodeInList ) { if (pdtlnodeInList->pdtlnodePrev) pdtlnodeInList->pdtlnodePrev->pdtlnodeNext = pdtlnodeInList->pdtlnodeNext; else pdtllist->pdtlnodeFirst = pdtlnodeInList->pdtlnodeNext; if (pdtlnodeInList->pdtlnodeNext) pdtlnodeInList->pdtlnodeNext->pdtlnodePrev = pdtlnodeInList->pdtlnodePrev; else pdtllist->pdtlnodeLast = pdtlnodeInList->pdtlnodePrev; --pdtllist->lNodes; } return pdtlnodeInList; } VOID DtlSwapLists( IN OUT DTLLIST* pdtllist1, IN OUT DTLLIST* pdtllist2 ) /* Swap the node chains between lists 'pdtllist1' and 'pdtllist2'. */ { DTLLIST dtllist; if( NULL == pdtllist1 || NULL == pdtllist2 ) { return; } dtllist.pdtlnodeFirst = pdtllist1->pdtlnodeFirst; dtllist.pdtlnodeLast = pdtllist1->pdtlnodeLast; dtllist.lNodes = pdtllist1->lNodes; pdtllist1->pdtlnodeFirst = pdtllist2->pdtlnodeFirst; pdtllist1->pdtlnodeLast = pdtllist2->pdtlnodeLast; pdtllist1->lNodes = pdtllist2->lNodes; pdtllist2->pdtlnodeFirst = dtllist.pdtlnodeFirst; pdtllist2->pdtlnodeLast = dtllist.pdtlnodeLast; pdtllist2->lNodes = dtllist.lNodes; }