1 /* =========================================================================
2 zlistx - extended generic list container
3
4 Copyright (c) the Contributors as noted in the AUTHORS file.
5 This file is part of CZMQ, the high-level C binding for 0MQ:
6 http://czmq.zeromq.org.
7
8 This Source Code Form is subject to the terms of the Mozilla Public
9 License, v. 2.0. If a copy of the MPL was not distributed with this
10 file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 =========================================================================
12 */
13
14 /*
15 @header
16 Provides a generic doubly-linked list container. This container provides
17 hooks for duplicator, comparator, and destructor functions. These tie
18 into CZMQ and standard C semantics, so e.g. for string items you can
19 use strdup, strcmp, and zstr_free. To store custom objects, define your
20 own duplicator and comparator, and use the standard object destructor.
21 @discuss
22 This is a reworking of the simpler zlist container. It is faster to
23 insert and delete items anywhere in the list, and to keep ordered lists.
24 @end
25 */
26
27 #include "czmq_classes.h"
28
29 #define NODE_TAG 0xcafe0006
30
31 // List node, used internally only
32
33 typedef struct _node_t {
34 uint32_t tag; // Object tag for validity checking
35 struct _node_t *next;
36 struct _node_t *prev;
37 void *item;
38 } node_t;
39
40
41 // ---------------------------------------------------------------------
42 // Structure of our class
43
44 struct _zlistx_t {
45 node_t *head; // First item in list, if any
46 node_t *cursor; // Current cursors for iteration
47 size_t size; // Number of items in list
48 // Function callbacks for duplicating and destroying items, if any
49 zlistx_duplicator_fn *duplicator;
50 zlistx_destructor_fn *destructor;
51 zlistx_comparator_fn *comparator;
52 };
53
54
55 // Initialize a list node and attach to the prev and next nodes, or itself
56 // if these are specified as null. Returns new node.
57
58 static node_t *
s_node_new(void * item)59 s_node_new (void *item)
60 {
61 node_t *self = (node_t *) zmalloc (sizeof (node_t));
62 assert (self);
63 self->tag = NODE_TAG;
64 self->prev = self;
65 self->next = self;
66 self->item = item;
67 return self;
68 }
69
70
71 // Removing and inserting a node are actually the same operation:
72 // swap (node->next, prev->next)
73 // swap (node->prev, next->prev)
74 // Which require only that the node be initialized to point to itself.
75 // When inserting, node goes in between prev and next.
76
77 static void
s_node_relink(node_t * node,node_t * prev,node_t * next)78 s_node_relink (node_t *node, node_t *prev, node_t *next)
79 {
80 node_t *temp = node->next;
81 node->next = prev->next;
82 prev->next = temp;
83 temp = node->prev;
84 node->prev = next->prev;
85 next->prev = temp;
86 }
87
88 // Default comparator
89
90 static int
s_comparator(const void * item1,const void * item2)91 s_comparator (const void *item1, const void *item2)
92 {
93 if (item1 == item2)
94 return 0;
95 else
96 if (item1 < item2)
97 return -1;
98 else
99 return 1;
100 }
101
102
103 // --------------------------------------------------------------------------
104 // Create a new, empty list.
105
106 zlistx_t *
zlistx_new(void)107 zlistx_new (void)
108 {
109 zlistx_t *self = (zlistx_t *) zmalloc (sizeof (zlistx_t));
110 assert (self);
111 self->head = s_node_new (NULL);
112 assert (self->head);
113 self->cursor = self->head;
114 self->comparator = s_comparator;
115 return self;
116 }
117
118
119 // --------------------------------------------------------------------------
120 // Destroy a list. If an item destructor was specified, all items in the
121 // list are automatically destroyed as well.
122
123 void
zlistx_destroy(zlistx_t ** self_p)124 zlistx_destroy (zlistx_t **self_p)
125 {
126 assert (self_p);
127 if (*self_p) {
128 zlistx_t *self = *self_p;
129 zlistx_purge (self);
130 freen (self->head);
131 freen (self);
132 *self_p = NULL;
133 }
134 }
135
136
137 // --------------------------------------------------------------------------
138 // Add an item to the head of the list. Calls the item duplicator, if any,
139 // on the item. Resets cursor to list head. Returns an item handle on
140 // success.
141
142 void *
zlistx_add_start(zlistx_t * self,void * item)143 zlistx_add_start (zlistx_t *self, void *item)
144 {
145 assert (self);
146 assert (item);
147
148 if (self->duplicator) {
149 item = (self->duplicator) (item);
150 assert (item);
151 }
152 node_t *node = s_node_new (item);
153 assert (node);
154
155 // Insert after head
156 s_node_relink (node, self->head, self->head->next);
157 self->cursor = self->head;
158 self->size++;
159 return node;
160 }
161
162
163 // --------------------------------------------------------------------------
164 // Add an item to the tail of the list. Calls the item duplicator, if any,
165 // on the item. Resets cursor to list head. Returns an item handle on
166 // success.
167
168 void *
zlistx_add_end(zlistx_t * self,void * item)169 zlistx_add_end (zlistx_t *self, void *item)
170 {
171 assert (self);
172 assert (item);
173
174 if (self->duplicator) {
175 item = (self->duplicator) (item);
176 assert (item);
177 }
178 node_t *node = s_node_new (item);
179 assert (node);
180
181 // Insert before head
182 s_node_relink (node, self->head->prev, self->head);
183 self->cursor = self->head;
184 self->size++;
185 return node;
186 }
187
188
189 // --------------------------------------------------------------------------
190 // Return the number of items in the list
191
192 size_t
zlistx_size(zlistx_t * self)193 zlistx_size (zlistx_t *self)
194 {
195 assert (self);
196 return self->size;
197 }
198
199
200 // --------------------------------------------------------------------------
201 // Return the item at the head of list. If the list is empty, returns NULL.
202 // Leaves cursor as-is.
203
204 void *
zlistx_head(zlistx_t * self)205 zlistx_head (zlistx_t *self)
206 {
207 assert (self);
208 return self->head? self->head->next->item: NULL;
209 }
210
211
212 // --------------------------------------------------------------------------
213 // Return the item at the tail of list. If the list is empty, returns NULL.
214
215 // Leaves cursor as-is.
216
217 void *
zlistx_tail(zlistx_t * self)218 zlistx_tail (zlistx_t *self)
219 {
220 assert (self);
221 return self->head? self->head->prev->item: NULL;
222 }
223
224
225 // --------------------------------------------------------------------------
226 // Return the item at the head of list. If the list is empty, returns NULL.
227 // Leaves cursor pointing at the head item, or NULL if the list is empty.
228
229 void *
zlistx_first(zlistx_t * self)230 zlistx_first (zlistx_t *self)
231 {
232 assert (self);
233 self->cursor = self->head->next;
234 return self->cursor == self->head? NULL: self->cursor->item;
235 }
236
237
238 // --------------------------------------------------------------------------
239 // Return the next item. At the end of the list (or in an empty list),
240 // returns NULL. Use repeated zlistx_next () calls to work through the list
241 // from zlistx_first (). First time, acts as zlistx_first().
242
243 void *
zlistx_next(zlistx_t * self)244 zlistx_next (zlistx_t *self)
245 {
246 assert (self);
247 self->cursor = self->cursor->next;
248 return self->cursor == self->head? NULL: self->cursor->item;
249 }
250
251
252 // --------------------------------------------------------------------------
253 // Return the previous item. At the start of the list (or in an empty list),
254 // returns NULL. Use repeated zlistx_prev () calls to work through the list
255 // backwards from zlistx_last (). First time, acts as zlistx_last().
256
257 void *
zlistx_prev(zlistx_t * self)258 zlistx_prev (zlistx_t *self)
259 {
260 assert (self);
261 self->cursor = self->cursor->prev;
262 return self->cursor == self->head? NULL: self->cursor->item;
263 }
264
265
266 // --------------------------------------------------------------------------
267 // Return the item at the tail of list. If the list is empty, returns NULL.
268 // Leaves cursor pointing at the tail item, or NULL if the list is empty.
269
270 void *
zlistx_last(zlistx_t * self)271 zlistx_last (zlistx_t *self)
272 {
273 assert (self);
274 self->cursor = self->head->prev;
275 return self->cursor == self->head? NULL: self->cursor->item;
276 }
277
278
279 // --------------------------------------------------------------------------
280 // Returns the value of the item at the cursor, or NULL if the cursor is
281 // not pointing to an item.
282
283 void *
zlistx_item(zlistx_t * self)284 zlistx_item (zlistx_t *self)
285 {
286 assert (self);
287 return self->cursor == self->head? NULL: self->cursor->item;
288 }
289
290
291 // --------------------------------------------------------------------------
292 // Returns the handle of the item at the cursor, or NULL if the cursor is
293 // not pointing to an item.
294
295 void *
zlistx_cursor(zlistx_t * self)296 zlistx_cursor (zlistx_t *self)
297 {
298 assert (self);
299 return self->cursor == self->head? NULL: self->cursor;
300 }
301
302 // --------------------------------------------------------------------------
303 // Returns the item associated with the given list handle, or NULL if passed
304 // handle is NULL. asserts that the passed in ptr points to a list node.
305
306 void *
zlistx_handle_item(void * handle)307 zlistx_handle_item (void *handle)
308 {
309 if (!handle)
310 return NULL;
311
312 node_t *node = (node_t *) handle;
313 assert (node->tag == NODE_TAG);
314 return node->item;
315 }
316
317
318 // --------------------------------------------------------------------------
319 // Find an item in the list, searching from the start. Uses the item
320 // comparator, if any, else compares item values directly. Returns the
321 // item handle found, or NULL. Sets the cursor to the found item, if any.
322
323 void *
zlistx_find(zlistx_t * self,void * item)324 zlistx_find (zlistx_t *self, void *item)
325 {
326 assert (self);
327 assert (item);
328
329 // Scan list for item, this is a O(N) operation
330 node_t *node = self->head->next;
331 while (node != self->head) {
332 if (self->comparator (node->item, item) == 0) {
333 self->cursor = node;
334 return node;
335 }
336 node = node->next;
337 }
338 return NULL;
339 }
340
341
342 // --------------------------------------------------------------------------
343 // Detach an item from the list, using its handle. The item is not modified,
344 // and the caller is responsible for destroying it if necessary. If handle is
345 // null, detaches the first item on the list. Returns item that was detached,
346 // or null if none was. If cursor was at item, moves cursor to previous item,
347 // so you can detach items while iterating forwards through a list.
348
349 void *
zlistx_detach(zlistx_t * self,void * handle)350 zlistx_detach (zlistx_t *self, void *handle)
351 {
352 assert (self);
353 node_t *node = (node_t *) handle;
354 if (!node)
355 node = self->head->next == self->head? NULL: self->head->next;
356
357 // Now detach node from list, without destroying item
358 if (node) {
359 // Reposition cursor so that delete/detach works during iteration
360 if (self->cursor == node)
361 self->cursor = self->cursor->prev;
362
363 // Remove node from list
364 assert (node->tag == NODE_TAG);
365 s_node_relink (node, node->prev, node->next);
366 node->tag = 0xDeadBeef;
367 void *item = node->item;
368 freen (node);
369 self->size--;
370 return item;
371 }
372 else {
373 assert (self->size == 0);
374 return NULL;
375 }
376 }
377
378
379 // --------------------------------------------------------------------------
380 // Detach item at the cursor, if any, from the list. The item is not modified,
381 // and the caller is responsible for destroying it as necessary. Returns item
382 // that was detached, or null if none was. Moves cursor to previous item, so
383 // you can detach items while iterating forwards through a list.
384
385 void *
zlistx_detach_cur(zlistx_t * self)386 zlistx_detach_cur (zlistx_t *self)
387 {
388 return zlistx_detach (self, zlistx_cursor (self));
389 }
390
391
392 // --------------------------------------------------------------------------
393 // Delete an item, using its handle. Calls the item destructor if any is
394 // set. If handle is null, deletes the first item on the list. Returns 0
395 // if an item was deleted, -1 if not. If cursor was at item, moves cursor
396 // to previous item, so you can delete items while iterating forwards
397 // through a list.
398
399 int
zlistx_delete(zlistx_t * self,void * handle)400 zlistx_delete (zlistx_t *self, void *handle)
401 {
402 assert (self);
403 void *item = zlistx_detach (self, handle);
404 if (item) {
405 if (self->destructor)
406 self->destructor (&item);
407 return 0;
408 }
409 else
410 return -1;
411 }
412
413
414 // --------------------------------------------------------------------------
415 // Move an item to the start of the list, via its handle.
416
417 void
zlistx_move_start(zlistx_t * self,void * handle)418 zlistx_move_start (zlistx_t *self, void *handle)
419 {
420 assert (self);
421 assert (handle);
422 node_t *node = (node_t *) handle;
423 assert (node->tag == NODE_TAG);
424
425 node_t *next = self->head->next;
426 if (node != next) {
427 // Remove node from list, insert before next node
428 s_node_relink (node, node->prev, node->next);
429 s_node_relink (node, next->prev, next);
430 }
431 }
432
433
434 // --------------------------------------------------------------------------
435 // Move an item to the end of the list, via its handle.
436
437 void
zlistx_move_end(zlistx_t * self,void * handle)438 zlistx_move_end (zlistx_t *self, void *handle)
439 {
440 assert (self);
441 assert (handle);
442 node_t *node = (node_t *) handle;
443 assert (node->tag == NODE_TAG);
444
445 node_t *prev = self->head->prev;
446 if (node != prev) {
447 // Remove node from list, insert after prev node
448 s_node_relink (node, node->prev, node->next);
449 s_node_relink (node, prev, prev->next);
450 }
451 }
452
453
454 // --------------------------------------------------------------------------
455 // Remove all items from the list, and destroy them if the item destructor
456 // is set.
457
458 void
zlistx_purge(zlistx_t * self)459 zlistx_purge (zlistx_t *self)
460 {
461 assert (self);
462 while (zlistx_size (self) > 0)
463 zlistx_delete (self, NULL);
464 }
465
466
467 // --------------------------------------------------------------------------
468 // Sort the list. If an item comparator was set, calls that to compare
469 // items, otherwise compares on item value. The sort is not stable, so may
470 // reorder equal items.
471
472 void
zlistx_sort(zlistx_t * self)473 zlistx_sort (zlistx_t *self)
474 {
475 // Uses a comb sort, which is simple and reasonably fast
476 // See http://en.wikipedia.org/wiki/Comb_sort
477 assert (self);
478 size_t gap = self->size;
479 bool swapped = false;
480 while (gap > 1 || swapped) {
481 gap = (size_t) ((double) gap / 1.3);
482 node_t *base = self->head->next;
483 node_t *test = self->head->next;
484 size_t jump = gap;
485 while (jump--)
486 test = test->next;
487
488 swapped = false;
489 while (base != self->head && test != self->head) {
490 if (self->comparator (base->item, test->item) > 0) {
491 // We don't actually swap nodes, just the items in the nodes.
492 // This is ridiculously simple and confuses the heck out of
493 // me every time I re-read the code, as I expect to see the
494 // nodes being swapped. Hence this comment. -- PH 2014/09/06
495 void *item = base->item;
496 base->item = test->item;
497 test->item = item;
498 swapped = true;
499 }
500 base = base->next;
501 test = test->next;
502 }
503 }
504 }
505
506
507 // --------------------------------------------------------------------------
508 // Create a new node and insert it into a sorted list. Calls the item
509 // duplicator, if any, on the item. If low_value is true, starts searching
510 // from the start of the list, otherwise searches from the end. Use the item
511 // comparator, if any, to find where to place the new node. Returns a handle
512 // to the new node, or NULL if memory was exhausted. Resets the cursor to the
513 // list head.
514
515 void *
zlistx_insert(zlistx_t * self,void * item,bool low_value)516 zlistx_insert (zlistx_t *self, void *item, bool low_value)
517 {
518 assert (self);
519 if (self->duplicator) {
520 item = (self->duplicator) (item);
521 assert (item);
522 }
523 node_t *node = s_node_new (item);
524 assert (node);
525 zlistx_reorder (self, node, low_value);
526 self->cursor = self->head;
527 self->size++;
528 return node;
529 }
530
531
532 // --------------------------------------------------------------------------
533 // Move an item, specified by handle, into position in a sorted list. Uses
534 // the item comparator, if any, to determine the new location. If low_value
535 // is true, starts searching from the start of the list, otherwise searches
536 // from the end.
537
538 void
zlistx_reorder(zlistx_t * self,void * handle,bool low_value)539 zlistx_reorder (zlistx_t *self, void *handle, bool low_value)
540 {
541 assert (self);
542 assert (handle);
543 node_t *node = (node_t *) handle;
544 assert (node->tag == NODE_TAG);
545
546 // Remove node from list, if it's attached
547 s_node_relink (node, node->prev, node->next);
548
549 if (low_value) {
550 node_t *next = self->head->next;
551 while (next != self->head) {
552 if (self->comparator (node->item, next->item) <= 0)
553 break;
554 next = next->next;
555 }
556 // Relink node before next node
557 s_node_relink (node, next->prev, next);
558 }
559 else {
560 node_t *prev = self->head->prev;
561 while (prev != self->head) {
562 if (self->comparator (prev->item, node->item) <= 0)
563 break;
564 prev = prev->prev;
565 }
566 // Relink node after prev node
567 s_node_relink (node, prev, prev->next);
568 }
569 }
570
571
572 // --------------------------------------------------------------------------
573 // Make a copy of the list; items are duplicated if you set a duplicator
574 // for the list, otherwise not. Copying a null reference returns a null
575 // reference.
576
577 zlistx_t *
zlistx_dup(zlistx_t * self)578 zlistx_dup (zlistx_t *self)
579 {
580 if (!self)
581 return NULL;
582
583 zlistx_t *copy = zlistx_new ();
584 if (copy) {
585 // Copy item handlers
586 copy->destructor = self->destructor;
587 copy->duplicator = self->duplicator;
588 copy->comparator = self->comparator;
589
590 // Copy nodes
591 node_t *node;
592 for (node = self->head->next; node != self->head; node = node->next)
593 zlistx_add_end (copy, node->item);
594 }
595 return copy;
596 }
597
598
599 // --------------------------------------------------------------------------
600 // Set a user-defined deallocator for list items; by default items are not
601 // freed when the list is destroyed.
602
603 void
zlistx_set_destructor(zlistx_t * self,zlistx_destructor_fn destructor)604 zlistx_set_destructor (zlistx_t *self, zlistx_destructor_fn destructor)
605 {
606 assert (self);
607 self->destructor = destructor;
608 }
609
610
611 // --------------------------------------------------------------------------
612 // Set a user-defined duplicator for list items; by default items are not
613 // copied when the list is duplicated.
614
615 void
zlistx_set_duplicator(zlistx_t * self,zlistx_duplicator_fn duplicator)616 zlistx_set_duplicator (zlistx_t *self, zlistx_duplicator_fn duplicator)
617 {
618 assert (self);
619 self->duplicator = duplicator;
620 }
621
622
623 // --------------------------------------------------------------------------
624 // Set a user-defined comparator for zlistx_find and zlistx_sort; the method
625 // must return -1, 0, or 1 depending on whether item1 is less than, equal to,
626 // or greater than, item2.
627
628 void
zlistx_set_comparator(zlistx_t * self,zlistx_comparator_fn comparator)629 zlistx_set_comparator (zlistx_t *self, zlistx_comparator_fn comparator)
630 {
631 assert (self);
632 self->comparator = comparator;
633 }
634
635 // --------------------------------------------------------------------------
636 // Serialize list to a binary frame that can be sent in a message.
637 // The packed format is compatible with the 'strings' type implemented by zproto:
638 //
639 // ; A list of strings
640 // list = list-count *longstr
641 // list-count = number-4
642 //
643 // ; Strings are always length + text contents
644 // longstr = number-4 *VCHAR
645 //
646 // ; Numbers are unsigned integers in network byte order
647 // number-4 = 4OCTET
648 // Caller owns return value and must destroy it when done.
649 zframe_t *
zlistx_pack(zlistx_t * self)650 zlistx_pack (zlistx_t *self) {
651 assert (self);
652
653 // First, calculate the packed data size
654 size_t frame_size = 4; // List size, number-4
655 char* item = (char *) zlistx_first (self);
656
657 while (item) {
658 frame_size += 4 + strlen (item);
659 item = (char *) zlistx_next (self);
660 }
661
662 // Now serialize items into the frame
663 zframe_t *frame = zframe_new (NULL, frame_size);
664 if (!frame)
665 return NULL;
666
667 byte *needle = zframe_data (frame);
668 *(uint32_t *) needle = htonl ((u_long) self->size);
669 needle += 4;
670
671 item = (char *) zlistx_first (self);
672 while (item) {
673 size_t length = strlen (item);
674 uint32_t serialize = htonl ((u_long) length);
675 memcpy (needle, &serialize, 4);
676 needle += 4;
677 memcpy (needle, item, length);
678 needle += length;
679
680 item = (char *) zlistx_next (self);
681 }
682
683 return frame;
684 }
685
686
687 // --------------------------------------------------------------------------
688 // Unpack binary frame into a new list. Packed data must follow format
689 // defined by zlistx_pack. List is set to autofree. An empty frame
690 // unpacks to an empty list.
691 zlistx_t *
zlistx_unpack(zframe_t * frame)692 zlistx_unpack (zframe_t *frame) {
693 zlistx_t *self = zlistx_new ();
694 if (!self)
695 return NULL;
696
697 // List will free values in destructor
698 zlistx_set_destructor (self, (zlistx_destructor_fn *) zstr_free);
699
700 assert (frame);
701 if (zframe_size (frame) < 4)
702 return self;
703
704 byte *needle = zframe_data (frame);
705 byte *ceiling = needle + zframe_size (frame);
706 size_t nbr_items = ntohl (*(uint32_t *) needle);
707 needle +=4;
708 while (nbr_items && needle < ceiling) {
709 if (needle + 4 <= ceiling) {
710 size_t length = ntohl (*(uint32_t *)needle);
711 needle += 4;
712 // Be wary of malformed frames
713 if (needle + length <= ceiling) {
714 char * item = (char *) zmalloc (length + 1);
715 assert (item);
716 memcpy (item, needle, length);
717 item[length] = 0;
718 needle += length;
719
720 if (!zlistx_add_end (self, item)) {
721 zlistx_destroy (&self);
722 break;
723 }
724 } else {
725
726 zlistx_destroy (&self);
727 break;
728 }
729 } else {
730 zlistx_destroy (&self);
731 break;
732 }
733 }
734
735 if (self)
736 zlistx_set_duplicator (self, (zlistx_duplicator_fn *) strdup);
737
738 return self;
739 }
740
741
742 // --------------------------------------------------------------------------
743 // Runs selftest of class
744
745 void
zlistx_test(bool verbose)746 zlistx_test (bool verbose)
747 {
748 printf (" * zlistx: ");
749
750 // @selftest
751 zlistx_t *list = zlistx_new ();
752 assert (list);
753 assert (zlistx_size (list) == 0);
754
755 // Test operations on an empty list
756 assert (zlistx_head (list) == NULL);
757 assert (zlistx_first (list) == NULL);
758 assert (zlistx_last (list) == NULL);
759 assert (zlistx_next (list) == NULL);
760 assert (zlistx_prev (list) == NULL);
761 assert (zlistx_find (list, "hello") == NULL);
762 assert (zlistx_delete (list, NULL) == -1);
763 assert (zlistx_detach (list, NULL) == NULL);
764 assert (zlistx_delete (list, NULL) == -1);
765 assert (zlistx_detach (list, NULL) == NULL);
766 zlistx_purge (list);
767 zlistx_sort (list);
768
769 // Use item handlers
770 zlistx_set_destructor (list, (zlistx_destructor_fn *) zstr_free);
771 zlistx_set_duplicator (list, (zlistx_duplicator_fn *) strdup);
772 zlistx_set_comparator (list, (zlistx_comparator_fn *) strcmp);
773
774 // Try simple insert/sort/delete/next
775 assert (zlistx_next (list) == NULL);
776 zlistx_add_end (list, "world");
777 assert (streq ((char *) zlistx_next (list), "world"));
778 assert (streq ((char *) zlistx_head (list), "world"));
779 zlistx_add_end (list, "hello");
780 assert (streq ((char *) zlistx_prev (list), "hello"));
781 zlistx_sort (list);
782 assert (zlistx_size (list) == 2);
783 void *handle = zlistx_find (list, "hello");
784 char *item1 = (char *) zlistx_item (list);
785 char *item2 = (char *) zlistx_handle_item (handle);
786 assert (item1 == item2);
787 assert (streq (item1, "hello"));
788 zlistx_delete (list, handle);
789 assert (zlistx_size (list) == 1);
790 char *string = (char *) zlistx_detach (list, NULL);
791 assert (streq (string, "world"));
792 freen (string);
793 assert (zlistx_size (list) == 0);
794
795 // Check next/back work
796 // Now populate the list with items
797 zlistx_add_start (list, "five");
798 zlistx_add_end (list, "six");
799 zlistx_add_start (list, "four");
800 zlistx_add_end (list, "seven");
801 zlistx_add_start (list, "three");
802 zlistx_add_end (list, "eight");
803 zlistx_add_start (list, "two");
804 zlistx_add_end (list, "nine");
805 zlistx_add_start (list, "one");
806 zlistx_add_end (list, "ten");
807
808 // Test our navigation skills
809 assert (zlistx_size (list) == 10);
810 assert (streq ((char *) zlistx_last (list), "ten"));
811 assert (streq ((char *) zlistx_prev (list), "nine"));
812 assert (streq ((char *) zlistx_prev (list), "eight"));
813 assert (streq ((char *) zlistx_prev (list), "seven"));
814 assert (streq ((char *) zlistx_prev (list), "six"));
815 assert (streq ((char *) zlistx_prev (list), "five"));
816 assert (streq ((char *) zlistx_first (list), "one"));
817 assert (streq ((char *) zlistx_next (list), "two"));
818 assert (streq ((char *) zlistx_next (list), "three"));
819 assert (streq ((char *) zlistx_next (list), "four"));
820
821 // Sort by alphabetical order
822 zlistx_sort (list);
823 assert (streq ((char *) zlistx_first (list), "eight"));
824 assert (streq ((char *) zlistx_last (list), "two"));
825
826 // Moving items around
827 handle = zlistx_find (list, "six");
828 zlistx_move_start (list, handle);
829 assert (streq ((char *) zlistx_first (list), "six"));
830 zlistx_move_end (list, handle);
831 assert (streq ((char *) zlistx_last (list), "six"));
832 zlistx_sort (list);
833 assert (streq ((char *) zlistx_last (list), "two"));
834
835 // Copying a list
836 zlistx_t *copy = zlistx_dup (list);
837 assert (copy);
838 assert (zlistx_size (copy) == 10);
839 assert (streq ((char *) zlistx_first (copy), "eight"));
840 assert (streq ((char *) zlistx_last (copy), "two"));
841 zlistx_destroy (©);
842
843 // Delete items while iterating
844 string = (char *) zlistx_first (list);
845 assert (streq (string, "eight"));
846 string = (char *) zlistx_next (list);
847 assert (streq (string, "five"));
848 zlistx_delete (list, zlistx_cursor (list));
849 string = (char *) zlistx_next (list);
850 assert (streq (string, "four"));
851
852 // Test pack/unpack methods
853 zframe_t *frame = zlistx_pack (list);
854 copy = zlistx_unpack (frame);
855 assert (copy);
856 zframe_destroy (&frame);
857 assert (zlistx_size (copy) == zlistx_size (list));
858
859 char *item_orig = (char *) zlistx_first (list);
860 char *item_copy = (char *) zlistx_first (copy);
861 while (item_orig) {
862 assert (strcmp(item_orig, item_copy) == 0);
863 item_orig = (char *) zlistx_next (list);
864 item_copy = (char *) zlistx_next (copy);
865 }
866
867 zlistx_destroy (©);
868
869 zlistx_purge (list);
870 zlistx_destroy (&list);
871
872 #if defined (__WINDOWS__)
873 zsys_shutdown();
874 #endif
875 // @end
876
877 printf ("OK\n");
878 }
879