1 /*
2 Copyright (c) 1991-1999 Thomas T. Wetmore IV
3 "The MIT license"
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /*=============================================================
9 * list.c -- Doubly-linked list data type
10 * Copyright(c) 1991-94 by T.T. Wetmore IV; all rights reserved
11 *===========================================================*/
12
13 #include "standard.h"
14 #include "llstdlib.h"
15 #include "vtable.h"
16
17 /*********************************************
18 * local types
19 *********************************************/
20
21
22 /* list object itself */
23 struct tag_list {
24 /* a LIST is an OBJECT */
25 struct tag_vtable * vtable; /* generic object table (see vtable.h) */
26 INT l_refcnt; /* reference counted object */
27 LNODE l_head;
28 LNODE l_tail;
29 INT l_len;
30 INT l_type;
31 ELEMENT_DESTRUCTOR l_del_element;
32 };
33 /* typedef struct tag_list *LIST; */ /* in list.h */
34
35 /* list iterator */
36 struct tag_list_iter {
37 struct tag_vtable *vtable; /* generic object */
38 INT refcnt; /* ref-countable object */
39 LNODE current;
40 LIST list;
41 INT status; /* 1=forward, 1=reverse, 0=EOF */
42 };
43 /* typedef struct tag_list_iter * LIST_ITER; */ /* in list.h */
44
45 /*********************************************
46 * local enums & defines
47 *********************************************/
48
49 #define ltype(l) ((l)->l_type)
50 #define lhead(l) ((l)->l_head)
51 #define ltail(l) ((l)->l_tail)
52 #define llen(l) ((l)->l_len)
53
54 /*********************************************
55 * local function prototypes
56 *********************************************/
57
58 /* alphabetical */
59 static void free_list_element(VPTR vptr);
60 static void free_list_iter(LIST_ITER listit);
61 static void list_destructor(VTABLE *obj);
62 static void listit_destructor(VTABLE *obj);
63 void make_list_empty_impl(LIST list, ELEMENT_DESTRUCTOR func);
64 static LNODE nth_in_list_from_tail(LIST list, INT index1b, BOOLEAN createels
65 , LIST_CREATE_VALUE createfnc);
66 static void validate_list(LIST list);
67
68 /*********************************************
69 * local variables
70 *********************************************/
71
72 static struct tag_vtable vtable_for_list = {
73 VTABLE_MAGIC
74 , "list"
75 , &list_destructor
76 , &refcountable_isref
77 , &refcountable_addref
78 , &refcountable_release
79 , 0 /* copy_fnc */
80 , &generic_get_type_name
81 };
82 static struct tag_vtable vtable_for_listit = {
83 VTABLE_MAGIC
84 , "list_iter"
85 , &listit_destructor
86 , &refcountable_isref
87 , &refcountable_addref
88 , &refcountable_release
89 , 0 /* copy_fnc */
90 , &generic_get_type_name
91 };
92
93 /*===========================
94 * create_list_impl -- Create list
95 * returns addref'd list
96 *=========================*/
97 static LIST
create_list_impl(void)98 create_list_impl (void)
99 {
100 LIST list = (LIST) stdalloc(sizeof(*list));
101 memset(list, 0, sizeof(*list));
102 list->vtable = &vtable_for_list;
103 list->l_refcnt = 1;
104 ltype(list) = LISTNOFREE;
105 lhead(list) = ltail(list) = NULL;
106 llen(list) = 0;
107 validate_list(list);
108 return list;
109 }
110 /*===========================
111 * create_list -- Create list (LISTNOFREE)
112 * returns addref'd list
113 *=========================*/
114 LIST
create_list(void)115 create_list (void)
116 {
117 return create_list2(LISTNOFREE);
118 }
119 /*===========================
120 * create_list2 -- Create list, with free type
121 * returns addref'd list
122 *=========================*/
123 LIST
create_list2(INT whattofree)124 create_list2 (INT whattofree)
125 {
126 LIST list = create_list_impl();
127 ltype(list) = whattofree;
128 return list;
129 }
130 /*===========================
131 * create_list3 -- Create list, with element destructor
132 * returns addref'd list
133 *=========================*/
134 LIST
create_list3(ELEMENT_DESTRUCTOR func)135 create_list3 (ELEMENT_DESTRUCTOR func)
136 {
137 LIST list = create_list_impl();
138 list->l_del_element = func;
139 return list;
140 }
141 /*===========================
142 * destroy_list -- Delete all elements & destroy list
143 * list: [IN] list to completely delete
144 *=========================*/
145 void
destroy_list(LIST list)146 destroy_list (LIST list)
147 {
148 if (!list) return;
149 ASSERT(list->vtable == &vtable_for_list);
150 make_list_empty_impl(list, NULL);
151 destroy_empty_list(list);
152 }
153 /*===========================
154 * destroy_empty_list -- Destroy a list with no elements
155 * ASSERT check that list is in fact empty
156 *=========================*/
157 void
destroy_empty_list(LIST list)158 destroy_empty_list (LIST list)
159 {
160 if (!list) return;
161 ASSERT(list->vtable == &vtable_for_list);
162 ASSERT(llen(list) == 0);
163 stdfree(list);
164 }
165 /*===========================
166 * in_list -- find first element returning true from check function
167 * list: [IN] list to search
168 * el: [IN] parameter to pass thru to check function
169 * func: [IN] check function
170 * Calls check function on each element in turn until one returns TRUE
171 * Returns index of element found, or -1 if none pass check
172 *=========================*/
173 INT
in_list(LIST list,VPTR param,BOOLEAN (* func)(VPTR param,VPTR el))174 in_list (LIST list, VPTR param, BOOLEAN (*func)(VPTR param, VPTR el))
175 {
176 LNODE lnode;
177 INT index=0;
178 if (is_empty_list(list)) /* calls validate_list */
179 return -1;
180 lnode = lhead(list);
181 while (lnode) {
182 if ((*func)(param, lelement(lnode)))
183 return index;
184 lnode = lnext(lnode);
185 }
186 validate_list(list);
187 return -1;
188 }
189 /*===================================
190 * free_list_element -- Simple heap element destructor
191 *=================================*/
192 static void
free_list_element(VPTR vptr)193 free_list_element (VPTR vptr)
194 {
195 if (vptr) stdfree(vptr);
196 }
197 /*===================================
198 * make_list_empty_impl -- Make list empty
199 *=================================*/
200 void
make_list_empty_impl(LIST list,ELEMENT_DESTRUCTOR func)201 make_list_empty_impl (LIST list, ELEMENT_DESTRUCTOR func)
202 {
203 LNODE lnode0, lnode;
204
205 if (!list) return;
206
207 if (!func)
208 func = list->l_del_element;
209 if (!func) {
210 if (ltype(list) == LISTDOFREE)
211 func = &free_list_element;
212 }
213
214 lnode0 = lhead(list);
215 while (lnode0) {
216 lnode = lnext(lnode0);
217 if (func) (*func)(lelement(lnode0));
218 stdfree(lnode0);
219 lnode0 = lnode;
220 }
221 lhead(list) = ltail(list) = NULL;
222 llen(list) = 0;
223 /* no effect on refcount */
224 validate_list(list);
225 }
226 /*===================================
227 * make_list_empty -- Make list empty
228 *=================================*/
229 void
make_list_empty(LIST list)230 make_list_empty (LIST list)
231 {
232 make_list_empty_impl(list, NULL);
233 }
234 /*===================================
235 * is_empty_list -- Check for empty list
236 * list: [IN] list
237 *=================================*/
238 BOOLEAN
is_empty_list(const LIST list)239 is_empty_list (const LIST list)
240 {
241 validate_list(list);
242 return !list || !llen(list);
243 }
244 /*==================================
245 * push_list -- Push element on head of list
246 * list: [I/O] list
247 * el: [IN] new element
248 *================================*/
249 void
push_list(LIST list,VPTR el)250 push_list (LIST list, VPTR el)
251 {
252 LNODE node = NULL;
253
254 if (!list) return;
255 node = (LNODE) stdalloc(sizeof(*node));
256 lelement(node) = el;
257 if (is_empty_list(list)) {
258 lprev(node) = lnext(node) = NULL;
259 lhead(list) = ltail(list) = node;
260 } else {
261 lnext(node) = lhead(list);
262 lprev(lhead(list)) = node;
263 lprev(node) = NULL;
264 lhead(list) = node;
265 }
266 ++llen(list);
267 validate_list(list);
268 }
269 /*=========================================
270 * back_list -- Put element on tail of list
271 * list: [I/O] list
272 * el: [IN] new element
273 *=======================================*/
274 void
back_list(LIST list,VPTR el)275 back_list (LIST list, VPTR el)
276 {
277 LNODE node = NULL;
278
279 if (!list) return;
280 node = (LNODE) stdalloc(sizeof(*node));
281 lelement(node) = el;
282 if (is_empty_list(list)) {
283 lprev(node) = lnext(node) = NULL;
284 lhead(list) = ltail(list) = node;
285 } else {
286 lprev(node) = ltail(list);
287 lnext(ltail(list)) = node;
288 lnext(node) = NULL;
289 ltail(list) = node;
290 }
291 ++llen(list);
292 validate_list(list);
293 }
294 /*==================================
295 * pop_list -- Pop element from head of list
296 * list: [I/O] list
297 *================================*/
298 VPTR
pop_list(LIST list)299 pop_list (LIST list)
300 {
301 LNODE node;
302 VPTR el;
303 if (is_empty_list(list)) /* calls validate_list */
304 return NULL;
305 node = lhead(list);
306 lhead(list) = lnext(node);
307 if (!lhead(list))
308 ltail(list) = NULL;
309 else
310 lprev(lhead(list)) = NULL;
311 el = lelement(node);
312 stdfree(node);
313 --llen(list);
314 validate_list(list);
315 return el;
316 }
317 /*========================================
318 * validate_list -- Verify list ends don't disagree
319 *======================================*/
320 static void
validate_list(LIST list)321 validate_list (LIST list)
322 {
323 #ifdef LIST_ASSERTS
324 ASSERT(!list || (lhead(list)&<ail(list)) || (!lhead(list)&&!ltail(list)));
325 #else
326 list=list; /* unused */
327 #endif
328 }
329 /*========================================
330 * enqueue_list -- Enqueue element on head of list
331 *======================================*/
332 void
enqueue_list(LIST list,VPTR el)333 enqueue_list (LIST list, VPTR el)
334 {
335 push_list(list, el);
336 }
337 /*==========================================
338 * dequeue_list -- Dequeue element from tail of list
339 *========================================*/
340 VPTR
dequeue_list(LIST list)341 dequeue_list (LIST list)
342 {
343 return pop_list_tail(list);
344 }
345 /*==========================================
346 * pop_list_tail -- Pop element from tail of list
347 *========================================*/
348 VPTR
pop_list_tail(LIST list)349 pop_list_tail (LIST list)
350 {
351 LNODE node;
352 VPTR el;
353 if (is_empty_list(list)) /* calls validate_list */
354 return NULL;
355 node = ltail(list);
356 ltail(list) = lprev(node);
357 if (!ltail(list))
358 lhead(list) = NULL;
359 else
360 lnext(ltail(list)) = NULL;
361 el = lelement(node);
362 stdfree(node);
363 --llen(list);
364 validate_list(list);
365 return el;
366 }
367 /*=================================================
368 * nth_in_list_from_tail -- Find nth node in list, relative 1
369 * start at tail & count towards head
370 * createels is FALSE if caller does not want elements added (delete_list_element)
371 *===============================================*/
372 static LNODE
nth_in_list_from_tail(LIST list,INT index1b,BOOLEAN createels,LIST_CREATE_VALUE createfnc)373 nth_in_list_from_tail (LIST list, INT index1b, BOOLEAN createels, LIST_CREATE_VALUE createfnc)
374 {
375 if (!list) return NULL;
376 validate_list(list);
377 /* handle reverse indices */
378 if (index1b < 1) index1b += llen(list);
379 /* null if out of bounds */
380 if (index1b < 1) return NULL;
381 if (index1b < llen(list)/2) {
382 /* want element in back half, so count back from tail */
383 LNODE node = ltail(list);
384 INT i = index1b;
385 while (--i) {
386 node = lprev(node);
387 }
388 return node;
389 } else if (index1b <= llen(list)) {
390 /* want element in front half, so count forward from head */
391 LNODE node = lhead(list);
392 INT i = llen(list) + 1 - index1b;
393 while (--i) {
394 node = lnext(node);
395 }
396 return node;
397 } else if (createels) {
398 /* want element beyond end, so add as required */
399 INT i = index1b + 1 - llen(list);
400 while (--i) {
401 VPTR newv = createfnc ? (*createfnc)(list) : NULL;
402 enqueue_list(list, newv);
403 }
404 validate_list(list);
405 return lhead(list);
406 } else {
407 /* element beyond but caller said not to create */
408 return NULL;
409 }
410 }
411 /*==================================================
412 * set_list_element - Set element using array access
413 *================================================*/
414 void
set_list_element(LIST list,INT index1b,VPTR val,LIST_CREATE_VALUE createfnc)415 set_list_element (LIST list, INT index1b, VPTR val, LIST_CREATE_VALUE createfnc)
416 {
417 LNODE node = NULL;
418 BOOLEAN createels = TRUE;
419 if (!list) return;
420 node = nth_in_list_from_tail(list, index1b, createels, createfnc);
421 if (!node) return;
422 lelement(node) = val;
423 validate_list(list);
424 }
425 /*=======================================================
426 * get_list_element - Retrieve element using array access
427 *=====================================================*/
428 VPTR
get_list_element(LIST list,INT index1b,LIST_CREATE_VALUE createfnc)429 get_list_element (LIST list, INT index1b, LIST_CREATE_VALUE createfnc)
430 {
431 LNODE node = NULL;
432 BOOLEAN createels = TRUE;
433 if (!list) return 0;
434 node = nth_in_list_from_tail(list, index1b, createels, createfnc);
435 if (!node) return 0;
436 return lelement(node);
437 }
438 /*==================================
439 * length_list -- Return list length
440 *================================*/
441 INT
length_list(LIST list)442 length_list (LIST list)
443 {
444 return !list ? 0 : llen(list);
445 }
446 /*=======================================================
447 * peek_list_head - Retrieve head element without removing it
448 *=====================================================*/
449 VPTR
peek_list_head(LIST list)450 peek_list_head (LIST list)
451 {
452 LNODE node;
453 if (!list) return 0;
454 node = lhead(list);
455 if (!node) return 0;
456 return lelement(node);
457 }
458 /*=================================================
459 * create_list_iter -- Create new list iterator
460 *===============================================*/
461 static LIST_ITER
create_list_iter(LIST list)462 create_list_iter (LIST list)
463 {
464 LIST_ITER listit = (LIST_ITER)stdalloc(sizeof(*listit));
465 memset(listit, 0, sizeof(*listit));
466 listit->list = list;
467 listit->refcnt = 1;
468 return listit;
469 }
470 /*=================================================
471 * begin_list -- Begin iteration of list
472 *===============================================*/
473 LIST_ITER
begin_list(LIST list)474 begin_list (LIST list)
475 {
476 LIST_ITER listit = create_list_iter(list);
477 /* current=0 is signal to next_list_element that we're starting */
478 listit->status = (lhead(listit->list) ? 1 : 0);
479 return listit;
480 }
481 /*=================================================
482 * begin_list_rev -- Begin reverse iteration of list
483 *===============================================*/
484 LIST_ITER
begin_list_rev(LIST list)485 begin_list_rev (LIST list)
486 {
487 LIST_ITER listit = create_list_iter(list);
488 /* current=0 is signal to next_list_element that we're starting */
489 listit->status = (ltail(listit->list) ? -1 : 0);
490 return listit;
491 }
492 /*=================================================
493 * next_element -- Find next element in list (iterating)
494 *===============================================*/
495 static BOOLEAN
next_list_element(LIST_ITER listit)496 next_list_element (LIST_ITER listit)
497 {
498 if (!listit->status)
499 return FALSE;
500 if (!listit->current) {
501 /* beginning */
502 if (listit->status > 0)
503 listit->current = lhead(listit->list);
504 else
505 listit->current = ltail(listit->list);
506 } else {
507 unlock_list_node(listit->current);
508 if (listit->status > 0)
509 listit->current = lnext(listit->current);
510 else
511 listit->current = lprev(listit->current);
512 }
513 if (listit->current)
514 lock_list_node(listit->current);
515 else
516 listit->status = 0;
517 return !!listit->status;
518 }
519 /*=================================================
520 * next_list_ptr -- Iterating list with pointers
521 *===============================================*/
522 BOOLEAN
next_list_ptr(LIST_ITER listit,VPTR * pptr)523 next_list_ptr (LIST_ITER listit, VPTR *pptr)
524 {
525 if (!next_list_element(listit)) {
526 *pptr = 0;
527 return FALSE;
528 }
529 *pptr = lelement(listit->current);
530 return TRUE;
531 }
532 /*=================================================
533 * change_list_ptr -- User changing pointer during iteration
534 *===============================================*/
535 BOOLEAN
change_list_ptr(LIST_ITER listit,VPTR newptr)536 change_list_ptr (LIST_ITER listit, VPTR newptr)
537 {
538 if (!listit || !listit->current)
539 return FALSE;
540 lelement(listit->current) = newptr;
541 return TRUE;
542 }
543 /*=================================================
544 * end_list_iter -- Release reference to list iterator object
545 *===============================================*/
546 void
end_list_iter(LIST_ITER * plistit)547 end_list_iter (LIST_ITER * plistit)
548 {
549 ASSERT(plistit);
550 ASSERT(*plistit);
551 --(*plistit)->refcnt;
552 if (!(*plistit)->refcnt) {
553 free_list_iter(*plistit);
554 }
555 *plistit = 0;
556 }
557 /*=================================================
558 * free_list_iter -- Delete & free table iterator object
559 *===============================================*/
560 static void
free_list_iter(LIST_ITER listit)561 free_list_iter (LIST_ITER listit)
562 {
563 if (!listit) return;
564 ASSERT(!listit->refcnt);
565 memset(listit, 0, sizeof(*listit));
566 stdfree(listit);
567 }
568 /*=================================================
569 * lock_list_node -- Increment node lock count
570 *===============================================*/
571 void
lock_list_node(LNODE node)572 lock_list_node (LNODE node)
573 {
574 ++llocks(node);
575 }
576 /*=================================================
577 * unlock_list_node -- Decrement node lock count
578 *===============================================*/
579 void
unlock_list_node(LNODE node)580 unlock_list_node (LNODE node)
581 {
582 --llocks(node);
583 ASSERT(llocks(node) >= 0);
584 }
585 /*==================================================
586 * delete_list_element - Delete element using array access
587 * Call func (unless NULL) on element before deleting
588 *================================================*/
589 #ifdef UNUSED_CODE
590 BOOLEAN
delete_list_element(LIST list,INT index1b,ELEMENT_DESTRUCTOR func)591 delete_list_element (LIST list, INT index1b, ELEMENT_DESTRUCTOR func)
592 {
593 LNODE node = NULL;
594 BOOLEAN createels = FALSE;
595 if (!list) return FALSE;
596 node = nth_in_list_from_tail(list, index1b, createels, 0);
597 if (!node) return FALSE;
598 if (llocks(node)) return FALSE;
599 if (llen(list) == 1) {
600 /* removing last element of list */
601 llen(list) = 0;
602 lhead(list) = ltail(list) = 0;
603 if (func)
604 (*func)(lelement(node));
605 stdfree(node);
606 return TRUE;
607 }
608 detach_node_from_list(list, node);
609 return TRUE;
610 }
611 #endif
612 /*==================================================
613 * detach_node_from_list - Remove node from list
614 * does not delete node, simply detaches it from the list
615 * and decrements list's count
616 *================================================*/
617 static void
detach_node_from_list(LIST list,LNODE node)618 detach_node_from_list (LIST list, LNODE node)
619 {
620 validate_list(list);
621 if (lprev(node)) {
622 lnext(lprev(node)) = lnext(node);
623 }
624 if (lnext(node)) {
625 lprev(lnext(node)) = lprev(node);
626 }
627 if (lhead(list) == node) {
628 lhead(list) = lnext(node);
629 }
630 if (ltail(list) == node) {
631 ltail(list) = lprev(node);
632 }
633 --llen(list);
634 validate_list(list);
635 }
636 /*==================================================
637 * find_delete_list_elements - Delete qualifying element(s)
638 * list: [I/O] list to change
639 * func: [IN] test function to qualify elements (return TRUE to choose)
640 * deleteall: [IN] true to delete all qualifying, false to delete first
641 * returns number elements deleted
642 *================================================*/
643 INT
find_delete_list_elements(LIST list,VPTR param,BOOLEAN (* func)(VPTR param,VPTR el),BOOLEAN deleteall)644 find_delete_list_elements (LIST list, VPTR param,
645 BOOLEAN (*func)(VPTR param, VPTR el), BOOLEAN deleteall)
646 {
647 LNODE lnode = NULL;
648 INT count = 0;
649 if (is_empty_list(list)) /* calls validate_list */
650 return 0;
651 ASSERT(func);
652 lnode = lhead(list);
653 while (lnode) {
654 LNODE lnext = lnext(lnode);
655 if ((*func)(param, lelement(lnode))) {
656 detach_node_from_list(list, lnode);
657 ++count;
658 if (ltype(list) == LISTDOFREE) {
659 free_list_element(lelement(lnode));
660 }
661 if (!deleteall)
662 return count;
663 }
664 lnode = lnext;
665 }
666 return count;
667
668 }
669 /*==================================================
670 * trav_list_head - Return tail node of list
671 * Only for internal use in FORLIST implementation
672 *================================================*/
673 LNODE
trav_list_head(LIST list)674 trav_list_head (LIST list)
675 {
676 ASSERT(list);
677 return list->l_head;
678 }
679 /*==================================================
680 * trav_list_tail - Return tail node of list
681 * Only for internal use in FORLIST implementation
682 *================================================*/
683 LNODE
trav_list_tail(LIST list)684 trav_list_tail (LIST list)
685 {
686 ASSERT(list);
687 return list->l_tail;
688 }
689 /*=================================================
690 * list_destructor -- destructor for list
691 * (destructor entry in vtable)
692 *===============================================*/
693 static void
list_destructor(VTABLE * obj)694 list_destructor (VTABLE *obj)
695 {
696 LIST list = (LIST)obj;
697 ASSERT((*obj) == &vtable_for_list);
698 destroy_list(list);
699 }
700 /*=================================================
701 * listit_destructor -- destructor for list iterator
702 * (vtable destructor)
703 *===============================================*/
704 static void
listit_destructor(VTABLE * obj)705 listit_destructor (VTABLE *obj)
706 {
707 LIST_ITER listit = (LIST_ITER)obj;
708 ASSERT(listit->vtable == &vtable_for_listit);
709 free_list_iter(listit);
710 }
711
712 /*=================================================
713 * addref_list -- increment reference count of list
714 *===============================================*/
715 void
addref_list(LIST list)716 addref_list (LIST list)
717 {
718 ASSERT(list->vtable == &vtable_for_list);
719 ++list->l_refcnt;
720 }
721 /*=================================================
722 * release_list -- decrement reference count of list
723 * and free if appropriate (ref count hits zero)
724 *===============================================*/
725 void
release_list(LIST list)726 release_list (LIST list)
727 {
728 ASSERT(list->vtable == &vtable_for_list);
729 --list->l_refcnt;
730 if (!list->l_refcnt) {
731 destroy_list(list);
732 }
733 }
734