Rev 17 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 16 | f9daq | 1 | //------------------------------------------------------------------------------------------- |
| 2 | // klist.c - parts to maintain a doubly linked list (org microsoft) |
||
| 3 | // |
||
| 4 | // Please announce changes and hints to ARW Elektronik |
||
| 5 | // |
||
| 6 | // |
||
| 7 | // $Log: Klist.c,v $ |
||
| 8 | // Revision 1.2 2004/07/24 07:47:46 klaus |
||
| 9 | // revised, removed wrong license terms |
||
| 10 | // |
||
| 11 | // Revision 1.1.1.1 2003/11/14 23:17:18 klaus |
||
| 12 | // First put into repository |
||
| 13 | // |
||
| 14 | // Revision 1.2 2002/10/27 17:05:33 klaus |
||
| 15 | // CVS log added, file addressing bug > 2 Gbtye circumvent |
||
| 16 | // |
||
| 17 | // what who when |
||
| 18 | // first steps AR 07.11.1999 |
||
| 19 | // |
||
| 20 | |||
| 21 | //------------------------------------------------------------------------------------------- |
||
| 22 | // INCLUDES |
||
| 23 | // |
||
| 24 | #include <memory.h> |
||
| 25 | #include <windows.h> |
||
| 26 | #include <Klist.h> |
||
| 27 | |||
| 28 | //------------------------------------------------------------------------------------------- |
||
| 29 | // DEFINES |
||
| 30 | // |
||
| 31 | #define memcpy memcpy |
||
| 32 | #define BLOCKSIZE 0x4000 |
||
| 33 | |||
| 34 | //------------------------------------------------------------------------------------------- |
||
| 35 | // TYPEDEFS |
||
| 36 | // |
||
| 37 | typedef struct |
||
| 38 | { |
||
| 39 | HANDLE hMem; // memory handle for this block |
||
| 40 | int iInUse; // number of allocations taken out of it. 0 => free it |
||
| 41 | int iNext; // next byte to use |
||
| 42 | char chData[BLOCKSIZE]; |
||
| 43 | } BLOCK, FAR *PBLOCK; |
||
| 44 | |||
| 45 | /* The following definition tells the truth about what an ITEM is. The |
||
| 46 | | header file says only that there's a structure with the tag item_tag and |
||
| 47 | | that a LIST is a pointer to one. Here we spell out what that structure |
||
| 48 | | is (and a LIST is still a pointer to one). A PLIST is defined as a |
||
| 49 | | pointer to one of those, but is only really used because the C |
||
| 50 | | parameter mechanism demands an extra level of indirection for a |
||
| 51 | | parameter that can be updated. (Modula-2 VAR parameter). |
||
| 52 | */ |
||
| 53 | typedef struct item_tag |
||
| 54 | { |
||
| 55 | struct item_tag FAR *pitNext; /* to next in circular list */ |
||
| 56 | struct item_tag FAR *pitPrev; /* to prev in circular list */ |
||
| 57 | PBLOCK pBlock; /* to memory block */ |
||
| 58 | BOOL bAnchor; /* TRUE iff an anchor block */ |
||
| 59 | BOOL bOK; /* true unless a list op has failed */ |
||
| 60 | int iLen; /* length of data only */ |
||
| 61 | char Data[1]; /* the caller's data. The '1' is a lie */ |
||
| 62 | } ITEM; |
||
| 63 | |||
| 64 | /* For an anchor block, only the fields pitNext thru bAnchor are allocated. |
||
| 65 | | For a normal list element, Data may well be longer than 1 byte. |
||
| 66 | | The bOK flag is to support a style of programming where several |
||
| 67 | | successive operations can be done without having to check the return |
||
| 68 | | code at each stage. At the end, the list can be examined to see if |
||
| 69 | | the data in it is valid or if it has been made invalid by the failure |
||
| 70 | | of any of the previous operations. Certain operations may result in |
||
| 71 | | having no list at all if they fail (e.g. create) and for these, you'd |
||
| 72 | | better check the result at once! |
||
| 73 | | ??? Some of this screed belongs in the header!!! |
||
| 74 | */ |
||
| 75 | |||
| 76 | //------------------------------------------------------------------------------------------- |
||
| 77 | // LOCALS |
||
| 78 | // |
||
| 79 | static CRITICAL_SECTION CritSec; /* to protect pCurrent */ |
||
| 80 | |||
| 81 | #define List_Enter_Crit(x) EnterCriticalSection(x) |
||
| 82 | #define List_Leave_Crit(x) LeaveCriticalSection(x) |
||
| 83 | |||
| 84 | static PBLOCK pCurrent = NULL; // block currently in use |
||
| 85 | // must always be either NULL or valid |
||
| 86 | |||
| 87 | static int iAnchorSize; /* Size of anchor block (no data, no dummy) */ |
||
| 88 | static int iHeaderSize; /* Size of data block not counting Data |
||
| 89 | and offset from cursor back to item. */ |
||
| 90 | static BOOL bInited = FALSE; /* TRUE <=> iAnchorSize and iHeaderSize are OK*/ |
||
| 91 | |||
| 92 | #define MOVEBACK(Curs) \ |
||
| 93 | { Curs = ((char FAR *)Curs-iHeaderSize); } /*move from Data to pitNext*/ |
||
| 94 | |||
| 95 | //------------------------------------------------------------------------------------------- |
||
| 96 | // EXTERNALS |
||
| 97 | // |
||
| 98 | |||
| 99 | //------------------------------------------------------------------------------------------- |
||
| 100 | // GLOBALS |
||
| 101 | // |
||
| 102 | |||
| 103 | //------------------------------------------------------------------------------------------- |
||
| 104 | // FUNCTIONS |
||
| 105 | // |
||
| 106 | |||
| 107 | /* Allocate storage for List elements. n.b. after a call to this |
||
| 108 | you MUST record the value of pCurrent as you need to hand that in |
||
| 109 | to Free. You don't hand in the value of the actual storage. |
||
| 110 | See screed above. |
||
| 111 | This function Enters the critical section. The caller must Leave it. |
||
| 112 | */ |
||
| 113 | static LPVOID Alloc(int size) |
||
| 114 | { |
||
| 115 | HANDLE hMem; |
||
| 116 | LPVOID pRet; |
||
| 117 | List_Enter_Crit(&CritSec); |
||
| 118 | if ((pCurrent==NULL)||(pCurrent->iNext+size>BLOCKSIZE+1)) |
||
| 119 | { |
||
| 120 | hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,(DWORD)(sizeof(BLOCK))); |
||
| 121 | if (hMem==NULL) |
||
| 122 | { |
||
| 123 | pCurrent = NULL; |
||
| 124 | OutputDebugString("GlobalAlloc failed!!\n"); |
||
| 125 | return NULL; |
||
| 126 | } |
||
| 127 | pCurrent = (PBLOCK)GlobalLock(hMem); |
||
| 128 | if (pCurrent==NULL) |
||
| 129 | { |
||
| 130 | OutputDebugString("GlobalLock failed!!\n"); |
||
| 131 | return NULL; |
||
| 132 | } |
||
| 133 | pCurrent->hMem = hMem; |
||
| 134 | pCurrent->iInUse = 0; |
||
| 135 | pCurrent->iNext = 0; |
||
| 136 | } |
||
| 137 | pRet = &(pCurrent->chData[pCurrent->iNext]); |
||
| 138 | ++(pCurrent->iInUse); |
||
| 139 | pCurrent->iNext += size; |
||
| 140 | |||
| 141 | // ensure that the data is aligned 4 byte |
||
| 142 | pCurrent->iNext += 3; |
||
| 143 | pCurrent->iNext -= pCurrent->iNext % 4; |
||
| 144 | |||
| 145 | return pRet; |
||
| 146 | } |
||
| 147 | |||
| 148 | static void Free(PBLOCK pBlock, LPVOID p) |
||
| 149 | { |
||
| 150 | HANDLE hMem; |
||
| 151 | List_Enter_Crit(&CritSec); |
||
| 152 | --pBlock->iInUse; |
||
| 153 | if (pBlock->iInUse<=0) |
||
| 154 | { |
||
| 155 | if (pBlock->iInUse<0) |
||
| 156 | { |
||
| 157 | // don't know what to do with it |
||
| 158 | } |
||
| 159 | |||
| 160 | hMem = pBlock->hMem; |
||
| 161 | GlobalUnlock(hMem); |
||
| 162 | GlobalFree(hMem); |
||
| 163 | if (pCurrent==pBlock) pCurrent = NULL; /* defend the invariant */ |
||
| 164 | } |
||
| 165 | List_Leave_Crit(&CritSec); |
||
| 166 | } |
||
| 167 | |||
| 168 | /*================================================================== |
||
| 169 | || Lists are circular, doubly linked with an anchor block which holds |
||
| 170 | || pointers to both ends. Every block has a flag which shows whether |
||
| 171 | || it's an anchor or not. |
||
| 172 | || |
||
| 173 | || Empty list: |
||
| 174 | || |
||
| 175 | || ------------- |
||
| 176 | || | | |
||
| 177 | || | Anchor | |
||
| 178 | || v ------- | |
||
| 179 | || Ul--->| Next--+--| |
||
| 180 | || |-------| | |
||
| 181 | || | Prev--+-- |
||
| 182 | || ------- |
||
| 183 | || |
||
| 184 | || One entry list: |
||
| 185 | || |
||
| 186 | || ------------------------------------ |
||
| 187 | || | | |
||
| 188 | || | Anchor | |
||
| 189 | || v ------- ------ | |
||
| 190 | || Ul--->| Next--+------------->| Next-+---| |
||
| 191 | || |-------| | |------| | |
||
| 192 | || | Prev--+---- | Prev-+--- |
||
| 193 | || ------- |------| |
||
| 194 | || | Len | |
||
| 195 | || |------| |
||
| 196 | || | Data | |
||
| 197 | || ------ |
||
| 198 | || Two entry list: |
||
| 199 | || |
||
| 200 | || ------------------------------------------------- |
||
| 201 | || | --------------- --------------- | |
||
| 202 | || || | | | | |
||
| 203 | || || Anchor | | | | |
||
| 204 | || vv -------- | v ------ | ------ | |
||
| 205 | || Ul--->| Next--+-----+----->| Next-+----+-->| Next-+-- |
||
| 206 | || |-------| | |------| | | |------| |
||
| 207 | || | Prev--+-- ------+-Prev | | ---+-Prev | |
||
| 208 | || ------- | |------| | |------| |
||
| 209 | || | | Len | | | Len | |
||
| 210 | || | |------| | |------|<----Cursor |
||
| 211 | || | | Data | | | Data | |
||
| 212 | || | ------ | ------ |
||
| 213 | || | | |
||
| 214 | || ------------------- |
||
| 215 | || |
||
| 216 | || etc. |
||
| 217 | || |
||
| 218 | || Note that an external cursor (i.e one which is seen by the caller) |
||
| 219 | || points to the Data field, not to the start of the structure. |
||
| 220 | || This allows easy access to the data by the user at the cost of a |
||
| 221 | || slightly slower traverse. |
||
| 222 | || Within this module, we may sometimes traverse a list with a cursor |
||
| 223 | || that points to the start of an item. This is called an item cursor. |
||
| 224 | È===================================================================*/ |
||
| 225 | |||
| 226 | /*------------------------------------------------------------------ |
||
| 227 | | Set iAnchorSize and iHeaderSize. Implementation independent! |
||
| 228 | -------------------------------------------------------------------*/ |
||
| 229 | void APIENTRY List_Init(void) |
||
| 230 | { |
||
| 231 | LIST P; |
||
| 232 | |||
| 233 | P = (LIST)&P; /* really any old address will do */ |
||
| 234 | iAnchorSize = (char FAR *)&(P->iLen) - (char FAR *)&(P->pitNext); |
||
| 235 | iHeaderSize = (char FAR *)&(P->Data) - (char FAR *)&(P->pitNext); |
||
| 236 | InitializeCriticalSection(&CritSec); |
||
| 237 | /* assumes layout in storage is linear */ |
||
| 238 | } |
||
| 239 | |||
| 240 | /*------------------------------------------------------------------ |
||
| 241 | | Create a list. It will be initially empty |
||
| 242 | -------------------------------------------------------------------*/ |
||
| 243 | LIST APIENTRY List_Create(void) |
||
| 244 | { |
||
| 245 | LIST lst; |
||
| 246 | |||
| 247 | if (!bInited) {List_Init(); } /* prevent some silly errors */ |
||
| 248 | lst = Alloc(iAnchorSize); |
||
| 249 | if (lst==NULL) { return NULL; } |
||
| 250 | lst->pBlock = pCurrent; |
||
| 251 | List_Leave_Crit(&CritSec); |
||
| 252 | lst->bOK = TRUE; |
||
| 253 | lst->pitNext = lst; |
||
| 254 | lst->pitPrev = lst; |
||
| 255 | lst->bAnchor = TRUE; |
||
| 256 | /* no length field set in an anchor block */ |
||
| 257 | return lst; |
||
| 258 | } |
||
| 259 | |||
| 260 | /*------------------------------------------------------------------ |
||
| 261 | | Destroy *plst. It does not need to be empty first |
||
| 262 | -------------------------------------------------------------------*/ |
||
| 263 | void APIENTRY List_Destroy(PLIST plst) |
||
| 264 | { |
||
| 265 | LIST pitP; /* item cursor on * plst */ |
||
| 266 | LIST pitQ; /* item cursor runs one step ahead of pitQ */ |
||
| 267 | |||
| 268 | if (plst==NULL) |
||
| 269 | return; |
||
| 270 | /* There is at least an anchor block to destroy */ |
||
| 271 | pitP = *plst; |
||
| 272 | do |
||
| 273 | { |
||
| 274 | pitQ = pitP->pitNext; |
||
| 275 | Free(pitP->pBlock, pitP); |
||
| 276 | pitP = pitQ; |
||
| 277 | }while(pitP != *plst); |
||
| 278 | *plst = NULL; |
||
| 279 | } |
||
| 280 | |||
| 281 | /*------------------------------------------------------------------ |
||
| 282 | | Return the address of the place for Len bytes of data in a new |
||
| 283 | | item at the start of lst |
||
| 284 | -------------------------------------------------------------------*/ |
||
| 285 | LPVOID APIENTRY List_NewFirst(LIST lst, UINT uLen) |
||
| 286 | { |
||
| 287 | LIST pit; |
||
| 288 | |||
| 289 | if (lst==NULL) |
||
| 290 | return NULL; |
||
| 291 | pit = Alloc(iHeaderSize+uLen); |
||
| 292 | if (pit==NULL) { lst->bOK = FALSE; return NULL; } |
||
| 293 | pit->pBlock = pCurrent; |
||
| 294 | List_Leave_Crit(&CritSec); |
||
| 295 | pit->iLen = uLen; |
||
| 296 | pit->pitPrev = lst; |
||
| 297 | pit->pitNext = lst->pitNext; |
||
| 298 | lst->pitNext->pitPrev = pit; /* for empty list that set lst->pitPrev */ |
||
| 299 | lst->pitNext = pit; |
||
| 300 | pit->bAnchor = FALSE; |
||
| 301 | return (char FAR *)&(pit->Data); |
||
| 302 | } |
||
| 303 | |||
| 304 | |||
| 305 | /*------------------------------------------------------------------ |
||
| 306 | | Delete the item that Curs identifies. |
||
| 307 | | This will be only a few (maybe as little as 3) machine instructions |
||
| 308 | | quicker than DeleteForwards or DeleteBackwards but leaves Curs dangling. |
||
| 309 | | It is therefore NOT usually to be preferred. |
||
| 310 | | It may be useful when you have a function which returns an LPVOID |
||
| 311 | | since the argument does not need to be a variable. |
||
| 312 | | Trivial example: List_Delete(List_First(L)); |
||
| 313 | -------------------------------------------------------------------*/ |
||
| 314 | void APIENTRY List_Delete(LPVOID Curs) |
||
| 315 | { |
||
| 316 | LIST pit; |
||
| 317 | |||
| 318 | if(Curs==NULL) |
||
| 319 | return; |
||
| 320 | MOVEBACK(Curs) |
||
| 321 | pit = (LIST)Curs; |
||
| 322 | pit->pitNext->pitPrev = pit->pitPrev; |
||
| 323 | pit->pitPrev->pitNext = pit->pitNext; |
||
| 324 | Free(pit->pBlock, pit); |
||
| 325 | } |
||
| 326 | |||
| 327 | |||
| 328 | /*--------------------------------------------------------------------- |
||
| 329 | | Return TRUE if and only if lst is empty |
||
| 330 | ----------------------------------------------------------------------*/ |
||
| 331 | BOOL APIENTRY List_IsEmpty(LIST lst) |
||
| 332 | { |
||
| 333 | if (lst==NULL) |
||
| 334 | return TRUE; /* well it's sort of true isn't it? */ |
||
| 335 | return lst->pitNext ==lst; |
||
| 336 | } |
||
| 337 |