Merge lp:~jamesodhunt/libnih/iterators into lp:libnih/1.0

Proposed by James Hunt
Status: Needs review
Proposed branch: lp:~jamesodhunt/libnih/iterators
Merge into: lp:libnih/1.0
Diff against target: 952 lines (+738/-8)
11 files modified
ChangeLog (+10/-0)
nih/hash.c (+62/-0)
nih/hash.h (+7/-0)
nih/list.c (+59/-0)
nih/list.h (+18/-0)
nih/tests/test_hash.c (+176/-2)
nih/tests/test_list.c (+154/-3)
nih/tests/test_tree.c (+170/-1)
nih/tree.c (+62/-0)
nih/tree.h (+18/-0)
po/libnih.pot (+2/-2)
To merge this branch: bzr merge lp:~jamesodhunt/libnih/iterators
Reviewer Review Type Date Requested Status
Scott James Remnant (Canonical) Pending
Review via email: mp+60308@code.launchpad.net

Description of the change

* New functions:
  - nih_list_foreach()
  - nih_hash_foreach()
  - nih_tree_foreach()
  - nih_list_count()
  - nih_hash_count()
  - nih_tree_count()

These functions will be marginally slower to run than the corresponding NIH_LIST_FOREACH(), NIH_HASH_FOREACH() and NIH_TREE_FOREACH_*() macros, but have the advantage of being (more easily) callable from within a debugger. Additionally, trivial handlers can be created by apps using nih which also simplifies debug.

For example, we could change Upstart to include functions like this:

init/conf.c:
#ifdef DEBUG
  int conf_sources_handler (ConfSource *source, void *data)
      __attribute__((unused));

  int
  conf_sources_handler (ConfSource *source, void *data)
  {
    nih_assert (source);
    return TRUE;
  }
#endif

This minimal handler is enough such that from within gdb, we can then say:

(gdb) b conf_sources_handler
(gdb) call nih_list_foreach (conf_sources, NULL, (NihListHandler)conf_sources_handler, NULL)
(gdb) r

And then when the breakpoint is hit...

(gdb) p source

This avoids cluttering our applications source code with horrid hard-coded functions to print out data structures - we can rely on gdb to do that for us.

To post a comment you must log in.

Unmerged revisions

1049. By James Hunt

* Changelog: Summary of new functions.
* nih/hash.c: New functions:
  - nih_hash_foreach()
  - nih_hash_count()
* nih/hash.h: Prototypes for new functions:
  - nih_hash_foreach()
  - nih_hash_count()
* nih/list.c: New functions:
  - nih_list_foreach()
  - nih_list_count()
* nih/list.h:
  - Prototypes for new functions:
    - nih_list_foreach()
    - nih_list_count()
  - New type: NihListHandler.
* nih/tree.c: New functions:
  - nih_tree_foreach()
  - nih_tree_count()
* nih/tree.h:
  - Prototypes for new functions:
    - nih_tree_foreach()
    - nih_tree_count()
  - New type: NihTreeHandler.
* nih/tests/test_hash.c:
  - New functions:
    - test_count()
    - test_foreach_func () and supporting functions:
      - hash_handler1()
      - hash_handler2()
      - hash_handler3()
  - Updated main to call test_foreach_func() and test_count().
  - Changed description for existing functions to make it clear that
    NIH_HASH_FOREACH() is a macro.
* nih/tests/test_list.c:
  - New functions:
    - test_count()
    - test_foreach_func() and supporting functions:
      - list_handler1()
      - list_handler2()
      - list_handler3()
  - Updated main to call test_foreach_func() and test_count().
  - Changed description for existing functions to make it clear that
    NIH_LIST_*() are macros.
* nih/tests/test_tree.c
  - New functions:
    - test_count()
    - test_foreach_func () and supporting functions:
      - tree_handler1()
      - tree_handler2()
      - tree_handler3()
  - Updated main to call test_foreach_func() and test_count().
  - Changed description for existing functions to make it clear that
    NIH_TREE_FOREACH_*() are macros.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2010-12-23 22:06:15 +0000
3+++ ChangeLog 2011-05-08 11:56:38 +0000
4@@ -1,3 +1,13 @@
5+2011-05-08 James Hunt <james.hunt@ubuntu.com>
6+
7+ * New functions:
8+ - nih_list_foreach()
9+ - nih_hash_foreach()
10+ - nih_tree_foreach()
11+ - nih_list_count()
12+ - nih_hash_count()
13+ - nih_tree_count()
14+
15 2010-12-23 Scott James Remnant <scott@netsplit.com>
16
17 * configure.ac: Bump version to 1.0.4
18
19=== modified file 'nih/hash.c'
20--- nih/hash.c 2009-06-23 09:29:37 +0000
21+++ nih/hash.c 2011-05-08 11:56:38 +0000
22@@ -397,3 +397,65 @@
23
24 return strcmp (key1, key2);
25 }
26+
27+/**
28+ * nih_hash_foreach:
29+ *
30+ * @hash: hash,
31+ * @len: optional output parameter that will contain count of hash entries,
32+ * @handler: optional function called for each hash entry,
33+ * @data: optional data to pass to handler along with hash entry.
34+ *
35+ * Iterate over specified hash.
36+ *
37+ * One of @len or @handler may be NULL.
38+ * If @handler is NULL, count of hash entries will still be returned in @len.
39+ * If @handler returns 1, @len will be set to the number of hash entries
40+ * processed successfully up to that point.
41+ *
42+ * Returns 0 on success (and when both @len and @handler are NULL),
43+ * or -1 if handler returns an error.
44+ **/
45+int
46+nih_hash_foreach (const NihHash *hash, size_t *len,
47+ NihListHandler handler, void *data)
48+{
49+ int ret;
50+
51+ nih_assert (hash);
52+
53+ if (len) *len = 0;
54+
55+ if (!len && !handler) return 0;
56+
57+ NIH_HASH_FOREACH (hash, iter) {
58+ if (handler) {
59+ ret = handler (iter, data);
60+ if (ret == FALSE) return -1;
61+ }
62+ if (len) ++*len;
63+ }
64+
65+ return 0;
66+}
67+
68+/**
69+ * nih_hash_count:
70+ *
71+ * @hash: hash.
72+ *
73+ * Returns count of number of entries in @hash.
74+ **/
75+size_t
76+nih_hash_count (const NihHash *hash)
77+{
78+ size_t len = 0;
79+ int ret;
80+
81+ nih_assert (hash);
82+
83+ ret = nih_hash_foreach (hash, &len, NULL, NULL);
84+
85+ return (ret == -1 ? 0 : len);
86+}
87+
88
89=== modified file 'nih/hash.h'
90--- nih/hash.h 2011-01-06 09:28:39 +0000
91+++ nih/hash.h 2011-05-08 11:56:38 +0000
92@@ -217,6 +217,13 @@
93 uint32_t nih_hash_string_hash (const char *key);
94 int nih_hash_string_cmp (const char *key1, const char *key2);
95
96+int nih_hash_foreach (const NihHash *hash, size_t *len,
97+ NihListHandler handler, void *data)
98+ __attribute__((unused));
99+
100+size_t nih_hash_count (const NihHash *hash)
101+ __attribute__((warn_unused_result, unused));
102+
103 NIH_END_EXTERN
104
105 #endif /* NIH_HASH_H */
106
107=== modified file 'nih/list.c'
108--- nih/list.c 2009-06-23 09:29:37 +0000
109+++ nih/list.c 2011-05-08 11:56:38 +0000
110@@ -249,3 +249,62 @@
111
112 return 0;
113 }
114+
115+/**
116+ * nih_list_foreach:
117+ * @list: list,
118+ * @len: optional output parameter that will contain length of list,
119+ * @handler: optional function called for each list entry,
120+ * @data: optional data to pass to handler along with list entry.
121+ *
122+ * Iterate over specified list.
123+ *
124+ * One of @len or @handler may be NULL.
125+ * If @handler is NULL, list length will still be returned in @len.
126+ * If @handler returns 1, @len will be set to the number of list entries
127+ * processed successfully up to that point.
128+ *
129+ * Returns 0 on success (and when both @len and @handler are NULL),
130+ * or -1 if handler returns an error.
131+ **/
132+int
133+nih_list_foreach (const NihList *list, size_t *len,
134+ NihListHandler handler, void *data)
135+{
136+ int ret;
137+
138+ nih_assert (list);
139+
140+ if (len) *len = 0;
141+
142+ if (!len && !handler) return 0;
143+
144+ NIH_LIST_FOREACH (list, iter) {
145+ if (handler) {
146+ ret = handler (iter, data);
147+ if (ret == FALSE) return -1;
148+ }
149+ if (len) ++*len;
150+ }
151+
152+ return 0;
153+}
154+
155+/**
156+ * nih_list_count:
157+ * @list: list.
158+ *
159+ * Returns count of number of entries in @list.
160+ **/
161+size_t
162+nih_list_count (const NihList *list)
163+{
164+ size_t len = 0;
165+ int ret;
166+
167+ nih_assert (list);
168+
169+ ret = nih_list_foreach (list, &len, NULL, NULL);
170+
171+ return (ret == -1 ? 0 : len);
172+}
173
174=== modified file 'nih/list.h'
175--- nih/list.h 2011-01-06 09:28:39 +0000
176+++ nih/list.h 2011-05-08 11:56:38 +0000
177@@ -110,6 +110,17 @@
178 };
179 } NihListEntry;
180
181+/**
182+ * NihListHandler:
183+ * @entry: list entry being visited,
184+ * @data: data pointer.
185+ *
186+ * A list handler is a function called for each list entry
187+ * when iterating over a list.
188+ *
189+ * Returns: TRUE if list entry process correctly, else FALSE.
190+ **/
191+typedef int (*NihListHandler) (NihList *entry, void *data);
192
193 /**
194 * NIH_LIST_EMPTY:
195@@ -208,6 +219,13 @@
196 NihList * nih_list_remove (NihList *entry);
197 int nih_list_destroy (NihList *entry);
198
199+int nih_list_foreach (const NihList *list, size_t *len,
200+ NihListHandler handler, void *data)
201+ __attribute__((unused));
202+
203+size_t nih_list_count (const NihList *list)
204+ __attribute__((warn_unused_result, unused));
205+
206 NIH_END_EXTERN
207
208 #endif /* NIH_LIST_H */
209
210=== modified file 'nih/tests/test_hash.c'
211--- nih/tests/test_hash.c 2009-06-23 09:29:37 +0000
212+++ nih/tests/test_hash.c 2011-05-08 11:56:38 +0000
213@@ -25,6 +25,11 @@
214 #include <nih/alloc.h>
215 #include <nih/list.h>
216 #include <nih/hash.h>
217+#include <nih/string.h>
218+
219+#define HASH_HANDLER_DATA_VALUE 42
220+#define LIST_ENTRIES 4
221+#define ERROR_ON_ENTRY 3
222
223
224 typedef struct hash_entry {
225@@ -514,7 +519,7 @@
226 * in the hash in the order we expect them to come out in, but add
227 * them in a different order for sanity.
228 */
229- TEST_FUNCTION ("NIH_HASH_FOREACH");
230+ TEST_FUNCTION ("NIH_HASH_FOREACH macro");
231 hash = nih_hash_string_new (NULL, 0);
232 entry0 = entry[2] = new_entry (hash, "entry 1");
233 entry1 = entry[1] = new_entry (hash, "entry 2");
234@@ -542,6 +547,173 @@
235 nih_free (hash);
236 }
237
238+int
239+hash_handler1 (NihList *entry, int *data)
240+{
241+ TEST_NE_P (entry, NULL);
242+ TEST_NE_P (data, NULL);
243+
244+ TEST_EQ (*data, HASH_HANDLER_DATA_VALUE);
245+
246+ return TRUE;
247+}
248+
249+int
250+hash_handler2 (NihList *entry, int *data)
251+{
252+ static size_t i = 0;
253+
254+ TEST_NE_P (entry, NULL);
255+ TEST_NE_P (data, NULL);
256+
257+ ++i;
258+ *data = i;
259+
260+ return TRUE;
261+}
262+
263+int
264+hash_handler3 (NihList *entry, void *data)
265+{
266+ static size_t i = 0;
267+
268+ TEST_NE_P (entry, NULL);
269+ TEST_NE_P (data, NULL);
270+
271+ ++i;
272+ *(int *)data = i;
273+
274+ if (i == ERROR_ON_ENTRY)
275+ return FALSE;
276+
277+ return TRUE;
278+}
279+
280+void
281+test_foreach_func (void)
282+{
283+ NihHash *hash;
284+ NihList *entry[LIST_ENTRIES];
285+ NihList *entry0, *entry1, *entry2, *entry3;
286+ size_t len = 0;
287+ int data1 = HASH_HANDLER_DATA_VALUE;
288+ size_t data2 = 0;
289+ int ret;
290+
291+ TEST_FUNCTION ("nih_hash_foreach");
292+
293+ hash = nih_hash_string_new (NULL, 0);
294+ TEST_NE_P (hash, NULL);
295+
296+
297+ TEST_FEATURE ("with no handler, length or data params");
298+ TEST_EQ (nih_hash_foreach (hash, NULL, NULL, NULL), 0);
299+
300+
301+ TEST_FEATURE ("with length param, but no handler");
302+
303+ ret = nih_hash_foreach (hash, &len, NULL, NULL);
304+ TEST_EQ (ret, 0);
305+ TEST_EQ (len, 0);
306+
307+
308+ TEST_FEATURE ("with data and handler reading a value, but no len");
309+
310+ entry0 = entry[0] = new_entry (hash, "entry 1");
311+ entry1 = entry[1] = new_entry (hash, "entry 2");
312+ entry2 = entry[2] = new_entry (hash, "entry 3");
313+ entry3 = entry[3] = new_entry (hash, "entry 4");
314+
315+ nih_hash_add (hash, entry0);
316+ nih_hash_add (hash, entry1);
317+ nih_hash_add (hash, entry2);
318+ nih_hash_add (hash, entry3);
319+
320+ ret = nih_hash_foreach (hash, NULL,
321+ (NihListHandler)hash_handler1, &data1);
322+
323+ TEST_EQ (ret, 0);
324+ TEST_EQ (data1, HASH_HANDLER_DATA_VALUE);
325+
326+
327+ TEST_FEATURE ("with len, data and handler reading a value");
328+
329+ ret = nih_hash_foreach (hash, &len,
330+ (NihListHandler)hash_handler1, &data1);
331+
332+ TEST_EQ (ret, 0);
333+ TEST_EQ (data1, HASH_HANDLER_DATA_VALUE);
334+ TEST_EQ (len, LIST_ENTRIES);
335+
336+
337+ TEST_FEATURE ("with len, data and hander changing a value");
338+
339+ len = 0;
340+ data2 = 0;
341+ ret = nih_hash_foreach (hash, &len,
342+ (NihListHandler)hash_handler2, &data2);
343+
344+ TEST_EQ (ret, 0);
345+ TEST_EQ (len, LIST_ENTRIES);
346+ TEST_EQ (data2, LIST_ENTRIES);
347+
348+
349+ TEST_FEATURE ("with handler returning error");
350+
351+ len = 0;
352+ data2 = 0;
353+ ret = nih_hash_foreach (hash, &len,
354+ (NihListHandler)hash_handler3, &data2);
355+ TEST_EQ (ret, -1);
356+ TEST_EQ (len, ERROR_ON_ENTRY-1);
357+}
358+
359+void
360+test_count (void)
361+{
362+ NihHash *hash;
363+ NihList *entry[LIST_ENTRIES];
364+ nih_local char *key;
365+ NihList *e;
366+ size_t i;
367+
368+ TEST_FUNCTION ("nih_hash_count");
369+
370+ hash = nih_hash_string_new (NULL, 0);
371+ TEST_NE_P (hash, NULL);
372+
373+ i = 0;
374+ TEST_FEATURE ("with no initial entries");
375+ TEST_EQ (nih_hash_count (hash), i);
376+
377+ entry[0] = new_entry (hash, "entry 1");
378+ entry[1] = new_entry (hash, "entry 2");
379+ entry[2] = new_entry (hash, "entry 3");
380+ entry[3] = new_entry (hash, "entry 4");
381+
382+ TEST_FEATURE ("with increasing number of entries");
383+
384+ for (i=0; i < LIST_ENTRIES; ++i) {
385+ nih_hash_add (hash, entry[i]);
386+ TEST_EQ (nih_hash_count (hash), i+1);
387+ }
388+
389+ TEST_FEATURE ("with decreasing number of entries");
390+
391+ for (i=LIST_ENTRIES; i > 0; --i) {
392+ key = nih_sprintf (NULL, "entry %d", i);
393+ TEST_NE_P (key, NULL);
394+
395+ e = nih_hash_lookup (hash, key);
396+ TEST_NE_P (e, NULL);
397+ nih_free (nih_list_remove (e));
398+ TEST_EQ (nih_hash_count (hash), i-1);
399+ }
400+
401+ TEST_FEATURE ("with no entries");
402+ TEST_EQ (nih_hash_count (hash), 0);
403+}
404+
405 void
406 test_foreach_safe (void)
407 {
408@@ -553,7 +725,7 @@
409 * order, visiting each entry in each bin; and that it's safe to
410 * remove the entries while doing so.
411 */
412- TEST_FUNCTION ("NIH_HASH_FOREACH_SAFE");
413+ TEST_FUNCTION ("NIH_HASH_FOREACH_SAFE macro");
414 hash = nih_hash_string_new (NULL, 0);
415 entry0 = entry[2] = new_entry (hash, "entry 1");
416 entry1 = entry[1] = new_entry (hash, "entry 2");
417@@ -619,6 +791,8 @@
418 test_lookup ();
419 test_foreach ();
420 test_foreach_safe ();
421+ test_foreach_func ();
422+ test_count ();
423 test_string_key ();
424
425 return 0;
426
427=== modified file 'nih/tests/test_list.c'
428--- nih/tests/test_list.c 2009-06-23 09:29:37 +0000
429+++ nih/tests/test_list.c 2011-05-08 11:56:38 +0000
430@@ -25,6 +25,9 @@
431 #include <nih/alloc.h>
432 #include <nih/list.h>
433
434+#define LIST_HANDLER_DATA_VALUE 42
435+#define LIST_ENTRIES 3
436+#define ERROR_ON_ENTRY 2
437
438 void
439 test_init (void)
440@@ -251,7 +254,7 @@
441 {
442 NihList *list, *entry;
443
444- TEST_FUNCTION ("NIH_LIST_EMPTY");
445+ TEST_FUNCTION ("NIH_LIST_EMPTY macro");
446
447 /* Check that NIH_LIST_EMPTY is TRUE on an empty list */
448 TEST_FEATURE ("with empty list");
449@@ -281,7 +284,7 @@
450 /* Check that NIH_LIST_FOREACH iterates the list correctly in
451 * order, visiting each entry.
452 */
453- TEST_FUNCTION ("NIH_LIST_FOREACH");
454+ TEST_FUNCTION ("NIH_LIST_FOREACH macro");
455 list = nih_list_new (NULL);
456 entry[0] = nih_list_add (list, nih_list_new (NULL));
457 entry[1] = nih_list_add (list, nih_list_new (NULL));
458@@ -306,13 +309,159 @@
459 nih_free (entry[2]);
460 }
461
462+int
463+list_handler1 (NihList *entry, int *data)
464+{
465+ TEST_NE_P (entry, NULL);
466+ TEST_NE_P (data, NULL);
467+
468+ TEST_EQ (*data, LIST_HANDLER_DATA_VALUE);
469+
470+ return TRUE;
471+}
472+
473+int
474+list_handler2 (NihList *entry, int *data)
475+{
476+ static size_t i = 0;
477+
478+ TEST_NE_P (entry, NULL);
479+ TEST_NE_P (data, NULL);
480+
481+ ++i;
482+ *data = i;
483+
484+ return TRUE;
485+}
486+
487+int
488+list_handler3 (NihList *entry, void *data)
489+{
490+ static size_t i = 0;
491+
492+ TEST_NE_P (entry, NULL);
493+ TEST_NE_P (data, NULL);
494+
495+ ++i;
496+ *(int *)data = i;
497+
498+ if (i == ERROR_ON_ENTRY)
499+ return FALSE;
500+
501+ return TRUE;
502+}
503+
504+void
505+test_foreach_func (void)
506+{
507+ NihList *list, *entry[LIST_ENTRIES];
508+ size_t len = 0;
509+ int data1 = LIST_HANDLER_DATA_VALUE;
510+ size_t data2 = 0;
511+ int ret;
512+
513+ TEST_FUNCTION ("nih_list_foreach");
514+
515+ list = nih_list_new (NULL);
516+ TEST_NE_P (list, NULL);
517+
518+
519+ TEST_FEATURE ("with no handler, length or data params");
520+
521+ ret = nih_list_foreach (list, NULL, NULL, NULL);
522+ TEST_EQ (ret, 0);
523+
524+ TEST_FEATURE ("with length param, but no handler");
525+ ret = nih_list_foreach (list, &len, NULL, NULL);
526+ TEST_EQ (ret, 0);
527+ TEST_EQ (len, 0);
528+
529+
530+ TEST_FEATURE ("with data and handler reading a value, but no len");
531+
532+ entry[0] = nih_list_add (list, nih_list_new (NULL));
533+ entry[1] = nih_list_add (list, nih_list_new (NULL));
534+ entry[2] = nih_list_add (list, nih_list_new (NULL));
535+
536+ ret = nih_list_foreach (list, NULL,
537+ (NihListHandler)list_handler1, &data1);
538+
539+ TEST_EQ (ret, 0);
540+ TEST_EQ (data1, LIST_HANDLER_DATA_VALUE);
541+
542+
543+ TEST_FEATURE ("with len, data and handler reading a value");
544+
545+ ret = nih_list_foreach (list, &len,
546+ (NihListHandler)list_handler1, &data1);
547+
548+ TEST_EQ (ret, 0);
549+ TEST_EQ (len, LIST_ENTRIES);
550+ TEST_EQ (data1, LIST_HANDLER_DATA_VALUE);
551+
552+
553+ TEST_FEATURE ("with len, data and hander changing a value");
554+
555+ len = 0;
556+ data2 = 0;
557+ ret = nih_list_foreach (list, &len,
558+ (NihListHandler)list_handler2, &data2);
559+ TEST_EQ (ret, 0);
560+ TEST_EQ (len, LIST_ENTRIES);
561+ TEST_EQ (data2, LIST_ENTRIES);
562+
563+
564+ TEST_FEATURE ("with handler returning error");
565+
566+ len = 0;
567+ data2 = 0;
568+ ret = nih_list_foreach (list, &len,
569+ (NihListHandler)list_handler3, &data2);
570+ TEST_EQ (ret, -1);
571+ TEST_EQ (len, ERROR_ON_ENTRY-1);
572+}
573+
574+void
575+test_count (void)
576+{
577+ NihList *list, *entry[3];
578+ size_t i;
579+
580+ TEST_FUNCTION ("nih_list_count");
581+
582+ list = nih_list_new (NULL);
583+ TEST_NE_P (list, NULL);
584+
585+ TEST_FEATURE ("with no initial entries");
586+ TEST_EQ (nih_list_count (list), 0);
587+ TEST_EQ (NIH_LIST_EMPTY (list), TRUE);
588+
589+ TEST_FEATURE ("with increasing number of entries");
590+ for (i=0; i < LIST_ENTRIES; ++i) {
591+ entry[i] = nih_list_add (list, nih_list_new (NULL));
592+ TEST_EQ (nih_list_count (list), i+1);
593+ }
594+
595+ TEST_FEATURE ("with decreasing number of entries");
596+
597+ for (i=LIST_ENTRIES; i > 0; --i) {
598+ nih_free (nih_list_remove (list->next));
599+ TEST_EQ (nih_list_count (list), i-1);
600+ }
601+
602+ TEST_FEATURE ("with no entries");
603+ TEST_EQ (nih_list_count (list), 0);
604+ TEST_EQ (NIH_LIST_EMPTY (list), TRUE);
605+
606+}
607+
608 void
609 test_foreach_safe (void)
610 {
611 NihList *list, *entry[3];
612 int i;
613
614- TEST_FUNCTION ("NIH_LIST_FOREACH_SAFE");
615+ TEST_FUNCTION ("NIH_LIST_FOREACH_SAFE macro");
616
617 /* Check that NIH_LIST_FOREACH_SAFE iterates the list correctly in
618 * order, visiting each entry.
619@@ -503,6 +652,8 @@
620 test_empty ();
621 test_foreach ();
622 test_foreach_safe ();
623+ test_foreach_func ();
624+ test_count ();
625 test_remove ();
626 test_destroy ();
627
628
629=== modified file 'nih/tests/test_tree.c'
630--- nih/tests/test_tree.c 2009-06-23 09:29:37 +0000
631+++ nih/tests/test_tree.c 2011-05-08 11:56:38 +0000
632@@ -25,6 +25,9 @@
633 #include <nih/alloc.h>
634 #include <nih/tree.h>
635
636+#define TREE_HANDLER_DATA_VALUE 42
637+#define NODE_COUNT 4
638+#define ERROR_ON_NODE 3
639
640 void
641 test_init (void)
642@@ -508,7 +511,7 @@
643 NihTree *node[12], *expect[13];
644 int i;
645
646- TEST_FUNCTION ("NIH_TREE_FOREACH");
647+ TEST_FUNCTION ("NIH_TREE_FOREACH macro");
648 for (i = 0; i < 12; i++)
649 node[i] = nih_tree_new (NULL);
650
651@@ -589,6 +592,170 @@
652 nih_free (node[i]);
653 }
654
655+int
656+tree_handler1 (NihTree *node, void *data)
657+{
658+ TEST_NE_P (node, NULL);
659+ TEST_NE_P (data, NULL);
660+
661+ TEST_EQ (*(int *)data, TREE_HANDLER_DATA_VALUE);
662+
663+ return TRUE;
664+}
665+
666+int
667+tree_handler2 (NihTree *node, void *data)
668+{
669+ static size_t count = 0;
670+
671+ TEST_NE_P (node, NULL);
672+ TEST_NE_P (data, NULL);
673+
674+ ++count;
675+ *(int *)data = count;
676+
677+ return TRUE;
678+}
679+
680+int
681+tree_handler3 (NihTree *node, void *data)
682+{
683+ static size_t i = 0;
684+
685+ TEST_NE_P (node, NULL);
686+ TEST_NE_P (data, NULL);
687+
688+ ++i;
689+ *(int *)data = i;
690+
691+ if (i == ERROR_ON_NODE)
692+ return FALSE;
693+
694+ return TRUE;
695+}
696+
697+void
698+test_foreach_func (void)
699+{
700+ NihTree *tree, *node1, *node2, *node3;
701+ int data1 = TREE_HANDLER_DATA_VALUE;
702+ size_t data2 = 0;
703+ int ret;
704+ size_t len;
705+
706+ TEST_FUNCTION ("nih_tree_foreach function");
707+
708+ tree = nih_tree_new (NULL);
709+
710+
711+ TEST_FEATURE ("with no handler, length or data params");
712+ TEST_EQ (nih_tree_foreach (tree, NULL, NULL, NULL), 0);
713+
714+
715+ TEST_FEATURE ("with length param, but no handler");
716+
717+ ret = nih_tree_foreach (tree, &len, NULL, NULL);
718+ TEST_EQ (ret, 0);
719+ TEST_EQ (len, 1);
720+
721+ node1 = nih_tree_new (tree);
722+ node2 = nih_tree_new (tree);
723+ node3 = nih_tree_new (tree);
724+
725+ nih_tree_add (tree, node1, NIH_TREE_LEFT);
726+ ret = nih_tree_foreach (tree, &len, NULL, NULL);
727+ TEST_EQ (ret, 0);
728+ TEST_EQ (len, 2);
729+
730+ nih_tree_add (tree, node2, NIH_TREE_RIGHT);
731+ ret = nih_tree_foreach (tree, &len, NULL, NULL);
732+ TEST_EQ (ret, 0);
733+ TEST_EQ (len, 3);
734+
735+ nih_tree_add (node1, node3, NIH_TREE_RIGHT);
736+
737+ ret = nih_tree_foreach (tree, &len, NULL, NULL);
738+ TEST_EQ (ret, 0);
739+ TEST_EQ (len, 4);
740+
741+
742+ TEST_FEATURE ("with data and handler reading a value, but no len");
743+
744+ ret = nih_tree_foreach (tree, NULL,
745+ (NihTreeHandler)tree_handler1, &data1);
746+
747+ TEST_EQ (ret, 0);
748+ TEST_EQ (data1, TREE_HANDLER_DATA_VALUE);
749+
750+ TEST_FEATURE ("with len, data and handler reading a value");
751+
752+ ret = nih_tree_foreach (tree, &len,
753+ (NihTreeHandler)tree_handler1, &data1);
754+
755+ TEST_EQ (ret, 0);
756+ TEST_EQ (data1, TREE_HANDLER_DATA_VALUE);
757+ TEST_EQ (len, NODE_COUNT);
758+
759+ TEST_FEATURE ("with len, data and handler changing a value");
760+
761+ data2 = 0;
762+ ret = nih_tree_foreach (tree, &len,
763+ (NihTreeHandler)tree_handler2, &data2);
764+
765+ TEST_EQ (ret, 0);
766+ TEST_EQ (len, NODE_COUNT);
767+ TEST_EQ (data2, NODE_COUNT);
768+
769+ TEST_FEATURE ("with handler returning error");
770+
771+ len = 0;
772+ data2 = 0;
773+ ret = nih_tree_foreach (tree, &len,
774+ (NihTreeHandler)tree_handler3, &data2);
775+
776+ TEST_EQ (ret, -1);
777+ TEST_EQ (len, ERROR_ON_NODE-1);
778+}
779+
780+void
781+test_count (void)
782+{
783+ NihTree *tree, *node1, *node2, *node3;
784+
785+ TEST_FUNCTION ("nih_tree_count");
786+
787+ tree = nih_tree_new (NULL);
788+ node1 = nih_tree_new (tree);
789+ node2 = nih_tree_new (tree);
790+ node3 = nih_tree_new (tree);
791+
792+ TEST_FEATURE ("with one initial entry");
793+ TEST_EQ (nih_tree_count (tree), 1);
794+
795+ TEST_FEATURE ("with increasing number of entries");
796+
797+ nih_tree_add (tree, node1, NIH_TREE_LEFT);
798+ TEST_EQ (nih_tree_count (tree), 2);
799+
800+ nih_tree_add (tree, node2, NIH_TREE_RIGHT);
801+ TEST_EQ (nih_tree_count (tree), 3);
802+
803+ nih_tree_add (node1, node3, NIH_TREE_RIGHT);
804+ TEST_EQ (nih_tree_count (tree), 4);
805+
806+ TEST_FEATURE ("with decreasing number of entries");
807+ nih_tree_remove (node3);
808+ TEST_EQ (nih_tree_count (tree), 3);
809+
810+ nih_tree_remove (node2);
811+ TEST_EQ (nih_tree_count (tree), 2);
812+
813+ nih_tree_remove (node1);
814+
815+ TEST_FEATURE ("with one final entry");
816+ TEST_EQ (nih_tree_count (tree), 1);
817+}
818+
819 void
820 test_prev (void)
821 {
822@@ -2214,6 +2381,8 @@
823 test_destroy ();
824 test_next ();
825 test_foreach ();
826+ test_foreach_func ();
827+ test_count ();
828 test_prev ();
829 test_next_pre ();
830 test_foreach_pre ();
831
832=== modified file 'nih/tree.c'
833--- nih/tree.c 2009-06-23 09:29:37 +0000
834+++ nih/tree.c 2011-05-08 11:56:38 +0000
835@@ -619,3 +619,65 @@
836 prev = tmp;
837 }
838 }
839+
840+/**
841+ * nih_tree_foreach:
842+ *
843+ * @tree: tree,
844+ * @len: optional output parameter that will contain count of tree nodes,
845+ * @handler: optional function called for each tree node,
846+ * @data: optional data to pass to handler along with tree node.
847+ *
848+ * Iterate over specified tree.
849+ *
850+ * One of @len or @handler may be NULL.
851+ * If @handler is NULL and @len is non-NULL, count of tree nodes will
852+ * still be returned in @len.
853+ * If @handler returns 1, @len will be set to the number of tree nodes
854+ * processed successfully up to that point.
855+ *
856+ * Returns 0 on success (and when both @len and @handler are NULL),
857+ * or -1 if handler returns an error.
858+ **/
859+int
860+nih_tree_foreach (NihTree *tree, size_t *len,
861+ NihTreeHandler handler, void *data)
862+{
863+ int ret;
864+
865+ nih_assert (tree);
866+
867+ if (len) *len = 0;
868+
869+ if (!len && !handler) return 0;
870+
871+ NIH_TREE_FOREACH_FULL (tree, iter, NULL, data) {
872+ if (handler) {
873+ ret = handler (iter, data);
874+ if (!ret) return -1;
875+ }
876+ if (len) ++*len;
877+ }
878+
879+ return 0;
880+}
881+
882+/**
883+ * nih_tree_count:
884+ *
885+ * @tree: tree.
886+ *
887+ * Returns count of number of entries in @tree.
888+ **/
889+size_t
890+nih_tree_count (NihTree *tree)
891+{
892+ size_t len = 0;
893+ int ret;
894+
895+ nih_assert (tree);
896+
897+ ret = nih_tree_foreach (tree, &len, NULL, NULL);
898+
899+ return (ret == -1 ? 0 : len);
900+}
901
902=== modified file 'nih/tree.h'
903--- nih/tree.h 2009-06-23 09:29:37 +0000
904+++ nih/tree.h 2011-05-08 11:56:38 +0000
905@@ -138,6 +138,17 @@
906 **/
907 typedef int (*NihTreeFilter) (void *data, NihTree *node);
908
909+/**
910+ * NihTreeHandler:
911+ * @node: tree entry being visited,
912+ * @data: data pointer.
913+ *
914+ * A tree handler is a function called for each tree node
915+ * when iterating over a tree.
916+ *
917+ * Returns: TRUE if tree entry process correctly, else FALSE.
918+ **/
919+typedef int (*NihTreeHandler) (NihTree *node, void *data);
920
921 /**
922 * NIH_TREE_FOREACH_FULL:
923@@ -370,6 +381,13 @@
924 NihTree * nih_tree_prev_post_full (NihTree *tree, NihTree *node,
925 NihTreeFilter filter, void *data);
926
927+int nih_tree_foreach (NihTree *tree, size_t *len,
928+ NihTreeHandler handler, void *data)
929+ __attribute__((unused));
930+
931+size_t nih_tree_count (NihTree *tree)
932+ __attribute__((warn_unused_result, unused));
933+
934 NIH_END_EXTERN
935
936 #endif /* NIH_TREE_H */
937
938=== modified file 'po/libnih.pot'
939--- po/libnih.pot 2010-12-23 22:04:08 +0000
940+++ po/libnih.pot 2011-05-08 11:56:38 +0000
941@@ -6,9 +6,9 @@
942 #, fuzzy
943 msgid ""
944 msgstr ""
945-"Project-Id-Version: libnih 1.0.3\n"
946+"Project-Id-Version: libnih 1.0.4\n"
947 "Report-Msgid-Bugs-To: libnih-bugs@netsplit.com\n"
948-"POT-Creation-Date: 2010-12-23 21:53+0000\n"
949+"POT-Creation-Date: 2011-05-08 12:30+0100\n"
950 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
951 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
952 "Language-Team: LANGUAGE <LL@li.org>\n"

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: