// list.c // // a VM growable array package #include "mbrmake.h" typedef struct _list { WORD cItems; } SLIST; typedef struct _biglist { WORD cItems; VA vaNext; } BLIST; typedef union _mixlist { SLIST sml; BLIST big; } GLIST; // this are the two VM lock numbers for the list package // #define LIST_LOCK 10 #define LIST_LOCK2 11 // Beware! For the system to work properly this number must // be small enough that the VM free lists won't overflow // i.e. C_ITEMS_MAX * sizeof(biggest_thing_stored) <= C_FREE_LIST_MAX // #define C_ITEMS_MAX 16 #pragma intrinsic(memcpy) #define cBlock 1 VA VaAddList(VA far *pvaList, LPV lpvData, WORD cbData, WORD grp) // add the given item to the list; create if necessary // return the virtual address of the most recently added item // { VA vaListNew; VA vaDirtyOnExit = vaNil; WORD cbBlock, cItems, cAlloc; GLIST far *lpList, *lpListNew; #ifdef SWAP_INFO iVMGrp = grp; #endif #if cBlock != 1 if (cBlock == 0) cBlock = C_ITEMS_MAX; #endif top: // for tail recursion... // current list is empty -- create a new list with one thing in it if (*pvaList == vaNil) { if (cBlock == C_ITEMS_MAX) { *pvaList = VaAllocGrpCb(grp, cbData*cBlock + sizeof(BLIST)); lpList = LpvFromVa(*pvaList, LIST_LOCK); lpList->big.vaNext = vaNil; lpList->big.cItems = 1; memcpy(((LPCH)lpList) + sizeof(BLIST), lpvData, cbData); if (vaDirtyOnExit) { DirtyVa(vaDirtyOnExit); UnlockW(LIST_LOCK+1); } DirtyVa(*pvaList); UnlockW(LIST_LOCK); return (PBYTE)*pvaList + sizeof(BLIST); } else { *pvaList = VaAllocGrpCb(grp, cbData*cBlock + sizeof(SLIST)); lpList = LpvFromVa(*pvaList, LIST_LOCK); lpList->sml.cItems = 1; memcpy(((LPCH)lpList) + sizeof(SLIST), lpvData, cbData); if (vaDirtyOnExit) { DirtyVa(vaDirtyOnExit); UnlockW(LIST_LOCK+1); } DirtyVa(*pvaList); UnlockW(LIST_LOCK); return (PBYTE)*pvaList + sizeof(SLIST); } } lpList = LpvFromVa(*pvaList, LIST_LOCK); cItems = lpList->sml.cItems; // if current list has extension blocks, recursively add to the // tail of this list if (cItems >= C_ITEMS_MAX) { vaDirtyOnExit = *pvaList; lpList->big.cItems++; DirtyVa(*pvaList); LpvFromVa(*pvaList, LIST_LOCK+1); // lock in mem so address stays good pvaList = &lpList->big.vaNext; UnlockW(LIST_LOCK); goto top; } cbBlock = cItems * cbData; cAlloc = cItems % cBlock; cAlloc = cItems - cAlloc + ( cAlloc ? cBlock : 0 ); // do we need to reallocate? If not do a fast insert // if (cItems < cAlloc) { if (cAlloc >= C_ITEMS_MAX) { memcpy(((LPCH)lpList) + cbBlock + sizeof(BLIST), lpvData, cbData); lpList->big.cItems++; DirtyVa(*pvaList); UnlockW(LIST_LOCK); return (PBYTE)*pvaList + cbBlock + sizeof(BLIST); } else { memcpy(((LPCH)lpList) + cbBlock + sizeof(SLIST), lpvData, cbData); lpList->sml.cItems++; DirtyVa(*pvaList); UnlockW(LIST_LOCK); return (PBYTE)*pvaList + cbBlock + sizeof(SLIST); } } // test if the next block will fit without turning the current list into // a chained list... allocate a new block & copy the old data if (cItems + cBlock < C_ITEMS_MAX) { vaListNew = VaAllocGrpCb(grp, cbBlock + cbData*cBlock + sizeof(SLIST)); lpListNew = LpvFromVa(vaListNew, 0); memcpy((LPCH)lpListNew, lpList, cbBlock + sizeof(SLIST)); memcpy((LPCH)lpListNew + cbBlock + sizeof(SLIST), lpvData, cbData); lpListNew->sml.cItems++; DirtyVa(vaListNew); FreeGrpVa(grp, *pvaList, cbBlock + sizeof(SLIST)); *pvaList = vaListNew; if (vaDirtyOnExit) { DirtyVa(vaDirtyOnExit); UnlockW(LIST_LOCK+1); } UnlockW(LIST_LOCK); return (PBYTE)vaListNew + cbBlock + sizeof(SLIST); } // this is the last item that will go into this block, // allocate a new block c/w link field & copy the old data // set the link field to 0 for now #if cBlock != 1 cBlock = C_ITEMS_MAX - cItems; #endif vaListNew = VaAllocGrpCb(grp, cbBlock + cbData*cBlock + sizeof(BLIST)); lpListNew = LpvFromVa(vaListNew, 0); memcpy(lpListNew + 1 , ((SLIST FAR *)lpList) + 1, cbBlock); memcpy(((LPCH)lpListNew) + cbBlock + sizeof(BLIST), lpvData, cbData); lpListNew->big.cItems = lpList->sml.cItems + 1; lpListNew->big.vaNext = vaNil; DirtyVa(vaListNew); FreeGrpVa(grp, *pvaList, cbBlock + sizeof(SLIST)); *pvaList = vaListNew; if (vaDirtyOnExit) { DirtyVa(vaDirtyOnExit); UnlockW(LIST_LOCK+1); } UnlockW(LIST_LOCK); return (PBYTE)vaListNew + cbBlock + sizeof(BLIST); } WORD CItemsList(VA vaList) // return total number of items in array // { if (vaList == vaNil) return 0; #ifdef SWAP_INFO iVMGrp = grpList; #endif return ((SLIST FAR *)LpvFromVa(vaList, 0))->cItems; } // to use the following iterator say something like // // vaPropList = cSYM.vaPropList; // while (cprop = CItemsIterate(&vaProps, &vaPropList, cBlock)) { // gPROP(vaProps); // for (;--cprop >= 0; cPROP++) { // cPROP.etc = ; // // } // } // // // The ENM_LIST, ENM_END, ENM_BREAK macros "do the right thing" with // these lists. // WORD CItemsIterate(VA FAR *vaData, VA FAR *vaNext) // give number of elements in current block and pointer to next block // { GLIST FAR *lpgList; WORD cItems, cAlloc; if (*vaNext == vaNil) return 0; #ifdef SWAP_INFO iVMGrp = grpList; #endif #if cBlock != 1 if (cBlock == 0) cBlock = C_ITEMS_MAX; #endif lpgList = LpvFromVa(*vaNext, 0); cItems = lpgList->sml.cItems; if (cItems >= C_ITEMS_MAX) { *vaData = (PBYTE)*vaNext + sizeof(BLIST); *vaNext = lpgList->big.vaNext; return C_ITEMS_MAX; } if (cBlock == 0) cAlloc = C_ITEMS_MAX; else { cAlloc = cItems % cBlock; cAlloc = cItems - cAlloc + ( cAlloc ? cBlock : 0 ); } if (cAlloc >= C_ITEMS_MAX) *vaData = (PBYTE)*vaNext + sizeof(BLIST); else *vaData = (PBYTE)*vaNext + sizeof(SLIST); *vaNext = 0; return cItems; } VOID FreeList(VA vaList, WORD cbData) // free up all the memory associated with this list // { (PBYTE)vaList + cbData; printf("FreeList is currently not working\n"); #if 0 GLIST FAR * lpgList; VA vaNextList; if (vaList == vaNil) return; top: // tail recursion lpgList = LpvFromVa(vaList, 0); if (lpgList->sml.cItems >= C_ITEMS_MAX) { vaNextList = lpgList->big.vaNext; FreeVa(vaList, C_ITEMS_MAX * cbData + sizeof(BLIST)); vaList = vaNextList; goto top; // tail recursion } FreeVa(vaList, lpgList->sml.cItems * cbData + sizeof(SLIST)); return; #endif }