1 |
|
---|
2 | /*
|
---|
3 | *@@sourcefile linklist.c:
|
---|
4 | * contains helper functions for maintaining doubly-linked lists.
|
---|
5 | * See explanations below.
|
---|
6 | *
|
---|
7 | * This file is new with V0.81 and contains all the functions
|
---|
8 | * that were in helpers.c previously.
|
---|
9 | *
|
---|
10 | * Usage: All C programs; not OS/2-specific.
|
---|
11 | *
|
---|
12 | * Function prefixes (new with V0.81):
|
---|
13 | * -- lst* linked list helper functions
|
---|
14 | *
|
---|
15 | * <B>Usage:</B>
|
---|
16 | *
|
---|
17 | * Linked lists are implemented through the LINKLIST structure.
|
---|
18 | * This can either be created on the stack or as a global variable
|
---|
19 | * (and must then be initialized using lstInit) or from the heap
|
---|
20 | * with lstCreate.
|
---|
21 | *
|
---|
22 | * Each list item is stored in a LISTNODE structure, which in
|
---|
23 | * turn has a pItemData field, which you can use to get your
|
---|
24 | * data. So a typical list would be coded like this (this is
|
---|
25 | * a heap list):
|
---|
26 | *
|
---|
27 | +
|
---|
28 | + typedef struct _YOURDATA
|
---|
29 | + {
|
---|
30 | + ULONG ulWhatever;
|
---|
31 | + } YOURDATA, *PYOURDATA
|
---|
32 | +
|
---|
33 | + // fill the list
|
---|
34 | + PLINKLIST pll = lstCreate(TRUE or FALSE);
|
---|
35 | + while ...
|
---|
36 | + {
|
---|
37 | + PYOURDATA pYourData = (PYOURDATA)malloc(sizeof(YOURDATA));
|
---|
38 | + lstAppendItem(pll, pYourData); // store the data in a new node
|
---|
39 | + }
|
---|
40 | +
|
---|
41 | + ...
|
---|
42 | +
|
---|
43 | + // now iterate over the list
|
---|
44 | + PLISTNODE pNode = lstQueryFirstNode(pll);
|
---|
45 | + while (pNode)
|
---|
46 | + {
|
---|
47 | + PYOURDATA pYourData = (PYOURDATA)pNode->pItemData;
|
---|
48 | + ...
|
---|
49 | + pNode = pNode->pNext;
|
---|
50 | + }
|
---|
51 | +
|
---|
52 | + lstFree(pll); // this is free the list items too, if TRUE had
|
---|
53 | + // been specified with lstCreate
|
---|
54 | *
|
---|
55 | * If __XWPMEMDEBUG__ is defined, the lstCreate and lstAppendItem funcs will
|
---|
56 | * automatically be replaced with lstCreateDebug and lstAppendItemDebug,
|
---|
57 | * and mapper macros are defined in linklist.h.
|
---|
58 | *
|
---|
59 | * Note: If you frequently need to search your data structures,
|
---|
60 | * you might prefer the new balanced binary trees in tree.c which
|
---|
61 | * have been added with V0.9.5.
|
---|
62 | *
|
---|
63 | * Note: Version numbering in this file relates to XWorkplace version
|
---|
64 | * numbering.
|
---|
65 | *
|
---|
66 | *@@header "helpers\linklist.h"
|
---|
67 | */
|
---|
68 |
|
---|
69 | /*
|
---|
70 | * Copyright (C) 1997-2001 Ulrich Mller.
|
---|
71 | * This file is part of the "XWorkplace helpers" source package.
|
---|
72 | * This is free software; you can redistribute it and/or modify
|
---|
73 | * it under the terms of the GNU General Public License as published
|
---|
74 | * by the Free Software Foundation, in version 2 as it comes in the
|
---|
75 | * "COPYING" file of the XWorkplace main distribution.
|
---|
76 | * This program is distributed in the hope that it will be useful,
|
---|
77 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
78 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
79 | * GNU General Public License for more details.
|
---|
80 | */
|
---|
81 |
|
---|
82 | #include <stdlib.h>
|
---|
83 | #include <string.h>
|
---|
84 |
|
---|
85 | #include "setup.h" // code generation and debugging options
|
---|
86 |
|
---|
87 | #define DONT_REPLACE_LIST_MALLOC
|
---|
88 | #include "helpers\linklist.h"
|
---|
89 |
|
---|
90 | #pragma hdrstop
|
---|
91 |
|
---|
92 | /*
|
---|
93 | *@@category: Helpers\C helpers\Linked lists
|
---|
94 | * See linklist.c.
|
---|
95 | */
|
---|
96 |
|
---|
97 | /* ******************************************************************
|
---|
98 | *
|
---|
99 | * List base functions
|
---|
100 | *
|
---|
101 | ********************************************************************/
|
---|
102 |
|
---|
103 | /*
|
---|
104 | *@@ lstMalloc:
|
---|
105 | * wrapper around malloc() to make sure memory is
|
---|
106 | * allocated from the C runtime the helpers were
|
---|
107 | * compiled with. This is useful for auto-free
|
---|
108 | * lists.
|
---|
109 | *
|
---|
110 | *@@added V0.9.7 (2000-12-07) [umoeller]
|
---|
111 | */
|
---|
112 |
|
---|
113 | void* lstMalloc(size_t size)
|
---|
114 | {
|
---|
115 | return (malloc(size));
|
---|
116 | }
|
---|
117 |
|
---|
118 | /*
|
---|
119 | *@@ lstStrDup:
|
---|
120 | * wrapper around strdup() to make sure memory is
|
---|
121 | * allocated from the C runtime the helpers were
|
---|
122 | * compiled with. This is useful for auto-free
|
---|
123 | * lists.
|
---|
124 | *
|
---|
125 | *@@added V0.9.7 (2000-12-07) [umoeller]
|
---|
126 | */
|
---|
127 |
|
---|
128 | void* lstStrDup(const char *pcsz)
|
---|
129 | {
|
---|
130 | return (strdup(pcsz));
|
---|
131 | }
|
---|
132 |
|
---|
133 | /*
|
---|
134 | *@@ lstInit:
|
---|
135 | * this initializes a given LINKLIST structure.
|
---|
136 | *
|
---|
137 | * This is useful only if you have a static
|
---|
138 | * LINKLIST structure in your sources (either as
|
---|
139 | * a global variable or as a stack variable) and
|
---|
140 | * don't use lstCreate/lstFree to have lists created
|
---|
141 | * from the heap.
|
---|
142 | *
|
---|
143 | * In that case, use this function as follows:
|
---|
144 | +
|
---|
145 | + LINKLIST llWhatever;
|
---|
146 | + lstInit(&llWhatever);
|
---|
147 | + ...
|
---|
148 | + lstClear(&llWhatever);
|
---|
149 | *
|
---|
150 | * The list which is initialized here must be
|
---|
151 | * cleaned up using lstClear.
|
---|
152 | *
|
---|
153 | * If (fItemsFreeable == TRUE), free() will be
|
---|
154 | * invoked on list items automatically in lstClear,
|
---|
155 | * lstRemoveNode, and lstRemoveItem.
|
---|
156 | *
|
---|
157 | * -- Set this to TRUE if you have created the
|
---|
158 | * list items yourself using malloc().
|
---|
159 | *
|
---|
160 | * This of course will be a "flat" free(). If
|
---|
161 | * you store structures in the list using other
|
---|
162 | * heap pointers, auto-free would cause memory leaks.
|
---|
163 | *
|
---|
164 | * Also, auto-free only works if the malloc() that
|
---|
165 | * has been used on the list item is in the same C
|
---|
166 | * runtime as with the linklist functions. If the
|
---|
167 | * caller uses a different runtime (e.g. from a DLL),
|
---|
168 | * it can use lstMalloc() for allocating the list
|
---|
169 | * item and still use auto-free.
|
---|
170 | *
|
---|
171 | * -- Set this to FALSE if you're storing other
|
---|
172 | * objects, such as numbers or other static items.
|
---|
173 | *
|
---|
174 | * Note: You better call lstInit only once per list,
|
---|
175 | * because we don't check here if the list
|
---|
176 | * contains any data. This will lead to memory
|
---|
177 | * leaks if called upon existing lists, because
|
---|
178 | * we'll lose track of the allocated items.
|
---|
179 | */
|
---|
180 |
|
---|
181 | void lstInit(PLINKLIST pList,
|
---|
182 | BOOL fItemsFreeable) // in: invoke free() on the data
|
---|
183 | // item pointers upon destruction?
|
---|
184 | {
|
---|
185 | if (pList)
|
---|
186 | {
|
---|
187 | memset(pList, 0, sizeof(LINKLIST));
|
---|
188 | pList->ulMagic = LINKLISTMAGIC; // set integrity check word
|
---|
189 | pList->fItemsFreeable = fItemsFreeable;
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | /*
|
---|
194 | *@@ lstClear:
|
---|
195 | * this will delete all list items. As opposed to
|
---|
196 | * lstFree, the "root" of the list will not be
|
---|
197 | * freed so that items can be added later again.
|
---|
198 | *
|
---|
199 | * This can be used on static lists initialized with
|
---|
200 | * lstInit also.
|
---|
201 | *
|
---|
202 | * If fItemsFreeable had been specified as TRUE
|
---|
203 | * when the list was initialized (lstInit), free()
|
---|
204 | * will be invoked on all data item pointers also.
|
---|
205 | * See the remarks for lstInit.
|
---|
206 | *
|
---|
207 | * Returns FALSE only upon errors, e.g. because
|
---|
208 | * integrity checks failed.
|
---|
209 | */
|
---|
210 |
|
---|
211 | BOOL lstClear(PLINKLIST pList)
|
---|
212 | {
|
---|
213 | BOOL brc = FALSE;
|
---|
214 |
|
---|
215 | if (pList)
|
---|
216 | if (pList->ulMagic == LINKLISTMAGIC)
|
---|
217 | {
|
---|
218 | PLISTNODE pNode = pList->pFirst,
|
---|
219 | pNode2;
|
---|
220 |
|
---|
221 | while (pNode)
|
---|
222 | {
|
---|
223 | if (pList->fItemsFreeable)
|
---|
224 | if (pNode->pItemData)
|
---|
225 | free(pNode->pItemData);
|
---|
226 | pNode2 = pNode->pNext;
|
---|
227 | free(pNode);
|
---|
228 | pNode = pNode2;
|
---|
229 | } // while (pNode);
|
---|
230 |
|
---|
231 | lstInit(pList, pList->fItemsFreeable);
|
---|
232 | brc = TRUE;
|
---|
233 | }
|
---|
234 |
|
---|
235 | return brc;
|
---|
236 | }
|
---|
237 |
|
---|
238 | #ifdef __DEBUG_MALLOC_ENABLED__
|
---|
239 |
|
---|
240 | /*
|
---|
241 | *@@ lstCreateDebug:
|
---|
242 | * debug version of lstCreate, using _debug_malloc.
|
---|
243 | *
|
---|
244 | * Calls to lstCreate are automatically mapped to
|
---|
245 | * this function if __XWPMEMDEBUG__ is defined.
|
---|
246 | * This makes sure that the source file and line stored
|
---|
247 | * with the debug memory functions will be that of the
|
---|
248 | * caller, not the ones in this file.
|
---|
249 | *
|
---|
250 | *@@added V0.9.1 (99-12-18) [umoeller]
|
---|
251 | */
|
---|
252 |
|
---|
253 | PLINKLIST lstCreateDebug(BOOL fItemsFreeable,
|
---|
254 | const char *file,
|
---|
255 | unsigned long line,
|
---|
256 | const char *function)
|
---|
257 | {
|
---|
258 | PLINKLIST pNewList = (PLINKLIST)memdMalloc(sizeof(LINKLIST),
|
---|
259 | file,
|
---|
260 | line,
|
---|
261 | function);
|
---|
262 |
|
---|
263 | lstInit(pNewList, fItemsFreeable);
|
---|
264 |
|
---|
265 | return pNewList;
|
---|
266 | }
|
---|
267 |
|
---|
268 | #endif
|
---|
269 |
|
---|
270 | // #else // __DEBUG_MALLOC_ENABLED__
|
---|
271 |
|
---|
272 | /*
|
---|
273 | *@@ lstCreate:
|
---|
274 | * this creates a new linked list and initializes it
|
---|
275 | * (using lstInit).
|
---|
276 | * The LINKLIST structure which defines the "root" of
|
---|
277 | * the list is returned. This must be passed to all
|
---|
278 | * other list functions.
|
---|
279 | *
|
---|
280 | * The list which is created here must be destroyed
|
---|
281 | * using lstFree.
|
---|
282 | *
|
---|
283 | * See lstInit for the description of fItemsFreeable.
|
---|
284 | *
|
---|
285 | * Returns NULL upon errors.
|
---|
286 | *
|
---|
287 | * <B>Usage:</B>
|
---|
288 | + PLINKLIST pllWhatever = lstCreate();
|
---|
289 | + ...
|
---|
290 | + lstFree(pllWhatever);
|
---|
291 | */
|
---|
292 |
|
---|
293 | PLINKLIST lstCreate(BOOL fItemsFreeable) // in: invoke free() on the data
|
---|
294 | // item pointers upon destruction?
|
---|
295 | {
|
---|
296 | PLINKLIST pNewList;
|
---|
297 | if (pNewList = (PLINKLIST)malloc(sizeof(LINKLIST)))
|
---|
298 | lstInit(pNewList, fItemsFreeable);
|
---|
299 |
|
---|
300 | return pNewList;
|
---|
301 | }
|
---|
302 |
|
---|
303 | // #endif // __DEBUG_MALLOC_ENABLED__
|
---|
304 |
|
---|
305 | /*
|
---|
306 | *@@ lstFree:
|
---|
307 | * this will delete all list items (by calling
|
---|
308 | * lstClear) and the list "root" itself.
|
---|
309 | * Returns TRUE if anything was deleted at all
|
---|
310 | * (even if the list was empty)
|
---|
311 | * or FALSE upon errors, e.g. because integrity
|
---|
312 | * checks failed.
|
---|
313 | *
|
---|
314 | * If fItemsFreeable had been specified as TRUE
|
---|
315 | * when the list was created (lstCreate), free()
|
---|
316 | * will be invoked on all data item pointers also.
|
---|
317 | * See the remarks for lstInit.
|
---|
318 | *
|
---|
319 | * This must only be used with heap lists created
|
---|
320 | * with lstCreate.
|
---|
321 | *
|
---|
322 | * This uses a pointer to a PLINKLIST so that
|
---|
323 | * the pointer is automatically reset to NULL
|
---|
324 | * by this function AND to avoid confusion
|
---|
325 | * with lstClear.
|
---|
326 | *
|
---|
327 | *@@changed V0.9.12 (2001-05-24) [umoeller]: changed prototype to use pointer to pointer
|
---|
328 | */
|
---|
329 |
|
---|
330 | BOOL lstFree(PLINKLIST *ppList)
|
---|
331 | {
|
---|
332 | BOOL brc = FALSE;
|
---|
333 | PLINKLIST p;
|
---|
334 |
|
---|
335 | if ( (ppList)
|
---|
336 | && (p = *ppList)
|
---|
337 | )
|
---|
338 | if (lstClear(p)) // this checks for list integrity
|
---|
339 | {
|
---|
340 | // unset magic word; the pList pointer
|
---|
341 | // will point to invalid memory after
|
---|
342 | // freeing the list, and subsequent
|
---|
343 | // integrity checks must fail
|
---|
344 | p->ulMagic = 0;
|
---|
345 | free(p);
|
---|
346 |
|
---|
347 | *ppList = NULL;
|
---|
348 |
|
---|
349 | brc = TRUE;
|
---|
350 | }
|
---|
351 |
|
---|
352 | return brc;
|
---|
353 | }
|
---|
354 |
|
---|
355 | /*
|
---|
356 | *@@ lstCountItems:
|
---|
357 | * this will return the number of items
|
---|
358 | * in the given linked list.
|
---|
359 | * This returns 0 if the list is empty
|
---|
360 | * or -1 if the list is invalid or corrupt.
|
---|
361 | */
|
---|
362 |
|
---|
363 | long lstCountItems(const LINKLIST *pList)
|
---|
364 | {
|
---|
365 | if ( (pList)
|
---|
366 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
367 | )
|
---|
368 | return pList->ulCount;
|
---|
369 |
|
---|
370 | return -1;
|
---|
371 | }
|
---|
372 |
|
---|
373 | /*
|
---|
374 | *@@ lstQueryFirstNode:
|
---|
375 | * this returns the first node of the list,
|
---|
376 | * or NULL if the list is empty. This is
|
---|
377 | * preferred over getting the first directly
|
---|
378 | * from the LINKLIST structure, because this
|
---|
379 | * one checks for data integrity.
|
---|
380 | *
|
---|
381 | * The item data can be queried from the node
|
---|
382 | * as follows:
|
---|
383 | *
|
---|
384 | + PLINKLIST pll = lstCreate();
|
---|
385 | + ... // add items here
|
---|
386 | + PLISTNODE pNode = lstQueryFirstNode(pll);
|
---|
387 | + PYOURDATA pData = pNode->pItemData;
|
---|
388 | *
|
---|
389 | * You can iterate over the whole list like this:
|
---|
390 | *
|
---|
391 | + PLISTNODE pNode = lstQueryFirstNode(pll);
|
---|
392 | + while (pNode)
|
---|
393 | + {
|
---|
394 | + PYOURDATA pYourData = (PYOURDATA)pNode->pItemData;
|
---|
395 | + ...
|
---|
396 | + pNode = pNode->pNext;
|
---|
397 | + }
|
---|
398 | */
|
---|
399 |
|
---|
400 | PLISTNODE lstQueryFirstNode(const LINKLIST *pList)
|
---|
401 | {
|
---|
402 | if ( (pList)
|
---|
403 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
404 | )
|
---|
405 | return pList->pFirst;
|
---|
406 |
|
---|
407 | return 0;
|
---|
408 | }
|
---|
409 |
|
---|
410 | /*
|
---|
411 | *@@ lstQueryLastNode:
|
---|
412 | * similar to lstQueryFirstNode, but this returns
|
---|
413 | * the last node.
|
---|
414 | *
|
---|
415 | *@@added V0.9.9 (2001-02-14) [umoeller]
|
---|
416 | */
|
---|
417 |
|
---|
418 | PLISTNODE lstQueryLastNode(const LINKLIST *pList)
|
---|
419 | {
|
---|
420 | if ( (pList)
|
---|
421 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
422 | )
|
---|
423 | return pList->pLast;
|
---|
424 |
|
---|
425 | return 0;
|
---|
426 | }
|
---|
427 |
|
---|
428 | /*
|
---|
429 | *@@ lstNodeFromIndex:
|
---|
430 | * returns the data of list item with a given ulIndex
|
---|
431 | * (counting from 0), or NULL if ulIndex is too large.
|
---|
432 | *
|
---|
433 | * This traverses the whole list, so it's not terribly
|
---|
434 | * fast. See lstQueryFirstNode for how to traverse
|
---|
435 | * the list yourself.
|
---|
436 | *
|
---|
437 | * Note: As opposed to lstItemFromIndex, this one
|
---|
438 | * returns the LISTNODE structure.
|
---|
439 | */
|
---|
440 |
|
---|
441 | PLISTNODE lstNodeFromIndex(PLINKLIST pList,
|
---|
442 | unsigned long ulIndex)
|
---|
443 | {
|
---|
444 | PLISTNODE pNode = NULL;
|
---|
445 |
|
---|
446 | if ( (pList)
|
---|
447 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
448 | && (pList->pFirst)
|
---|
449 | )
|
---|
450 | {
|
---|
451 | unsigned long ulCount = 0;
|
---|
452 | pNode = pList->pFirst;
|
---|
453 | for (ulCount = 0;
|
---|
454 | ((pNode) && (ulCount < ulIndex));
|
---|
455 | ulCount++)
|
---|
456 | {
|
---|
457 | if (pNode->pNext)
|
---|
458 | pNode = pNode->pNext;
|
---|
459 | else
|
---|
460 | pNode = NULL; // exit
|
---|
461 | }
|
---|
462 | }
|
---|
463 |
|
---|
464 | return pNode;
|
---|
465 | }
|
---|
466 |
|
---|
467 | /*
|
---|
468 | *@@ lstNodeFromItem:
|
---|
469 | * this finds the list node which has the given
|
---|
470 | * data pointer or NULL if not found.
|
---|
471 | *
|
---|
472 | * This traverses the whole list, so it's not terribly
|
---|
473 | * fast. See lstQueryFirstNode for how to traverse
|
---|
474 | * the list yourself.
|
---|
475 | */
|
---|
476 |
|
---|
477 | PLISTNODE lstNodeFromItem(PLINKLIST pList,
|
---|
478 | void* pItemData)
|
---|
479 | {
|
---|
480 | PLISTNODE pNode = 0,
|
---|
481 | pNodeFound = 0;
|
---|
482 |
|
---|
483 | if ( (pList)
|
---|
484 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
485 | && (pItemData)
|
---|
486 | )
|
---|
487 | {
|
---|
488 | pNode = pList->pFirst;
|
---|
489 | while (pNode)
|
---|
490 | {
|
---|
491 | if (pNode->pItemData == pItemData)
|
---|
492 | {
|
---|
493 | pNodeFound = pNode;
|
---|
494 | break;
|
---|
495 | }
|
---|
496 |
|
---|
497 | pNode = pNode->pNext;
|
---|
498 | }
|
---|
499 | }
|
---|
500 |
|
---|
501 | return pNodeFound;
|
---|
502 | }
|
---|
503 |
|
---|
504 | /*
|
---|
505 | *@@ lstItemFromIndex:
|
---|
506 | * returns the data of the list item with a given ulIndex
|
---|
507 | * (counting from 0), or NULL if ulIndex is too large.
|
---|
508 | *
|
---|
509 | * This traverses the whole list, so it's not terribly
|
---|
510 | * fast. See lstQueryFirstNode for how to traverse
|
---|
511 | * the list yourself.
|
---|
512 | *
|
---|
513 | * Note: As opposed to lstNodeFromIndex, this one
|
---|
514 | * returns the item data directly, not the LISTNODE
|
---|
515 | * structure.
|
---|
516 | */
|
---|
517 |
|
---|
518 | void* lstItemFromIndex(PLINKLIST pList,
|
---|
519 | unsigned long ulIndex)
|
---|
520 | {
|
---|
521 | PLISTNODE pNode;
|
---|
522 | if (pNode = lstNodeFromIndex(pList, ulIndex))
|
---|
523 | return (pNode->pItemData);
|
---|
524 | else
|
---|
525 | return (0);
|
---|
526 | }
|
---|
527 |
|
---|
528 | /*
|
---|
529 | *@@ lstIndexFromItem:
|
---|
530 | * returns the index of the list node with
|
---|
531 | * the specified item pointer. The first
|
---|
532 | * node returns 0, the second 1, and so on.
|
---|
533 | *
|
---|
534 | * Returns -1 if not found.
|
---|
535 | *
|
---|
536 | * In the worst case, this function traverses
|
---|
537 | * the whole list.
|
---|
538 | *
|
---|
539 | *@@added V0.9.7 (2000-12-13) [umoeller]
|
---|
540 | */
|
---|
541 |
|
---|
542 | unsigned long lstIndexFromItem(PLINKLIST pList, void *pItemData)
|
---|
543 | {
|
---|
544 | ULONG ulrc = -1,
|
---|
545 | ulIndex = 0;
|
---|
546 | PLISTNODE pNode = lstQueryFirstNode(pList);
|
---|
547 | while (pNode)
|
---|
548 | {
|
---|
549 | if (pNode->pItemData == pItemData)
|
---|
550 | {
|
---|
551 | ulrc = ulIndex;
|
---|
552 | break;
|
---|
553 | }
|
---|
554 |
|
---|
555 | pNode = pNode->pNext;
|
---|
556 | ulIndex++;
|
---|
557 | }
|
---|
558 |
|
---|
559 | return ulrc;
|
---|
560 | }
|
---|
561 |
|
---|
562 | #ifdef __DEBUG_MALLOC_ENABLED__
|
---|
563 |
|
---|
564 | /*
|
---|
565 | *@@ lstAppendItemDebug:
|
---|
566 | * debug version of lstAppendItem, using _debug_malloc.
|
---|
567 | *
|
---|
568 | * Calls to lstAppendItem are automatically mapped to
|
---|
569 | * this function if __XWPMEMDEBUG__ is defined.
|
---|
570 | * This makes sure that the source file and line stored
|
---|
571 | * with the debug memory functions will be that of the
|
---|
572 | * caller, not the ones in this file.
|
---|
573 | *
|
---|
574 | *@@added V0.9.1 (99-12-18) [umoeller]
|
---|
575 | */
|
---|
576 |
|
---|
577 | PLISTNODE lstAppendItemDebug(PLINKLIST pList,
|
---|
578 | void* pNewItemData, // in: data to store in list node
|
---|
579 | const char *file,
|
---|
580 | unsigned long line,
|
---|
581 | const char *function)
|
---|
582 | {
|
---|
583 | PLISTNODE pNewNode = NULL;
|
---|
584 |
|
---|
585 | if ( (pList)
|
---|
586 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
587 | && (pNewNode = (PLISTNODE)memdMalloc(sizeof(LISTNODE), file, line, function))
|
---|
588 | )
|
---|
589 | {
|
---|
590 | memset(pNewNode, 0, sizeof(LISTNODE));
|
---|
591 | pNewNode->pItemData = pNewItemData;
|
---|
592 |
|
---|
593 | if (pList->pLast)
|
---|
594 | {
|
---|
595 | // list is not empty: append to tail
|
---|
596 |
|
---|
597 | // 1) make last item point to new node
|
---|
598 | pList->pLast->pNext = pNewNode;
|
---|
599 | // 2) make new node point to last item
|
---|
600 | pNewNode->pPrevious = pList->pLast;
|
---|
601 | // 3) store new node as new last item
|
---|
602 | pList->pLast = pNewNode;
|
---|
603 |
|
---|
604 | pList->ulCount++;
|
---|
605 | }
|
---|
606 | else
|
---|
607 | {
|
---|
608 | // list is empty: insert as first
|
---|
609 | pList->pFirst
|
---|
610 | = pList->pLast
|
---|
611 | = pNewNode;
|
---|
612 |
|
---|
613 | pList->ulCount = 1;
|
---|
614 | }
|
---|
615 | }
|
---|
616 |
|
---|
617 | return pNewNode;
|
---|
618 | }
|
---|
619 |
|
---|
620 | #endif
|
---|
621 |
|
---|
622 | // #else // __DEBUG_MALLOC_ENABLED__
|
---|
623 |
|
---|
624 | /*
|
---|
625 | *@@ lstAppendItem:
|
---|
626 | * this adds a new node to the tail of the list.
|
---|
627 | * As opposed to lstInsertItemBefore, this is very
|
---|
628 | * fast, since the list always keeps track of its last item.
|
---|
629 | *
|
---|
630 | * This returns the LISTNODE of the new list item,
|
---|
631 | * or NULL upon errors.
|
---|
632 | */
|
---|
633 |
|
---|
634 | PLISTNODE lstAppendItem(PLINKLIST pList,
|
---|
635 | void* pNewItemData) // in: data to store in list node
|
---|
636 | {
|
---|
637 | PLISTNODE pNewNode = NULL;
|
---|
638 |
|
---|
639 | if ( (pList)
|
---|
640 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
641 | && (pNewNode = (PLISTNODE)malloc(sizeof(LISTNODE)))
|
---|
642 | )
|
---|
643 | {
|
---|
644 | memset(pNewNode, 0, sizeof(LISTNODE));
|
---|
645 | pNewNode->pItemData = pNewItemData;
|
---|
646 |
|
---|
647 | if (pList->pLast)
|
---|
648 | {
|
---|
649 | // list is not empty: append to tail
|
---|
650 |
|
---|
651 | // 1) make last item point to new node
|
---|
652 | pList->pLast->pNext = pNewNode;
|
---|
653 | // 2) make new node point to last item
|
---|
654 | pNewNode->pPrevious = pList->pLast;
|
---|
655 | // 3) store new node as new last item
|
---|
656 | pList->pLast = pNewNode;
|
---|
657 |
|
---|
658 | pList->ulCount++;
|
---|
659 | }
|
---|
660 | else
|
---|
661 | {
|
---|
662 | // list is empty: insert as first
|
---|
663 | pList->pFirst
|
---|
664 | = pList->pLast
|
---|
665 | = pNewNode;
|
---|
666 |
|
---|
667 | pList->ulCount = 1;
|
---|
668 | }
|
---|
669 | }
|
---|
670 |
|
---|
671 | return pNewNode;
|
---|
672 | }
|
---|
673 |
|
---|
674 | // #endif // __DEBUG_MALLOC_ENABLED__
|
---|
675 |
|
---|
676 | /*
|
---|
677 | *@@ lstInsertItemBefore:
|
---|
678 | * this inserts a new node to the list. As opposed to
|
---|
679 | * lstAppendItem, the new node can be appended anywhere
|
---|
680 | * in the list, that is, it will be appended BEFORE
|
---|
681 | * the specified index lIndex.
|
---|
682 | *
|
---|
683 | * So if ulIndex == 0, the new item will be made the first
|
---|
684 | * item.
|
---|
685 | *
|
---|
686 | * If ulIndex is larger than the item count on the list,
|
---|
687 | * the new node will be appended at the tail of the list
|
---|
688 | * (as with lstAppendItem).
|
---|
689 | *
|
---|
690 | * This needs to traverse the list to find the indexed
|
---|
691 | * item, so for larger ulIndex's this function is not
|
---|
692 | * terribly fast.
|
---|
693 | *
|
---|
694 | * This returns the LISTNODE of the new list item,
|
---|
695 | * or NULL upon errors.
|
---|
696 | *
|
---|
697 | *@@changed V0.9.14 (2001-07-14) [umoeller]: this never worked on empty lists, fixed
|
---|
698 | */
|
---|
699 |
|
---|
700 | PLISTNODE lstInsertItemBefore(PLINKLIST pList,
|
---|
701 | void* pNewItemData, // data to store in list node
|
---|
702 | unsigned long ulIndex)
|
---|
703 | {
|
---|
704 | PLISTNODE pNewNode = NULL;
|
---|
705 |
|
---|
706 | if ( (pList)
|
---|
707 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
708 | && (pNewNode = (PLISTNODE)malloc(sizeof(LISTNODE)))
|
---|
709 | )
|
---|
710 | {
|
---|
711 | memset(pNewNode, 0, sizeof(LISTNODE));
|
---|
712 | pNewNode->pItemData = pNewItemData;
|
---|
713 |
|
---|
714 | if (ulIndex == 0)
|
---|
715 | {
|
---|
716 | // insert at beginning:
|
---|
717 | if (pList->pFirst)
|
---|
718 | pList->pFirst->pPrevious = pNewNode;
|
---|
719 |
|
---|
720 | pNewNode->pNext = pList->pFirst;
|
---|
721 | pNewNode->pPrevious = NULL;
|
---|
722 |
|
---|
723 | pList->pFirst = pNewNode;
|
---|
724 |
|
---|
725 | if (!pList->pLast)
|
---|
726 | // the list was empty:
|
---|
727 | pList->pLast = pNewNode; // V0.9.14 (2001-07-14) [umoeller]
|
---|
728 |
|
---|
729 | (pList->ulCount)++;
|
---|
730 | }
|
---|
731 | else
|
---|
732 | {
|
---|
733 | // insert at a later position:
|
---|
734 | PLISTNODE pNodeInsertAfter = lstNodeFromIndex(
|
---|
735 | pList,
|
---|
736 | (ulIndex-1));
|
---|
737 |
|
---|
738 | if (pNodeInsertAfter)
|
---|
739 | {
|
---|
740 | // 1) set pointers for new node
|
---|
741 | pNewNode->pPrevious = pNodeInsertAfter;
|
---|
742 | pNewNode->pNext = pNodeInsertAfter->pNext;
|
---|
743 |
|
---|
744 | // 2) adjust next item
|
---|
745 | // so that it points to the new node
|
---|
746 | if (pNodeInsertAfter->pNext)
|
---|
747 | pNodeInsertAfter->pNext->pPrevious = pNewNode;
|
---|
748 |
|
---|
749 | // 3) adjust previous item
|
---|
750 | // so that it points to the new node
|
---|
751 | pNodeInsertAfter->pNext = pNewNode;
|
---|
752 |
|
---|
753 | // 4) adjust last item, if necessary
|
---|
754 | if (pList->pLast == pNodeInsertAfter)
|
---|
755 | pList->pLast = pNewNode;
|
---|
756 |
|
---|
757 | (pList->ulCount)++;
|
---|
758 | }
|
---|
759 | else
|
---|
760 | {
|
---|
761 | // item index too large: append instead
|
---|
762 | free(pNewNode);
|
---|
763 | pNewNode = lstAppendItem(pList, pNewItemData);
|
---|
764 | }
|
---|
765 | }
|
---|
766 | }
|
---|
767 |
|
---|
768 | return pNewNode;
|
---|
769 | }
|
---|
770 |
|
---|
771 | /*
|
---|
772 | *@@ lstRemoveNode:
|
---|
773 | * this will remove a given pRemoveNode from
|
---|
774 | * the given linked list.
|
---|
775 | *
|
---|
776 | * To remove a node according to the item pointer,
|
---|
777 | * use lstRemoveItem.
|
---|
778 | * To get the node from a node index, use
|
---|
779 | * lstNodeFromIndex.
|
---|
780 | *
|
---|
781 | * Since the LISTNODE is known, this function
|
---|
782 | * operates very fast.
|
---|
783 | *
|
---|
784 | * If fItemsFreeable had been specified as TRUE
|
---|
785 | * when the list was created (lstInit or lstCreate),
|
---|
786 | * free() will be invoked on the data item pointer also.
|
---|
787 | * See the remarks there.
|
---|
788 | *
|
---|
789 | * Returns TRUE if successful, FALSE upon errors.
|
---|
790 | */
|
---|
791 |
|
---|
792 | BOOL lstRemoveNode(PLINKLIST pList,
|
---|
793 | PLISTNODE pRemoveNode)
|
---|
794 | {
|
---|
795 | BOOL fFound = FALSE;
|
---|
796 |
|
---|
797 | if ( (pList)
|
---|
798 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
799 | && (pRemoveNode)
|
---|
800 | )
|
---|
801 | {
|
---|
802 | if (pList->pFirst == pRemoveNode)
|
---|
803 | // item to be removed is first: adjust first
|
---|
804 | pList->pFirst = pRemoveNode->pNext; // can be NULL
|
---|
805 |
|
---|
806 | if (pList->pLast == pRemoveNode)
|
---|
807 | // item to be removed is last: adjust last
|
---|
808 | pList->pLast = pRemoveNode->pPrevious; // can be NULL
|
---|
809 |
|
---|
810 | if (pRemoveNode->pPrevious)
|
---|
811 | // adjust previous item
|
---|
812 | pRemoveNode->pPrevious->pNext = pRemoveNode->pNext;
|
---|
813 |
|
---|
814 | if (pRemoveNode->pNext)
|
---|
815 | // adjust next item
|
---|
816 | pRemoveNode->pNext->pPrevious = pRemoveNode->pPrevious;
|
---|
817 |
|
---|
818 | // decrease list count
|
---|
819 | pList->ulCount--;
|
---|
820 |
|
---|
821 | // free node data
|
---|
822 | if (pList->fItemsFreeable)
|
---|
823 | if (pRemoveNode->pItemData)
|
---|
824 | free(pRemoveNode->pItemData);
|
---|
825 | // free node
|
---|
826 | free(pRemoveNode);
|
---|
827 |
|
---|
828 | fFound = TRUE;
|
---|
829 | }
|
---|
830 |
|
---|
831 | return fFound;
|
---|
832 | }
|
---|
833 |
|
---|
834 | /*
|
---|
835 | *@@ lstRemoveItem:
|
---|
836 | * as opposed to lstRemoveNode, this removes a
|
---|
837 | * node according to an item data pointer.
|
---|
838 | *
|
---|
839 | * This needs to call lstNodeFromItem first and
|
---|
840 | * then invokes lstRemoveNode, so this is a lot
|
---|
841 | * slower.
|
---|
842 | *
|
---|
843 | * If fItemsFreeable had been specified as TRUE
|
---|
844 | * when the list was created (lstInit or lstCreate),
|
---|
845 | * free() will be invoked on the data item pointer also.
|
---|
846 | * See the remarks there.
|
---|
847 | *
|
---|
848 | * Returns TRUE if successful, FALSE upon errors.
|
---|
849 | */
|
---|
850 |
|
---|
851 | BOOL lstRemoveItem(PLINKLIST pList, void* pRemoveItem)
|
---|
852 | {
|
---|
853 | PLISTNODE pNode;
|
---|
854 |
|
---|
855 | if (pNode = lstNodeFromItem(pList, pRemoveItem))
|
---|
856 | return lstRemoveNode(pList, pNode);
|
---|
857 |
|
---|
858 | return FALSE;
|
---|
859 | }
|
---|
860 |
|
---|
861 | /*
|
---|
862 | *@@ lstSwapItems:
|
---|
863 | * this will swap the items pNode1 and pNode2.
|
---|
864 | * This is used in the sort routines.
|
---|
865 | *
|
---|
866 | * Note that it is not really the nodes that are swapped,
|
---|
867 | * but only the data item pointers. This is a lot quicker
|
---|
868 | * than what we did in versions < 0.9.0.
|
---|
869 | */
|
---|
870 |
|
---|
871 | BOOL lstSwapNodes(PLISTNODE pNode1,
|
---|
872 | PLISTNODE pNode2)
|
---|
873 | {
|
---|
874 | if ( (pNode1) && (pNode2) )
|
---|
875 | {
|
---|
876 | void* pTemp = pNode1->pItemData;
|
---|
877 | pNode1->pItemData = pNode2->pItemData;
|
---|
878 | pNode2->pItemData = pTemp;
|
---|
879 |
|
---|
880 | return TRUE;
|
---|
881 | }
|
---|
882 |
|
---|
883 | return FALSE;
|
---|
884 | }
|
---|
885 |
|
---|
886 | /* ******************************************************************
|
---|
887 | *
|
---|
888 | * List sorting
|
---|
889 | *
|
---|
890 | ********************************************************************/
|
---|
891 |
|
---|
892 | /*
|
---|
893 | * lstQuickSort2:
|
---|
894 | * helper routine for recursing during quicksort (lstQuickSort).
|
---|
895 | */
|
---|
896 |
|
---|
897 | void lstQuickSort2(PLINKLIST pList,
|
---|
898 | PFNSORTLIST pfnSort,
|
---|
899 | void* pStorage,
|
---|
900 | long lLeft,
|
---|
901 | long lRight)
|
---|
902 | {
|
---|
903 | long ll = lLeft,
|
---|
904 | lr = lRight - 1,
|
---|
905 | lPivot = lRight;
|
---|
906 |
|
---|
907 | if (lRight > lLeft)
|
---|
908 | {
|
---|
909 | PLISTNODE pNodeLeft = lstNodeFromIndex(pList, ll),
|
---|
910 | pNodeRight = lstNodeFromIndex(pList, lr),
|
---|
911 | pNodePivot = lstNodeFromIndex(pList, lPivot);
|
---|
912 |
|
---|
913 | while (TRUE)
|
---|
914 | {
|
---|
915 | // compare left item data to pivot item data
|
---|
916 | while ( pfnSort(pNodeLeft->pItemData,
|
---|
917 | pNodePivot->pItemData,
|
---|
918 | pStorage)
|
---|
919 | < 0 )
|
---|
920 | {
|
---|
921 | ll++;
|
---|
922 | // advance to next
|
---|
923 | pNodeLeft = pNodeLeft->pNext;
|
---|
924 | }
|
---|
925 |
|
---|
926 | // compare right item to pivot
|
---|
927 | while ( ( pfnSort(pNodeRight->pItemData,
|
---|
928 | pNodePivot->pItemData,
|
---|
929 | pStorage)
|
---|
930 | >= 0 )
|
---|
931 | && (lr > ll)
|
---|
932 | )
|
---|
933 | {
|
---|
934 | lr--;
|
---|
935 | // step to previous
|
---|
936 | pNodeRight = pNodeRight->pPrevious;
|
---|
937 | }
|
---|
938 |
|
---|
939 | if (lr <= ll)
|
---|
940 | break;
|
---|
941 |
|
---|
942 | // swap pNodeLeft and pNodeRight
|
---|
943 | lstSwapNodes(pNodeLeft, pNodeRight);
|
---|
944 | }
|
---|
945 |
|
---|
946 | // swap pNodeLeft and pNodePivot
|
---|
947 | lstSwapNodes(pNodeLeft, pNodePivot);
|
---|
948 |
|
---|
949 | // recurse!
|
---|
950 | lstQuickSort2(pList, pfnSort, pStorage,
|
---|
951 | lLeft,
|
---|
952 | ll - 1);
|
---|
953 | lstQuickSort2(pList, pfnSort, pStorage,
|
---|
954 | ll + 1,
|
---|
955 | lRight);
|
---|
956 | }
|
---|
957 | }
|
---|
958 |
|
---|
959 | /*
|
---|
960 | *@@ lstQuickSort:
|
---|
961 | * this will sort the given linked list using the
|
---|
962 | * sort function pfnSort, which works similar to those of the
|
---|
963 | * container sorts, i.e. it must be declared as a
|
---|
964 | + signed short XWPENTRY fnSort(void* pItem1, void* pItem2, void* pStorage)
|
---|
965 | *
|
---|
966 | * These sort functions need to return the following:
|
---|
967 | * -- 0: pItem1 == pItem2
|
---|
968 | * -- -1: pItem1 < pItem2
|
---|
969 | * -- +1: pItem1 > pItem2
|
---|
970 | *
|
---|
971 | * Note that the comparison function does not get the PLISTNODEs,
|
---|
972 | * but the item data pointers instead.
|
---|
973 | *
|
---|
974 | * The pStorage parameter will simply be passed to the comparison
|
---|
975 | * function. This might be useful for additional data you might need.
|
---|
976 | *
|
---|
977 | * This returns FALSE upon errors.
|
---|
978 | *
|
---|
979 | * This function implements the "quick sort" algorithm, which is one
|
---|
980 | * of the fastest sort algorithms known to mankind. However, this is
|
---|
981 | * an "instable" sort algorithm, meaning that list items considered
|
---|
982 | * equal by pfnSort do _not_ retain their position, but might be
|
---|
983 | * changed. If you need these to remain where they are, use
|
---|
984 | * lstBubbleSort, which is a lot slower though.
|
---|
985 | *
|
---|
986 | * This calls lstSwapNodes to swap the list items.
|
---|
987 | */
|
---|
988 |
|
---|
989 | BOOL lstQuickSort(PLINKLIST pList,
|
---|
990 | PFNSORTLIST pfnSort,
|
---|
991 | void* pStorage)
|
---|
992 | {
|
---|
993 | BOOL brc = FALSE;
|
---|
994 |
|
---|
995 | if ( (pList)
|
---|
996 | && (pList->ulMagic == LINKLISTMAGIC)
|
---|
997 | && (pfnSort)
|
---|
998 | )
|
---|
999 | {
|
---|
1000 | long lRight = lstCountItems(pList) - 1;
|
---|
1001 |
|
---|
1002 | lstQuickSort2(pList, pfnSort, pStorage,
|
---|
1003 | 0, // lLeft
|
---|
1004 | lRight);
|
---|
1005 | brc = TRUE;
|
---|
1006 | }
|
---|
1007 |
|
---|
1008 | return brc;
|
---|
1009 | }
|
---|
1010 |
|
---|
1011 | /*
|
---|
1012 | *@@ lstBubbleSort:
|
---|
1013 | * just like lstQuickSort, this will sort a given linked list.
|
---|
1014 | * See lstQuickSort for the parameters.
|
---|
1015 | *
|
---|
1016 | * However, this function uses the "bubble sort" algorithm, which
|
---|
1017 | * will cause a lot of calls to pfnSort and which is really
|
---|
1018 | * ineffective for lists with more than 100 items.
|
---|
1019 | * Use only if you absolutely need a stable sort.
|
---|
1020 | *
|
---|
1021 | * This calls lstSwapNodes to swap the list items.
|
---|
1022 | */
|
---|
1023 |
|
---|
1024 | BOOL lstBubbleSort(PLINKLIST pList,
|
---|
1025 | PFNSORTLIST pfnSort,
|
---|
1026 | void* pStorage)
|
---|
1027 | {
|
---|
1028 | BOOL brc = FALSE;
|
---|
1029 |
|
---|
1030 | if (pList)
|
---|
1031 | if ( (pList->ulMagic == LINKLISTMAGIC)
|
---|
1032 | && (pfnSort)
|
---|
1033 | )
|
---|
1034 | {
|
---|
1035 | long lRight = lstCountItems(pList),
|
---|
1036 | lSorted,
|
---|
1037 | x;
|
---|
1038 |
|
---|
1039 | do {
|
---|
1040 | lRight--;
|
---|
1041 | lSorted = 0;
|
---|
1042 | for (x = 0; x < lRight; x++)
|
---|
1043 | {
|
---|
1044 | // ulComp++;
|
---|
1045 | PLISTNODE pNode1 = lstNodeFromIndex(pList, x),
|
---|
1046 | pNode2 = pNode1->pNext;
|
---|
1047 | if ((pNode1) && (pNode2))
|
---|
1048 | if ( (*pfnSort)(pNode1->pItemData,
|
---|
1049 | pNode2->pItemData,
|
---|
1050 | pStorage) > 0
|
---|
1051 | )
|
---|
1052 | {
|
---|
1053 | lstSwapNodes(pNode1, pNode2);
|
---|
1054 | lSorted++;
|
---|
1055 | }
|
---|
1056 | }
|
---|
1057 | } while ( lSorted && (lRight > 1) );
|
---|
1058 |
|
---|
1059 | brc = TRUE;
|
---|
1060 | }
|
---|
1061 |
|
---|
1062 | return brc;
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | /* ******************************************************************
|
---|
1066 | *
|
---|
1067 | * List pseudo-stacks
|
---|
1068 | *
|
---|
1069 | ********************************************************************/
|
---|
1070 |
|
---|
1071 | /*
|
---|
1072 | *@@ lstPush:
|
---|
1073 | * pushes any data onto a "stack", which is a plain
|
---|
1074 | * LINKLIST being abused as a stack.
|
---|
1075 | * The "stack" is last-in, first-out (LIFO), meaning
|
---|
1076 | * that the last item pushed onto the list will be
|
---|
1077 | * the first returned by lstPop. See lstPop for example
|
---|
1078 | * usage.
|
---|
1079 | *
|
---|
1080 | *@@added V0.9.3 (2000-05-18) [umoeller]
|
---|
1081 | */
|
---|
1082 |
|
---|
1083 | PLISTNODE lstPush(PLINKLIST pList,
|
---|
1084 | void* pNewItemData) // in: data to store in list node
|
---|
1085 | {
|
---|
1086 | return lstAppendItem(pList, pNewItemData);
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | /*
|
---|
1090 | *@@ lstPop:
|
---|
1091 | * returns the last item which has been pushed onto
|
---|
1092 | * a pseudo-stack LIFO LINKLIST using lstPush.
|
---|
1093 | *
|
---|
1094 | * If (fRemove == TRUE), the item is also removed
|
---|
1095 | * from the stack. Otherwise it remains there so the
|
---|
1096 | * next lstPop would return the next item.
|
---|
1097 | *
|
---|
1098 | * Example:
|
---|
1099 | *
|
---|
1100 | + LINKLIST ll;
|
---|
1101 | + PLISTNODE pNode;
|
---|
1102 | + lstInit(&ll, FALSE);
|
---|
1103 | +
|
---|
1104 | + lstPush(&ll, (PVOID)1);
|
---|
1105 | + lstPush(&ll, (PVOID)2);
|
---|
1106 | +
|
---|
1107 | + pNode = lstPop(&ll); // returns the node containing "2"
|
---|
1108 | + lstRemoveNode(pNode);
|
---|
1109 | + pNode = lstPop(&ll); // returns the node containing "1"
|
---|
1110 | *
|
---|
1111 | *@@added V0.9.3 (2000-05-18) [umoeller]
|
---|
1112 | */
|
---|
1113 |
|
---|
1114 | PLISTNODE lstPop(PLINKLIST pList)
|
---|
1115 | {
|
---|
1116 | return pList->pLast;
|
---|
1117 | }
|
---|
1118 |
|
---|
1119 |
|
---|