aboutsummaryrefslogtreecommitdiffstats
path: root/lib/includes
diff options
context:
space:
mode:
authorJordan Augé <jordan.auge+fdio@cisco.com>2019-11-12 00:03:08 +0100
committerJordan Augé <jordan.auge+fdio@cisco.com>2019-11-14 17:16:56 +0100
commitfdb523a02680f5aa0727b862f0616ba5f8cb24cf (patch)
tree01fdf30eafde2a6f74edba52890715f55fdd6554 /lib/includes
parent4f57ca72e8131e5cfb023b26417b924e774d5e73 (diff)
[HICN-386] Improve API error management in libhicnctrl
Change-Id: I332e74ebcd89798c93de50ae7a20f7af8f59f54c Signed-off-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Diffstat (limited to 'lib/includes')
-rw-r--r--lib/includes/CMakeLists.txt4
-rw-r--r--lib/includes/hicn/util/array.h199
-rw-r--r--lib/includes/hicn/util/log.h66
-rw-r--r--lib/includes/hicn/util/map.h250
-rw-r--r--lib/includes/hicn/util/set.h228
5 files changed, 747 insertions, 0 deletions
diff --git a/lib/includes/CMakeLists.txt b/lib/includes/CMakeLists.txt
index d1eaa0b57..60247909c 100644
--- a/lib/includes/CMakeLists.txt
+++ b/lib/includes/CMakeLists.txt
@@ -47,7 +47,11 @@ set(LIBHICN_HEADER_FILES_PROTOCOL
)
set(LIBHICN_HEADER_FILES_UTIL
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/array.h
${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/ip_address.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/log.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/map.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/set.h
${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/token.h
${CMAKE_CURRENT_SOURCE_DIR}/hicn/util/types.h
PARENT_SCOPE
diff --git a/lib/includes/hicn/util/array.h b/lib/includes/hicn/util/array.h
new file mode 100644
index 000000000..ab8852ed8
--- /dev/null
+++ b/lib/includes/hicn/util/array.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file array.h
+ * \brief Generic array template
+ */
+
+#ifndef UTIL_ARRAY_H
+#define UTIL_ARRAY_H
+
+#include <assert.h>
+#include <hicn/util/log.h>
+#include <math.h> // log2
+#include <string.h> // memmove
+
+#define BUFSIZE 1024
+
+typedef int(*cmp_t)(const void * x, const void * y);
+
+#define TYPEDEF_ARRAY_H(NAME, T) \
+ \
+typedef struct { \
+ size_t size; \
+ size_t max_size_log; \
+ T * elements; \
+} NAME ## _t; \
+ \
+int NAME ## _initialize(NAME ## _t * array); \
+ \
+int NAME ## _finalize(NAME ## _t * array); \
+ \
+NAME ## _t * NAME ## _create(); \
+ \
+void NAME ## _free(NAME ## _t * array); \
+ \
+int NAME ## _add(NAME ## _t * array, T element); \
+ \
+int NAME ## _remove_index(NAME ## _t * array, int index, T * element); \
+ \
+int NAME ## _remove(NAME ## _t * array, const T search, T * element); \
+ \
+int NAME ## _get(const NAME ## _t * array, const T search, T * element); \
+ \
+int NAME ## _get_index(const NAME ## _t * array, int index, T * element); \
+ \
+int NAME ## _get_elements(const NAME ## _t * array, T ** elements); \
+ \
+size_t NAME ## _len(const NAME ## _t * array);
+
+
+#define ARRAY_MAX_SIZE_LOG_INIT 0
+
+#define TYPEDEF_ARRAY(NAME, T, CMP, SNPRINTF) \
+int \
+NAME ## _initialize(NAME ## _t * array) \
+{ \
+ array->max_size_log = ARRAY_MAX_SIZE_LOG_INIT; \
+ array->size = 0; \
+ if (array->max_size_log == 0) { \
+ array->elements = NULL; \
+ return 0; \
+ } \
+ array->elements = malloc((1 << array->max_size_log) * sizeof(T)); \
+ if (!array->elements) \
+ return -1; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _finalize(NAME ## _t * array) \
+{ \
+ for (unsigned i = 0; i < array->size; i++) { \
+ NAME ## _remove_index(array, i, NULL); \
+ } \
+ return 0; \
+} \
+ \
+NAME ## _t * \
+NAME ## _create() \
+{ \
+ NAME ## _t * array = malloc(sizeof(NAME ## _t)); \
+ if (!array) \
+ goto ERR_MALLOC; \
+ \
+ if (NAME ## _initialize(array) < 0) \
+ goto ERR_INITIALIZE; \
+ \
+ return array; \
+ \
+ERR_INITIALIZE: \
+ free(array); \
+ERR_MALLOC: \
+ return NULL; \
+} \
+ \
+void \
+NAME ## _free(NAME ## _t * array) \
+{ \
+ NAME ## _finalize(array); \
+ free(array->elements); \
+ free(array); \
+} \
+ \
+int \
+NAME ## _add(NAME ## _t * array, T element) \
+{ \
+ /* Ensure sufficient space for next addition */ \
+ size_t new_size_log = (array->size > 0) ? log2(array->size)+1 : 1; \
+ if (new_size_log > array->max_size_log) { \
+ array->max_size_log = new_size_log; \
+ array->elements = realloc(array->elements, \
+ (1 << new_size_log) * sizeof(T)); \
+ } \
+ \
+ if (!array->elements) \
+ goto ERR_REALLOC; \
+ \
+ array->elements[array->size++] = element; \
+ return 0; \
+ \
+ERR_REALLOC: \
+ return -1; \
+} \
+ \
+int \
+NAME ## _remove_index(NAME ## _t * array, int index, T * element) \
+{ \
+ if (index > NAME ## _len(array)) \
+ return -1; \
+ if (element) \
+ *element = array->elements[index]; \
+ if (index < array->size) \
+ memmove(array->elements + index, array->elements + index + 1, \
+ array->size - index); \
+ array->size--; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _remove(NAME ## _t * array, const T search, T * element) \
+{ \
+ for (unsigned i = 0; i < array->size; i++) { \
+ if (CMP(search, array->elements[i]) == 0) \
+ return facelet_array_remove_index(array, i, element); \
+ } \
+ /* Not found */ \
+ if (element) \
+ *element = NULL; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _get(const NAME ## _t * array, const T search, T * element) \
+{ \
+ assert(element); \
+ for (unsigned i = 0; i < array->size; i++) \
+ if (CMP(search, array->elements[i]) == 0) \
+ *element = array->elements[i]; \
+ return 0; \
+ /* Not found */ \
+ *element = NULL; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _get_index(const NAME ## _t * array, int index, T * element) \
+{ \
+ assert(element); \
+ *element = array->elements[index]; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _get_elements(const NAME ## _t * array, T ** elements) \
+{ \
+ *elements = array->elements; \
+ return 0; \
+} \
+ \
+size_t \
+NAME ## _len(const NAME ## _t * array) \
+{ \
+ return array->size; \
+}
+
+#endif /* UTIL_ARRAY_H */
diff --git a/lib/includes/hicn/util/log.h b/lib/includes/hicn/util/log.h
new file mode 100644
index 000000000..f1cafba47
--- /dev/null
+++ b/lib/includes/hicn/util/log.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_LOG_H
+#define UTIL_LOG_H
+
+#include <stdarg.h> // va_*
+#include <stdio.h> // FILE
+#include <time.h> // time, localtime
+
+#define LOG_FATAL 0
+#define LOG_ERROR 1
+#define LOG_WARN 2
+#define LOG_INFO 3
+#define LOG_DEBUG 4
+#define LOG_TRACE 5
+
+typedef struct {
+ int log_level;
+ int debug;
+ FILE * log_file;
+} log_conf_t;
+
+#define DEFAULT_LOG_CONF { \
+ .log_level = LOG_DEBUG, \
+ .debug = 0, \
+ .log_file = NULL, \
+};
+
+extern log_conf_t log_conf;
+
+#define WITH_DEBUG(BLOCK) \
+ if (log_conf.log_level >= LOG_DEBUG) \
+ BLOCK
+
+#define FATAL(fmt, ...) (_log(LOG_FATAL, fmt, ##__VA_ARGS__ ))
+#define ERROR(fmt, ...) (_log(LOG_ERROR, fmt, ##__VA_ARGS__ ))
+#define WARN(fmt, ...) (_log(LOG_WARN, fmt, ##__VA_ARGS__ ))
+#define INFO(fmt, ...) (_log(LOG_INFO, fmt, ##__VA_ARGS__ ))
+#define DEBUG(fmt, ...) (_log(LOG_DEBUG, fmt, ##__VA_ARGS__ ))
+#define TRACE(fmt, ...) (_log(LOG_TRACE, fmt, ##__VA_ARGS__ ))
+
+void _log_va(int level, const char *fmt, va_list ap);
+
+void _log(int level, const char *fmt, ...);
+
+void fatal(char *fmt, ...);
+
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+void print_trace(void);
+#endif
+
+#endif // UTIL_LOG_H
diff --git a/lib/includes/hicn/util/map.h b/lib/includes/hicn/util/map.h
new file mode 100644
index 000000000..1ac5812ef
--- /dev/null
+++ b/lib/includes/hicn/util/map.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_MAP_H
+#define UTIL_MAP_H
+
+#include <stdlib.h>
+
+#include "set.h"
+
+#define ERR_MAP_EXISTS -2
+#define ERR_MAP_NOT_FOUND -3
+
+#define TYPEDEF_MAP_H(NAME, KEY_T, VAL_T) \
+ \
+typedef struct { \
+ KEY_T key; \
+ VAL_T value; \
+} NAME ## _pair_t; \
+ \
+NAME ## _pair_t * NAME ## _pair_create(KEY_T key, VAL_T value); \
+ \
+void NAME ## _pair_free(NAME ## _pair_t * pair); \
+ \
+int NAME ## _pair_cmp(const NAME ## _pair_t * p1, const NAME ## _pair_t * p2); \
+ \
+TYPEDEF_SET_H(NAME ## _pair_set, NAME ## _pair_t *) \
+ \
+typedef struct NAME ## _s { \
+ NAME ## _pair_set_t pair_set; \
+} NAME ## _t; \
+ \
+int NAME ## _initialize(NAME ## _t * map); \
+ \
+int NAME ## _finalize(NAME ## _t * map); \
+ \
+NAME ## _t * NAME ## _create(); \
+ \
+void NAME ## _free(NAME ## _t * map); \
+ \
+int NAME ## _add(NAME ## _t * map, KEY_T key, VAL_T value); \
+ \
+int NAME ## _remove(NAME ## _t * map, KEY_T key, VAL_T * value); \
+ \
+int NAME ## _get(NAME ## _t * map, KEY_T key, VAL_T * value); \
+ \
+void NAME ## _dump(NAME ## _t * map);
+
+
+
+
+#define TYPEDEF_MAP(NAME, KEY_T, VAL_T, CMP, KEY_SNPRINTF, VALUE_SNPRINTF) \
+ \
+NAME ## _pair_t * NAME ## _pair_create(KEY_T key, VAL_T value) \
+{ \
+ /* Create pair */ \
+ NAME ## _pair_t * pair = malloc(sizeof(NAME ## _pair_t)); \
+ if (!pair) \
+ return NULL; \
+ \
+ pair->key = key; \
+ pair->value = value; \
+ \
+ return pair; \
+} \
+ \
+void NAME ## _pair_free(NAME ## _pair_t * pair) \
+{ \
+ free(pair); \
+} \
+ \
+int \
+NAME ## _pair_cmp(const NAME ## _pair_t * p1, const NAME ## _pair_t * p2) \
+{ \
+ return (CMP(p1->key, p2->key)); \
+} \
+ \
+int \
+NAME ## _pair_snprintf(char * buf, size_t size, const NAME ## _pair_t * pair) { \
+ int rc; \
+ rc = KEY_SNPRINTF(buf, BUFSIZE/2, (KEY_T)pair->key); \
+ if (rc < 0) \
+ return rc; \
+ rc = VALUE_SNPRINTF(buf+rc, BUFSIZE/2, (VAL_T)pair->value); \
+ return rc; \
+} \
+ \
+TYPEDEF_SET(NAME ## _pair_set, NAME ## _pair_t *, NAME ## _pair_cmp, NAME ## _pair_snprintf); \
+ \
+int \
+NAME ## _initialize(NAME ## _t * map) \
+{ \
+ return NAME ## _pair_set_initialize(&map->pair_set); \
+} \
+ \
+int \
+NAME ## _finalize(NAME ## _t * map) \
+{ \
+ NAME ## _pair_t ** array; \
+ int n = NAME ## _pair_set_get_array(&map->pair_set, &array); \
+ if (n < 0) \
+ return -1; \
+ for (unsigned i = 0; i < n; i++) { \
+ NAME ## _pair_t * pair = array[i]; \
+ NAME ## _pair_set_remove(&map->pair_set, pair, NULL); \
+ NAME ## _pair_free(pair); \
+ } \
+ free(array); \
+ return NAME ## _pair_set_finalize(&map->pair_set); \
+} \
+ \
+NAME ## _t * \
+NAME ## _create() \
+{ \
+ NAME ## _t * map = malloc(sizeof(NAME ## _t)); \
+ if (!map) \
+ goto ERR_MALLOC; \
+ \
+ if (NAME ## _initialize(map) < 0) \
+ goto ERR_INITIALIZE; \
+ \
+ return map; \
+ \
+ERR_INITIALIZE: \
+ free(map); \
+ERR_MALLOC: \
+ return NULL; \
+} \
+ \
+void \
+NAME ## _free(NAME ## _t * map) \
+{ \
+ NAME ## _finalize(map); \
+ free(map); \
+} \
+ \
+int \
+NAME ## _add(NAME ## _t * map, KEY_T key, VAL_T value) \
+{ \
+ int rc; \
+ NAME ## _pair_t * found = NULL; \
+ \
+ NAME ## _pair_t * pair = NAME ## _pair_create(key, value); \
+ if (!pair) \
+ return -1; \
+ \
+ rc = NAME ## _pair_set_get(&map->pair_set, pair, &found); \
+ if (rc < 0) \
+ return -1; \
+ if (found) { \
+ NAME ## _pair_free(pair); \
+ return ERR_MAP_EXISTS; \
+ } \
+ \
+ rc = NAME ## _pair_set_add(&map->pair_set, pair); \
+ if (rc < 0) { \
+ NAME ## _pair_free(pair); \
+ return -1; \
+ } \
+ return 0; \
+} \
+ \
+int \
+NAME ## _remove(NAME ## _t * map, KEY_T key, VAL_T * value) \
+{ \
+ NAME ## _pair_t * found = NULL; \
+ NAME ## _pair_t search = { .key = key }; \
+ int rc = NAME ## _pair_set_remove(&map->pair_set, &search, &found); \
+ if (rc < 0) \
+ return ERR_MAP_NOT_FOUND; \
+ if (value) \
+ *value = found->value; \
+ NAME ## _pair_free(found); \
+ return 0; \
+} \
+ \
+int \
+NAME ## _get(NAME ## _t * map, KEY_T key, VAL_T * value) \
+{ \
+ NAME ## _pair_t * found = NULL, search = { .key = key }; \
+ int rc = NAME ## _pair_set_get(&map->pair_set, &search, &found); \
+ if (rc < 0) \
+ return -1; \
+ if (found) \
+ *value = found->value; \
+ return 0; \
+} \
+ \
+void \
+NAME ## _dump(NAME ## _t * map) { \
+ NAME ## _pair_set_dump(&map->pair_set); \
+} \
+ \
+int \
+NAME ## _get_key_array(NAME ## _t * map, KEY_T **array) { \
+ NAME ## _pair_t ** pair_array; \
+ int n = NAME ## _pair_set_get_array(&map->pair_set, &pair_array); \
+ if (n < 0) \
+ return -1; \
+ if (!array) \
+ goto END; \
+ /* Allocate result array */ \
+ *array = malloc(n * sizeof(KEY_T)); \
+ if (!array) { \
+ free(pair_array); \
+ return -1; \
+ } \
+ /* Copy keys */ \
+ for (int i = 0; i < n; i++) \
+ (*array)[i] = pair_array[i]->key; \
+ free(pair_array); \
+END: \
+ return n; \
+} \
+ \
+int \
+NAME ## _get_value_array(NAME ## _t * map, VAL_T **array) { \
+ NAME ## _pair_t ** pair_array; \
+ int n = NAME ## _pair_set_get_array(&map->pair_set, &pair_array); \
+ if (n < 0) \
+ return -1; \
+ if (!array) \
+ goto END; \
+ /* Allocate result array */ \
+ *array = malloc(n * sizeof(VAL_T)); \
+ if (!array) { \
+ free(pair_array); \
+ return -1; \
+ } \
+ /* Copy values */ \
+ for (int i = 0; i < n; i++) \
+ (*array)[i] = pair_array[i]->value; \
+ free(pair_array); \
+END: \
+ return n; \
+}
+
+#endif /* UTIL_MAP_H */
diff --git a/lib/includes/hicn/util/set.h b/lib/includes/hicn/util/set.h
new file mode 100644
index 000000000..c1f43918b
--- /dev/null
+++ b/lib/includes/hicn/util/set.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_SET_H
+#define UTIL_SET_H
+
+#include <hicn/util/log.h>
+#include <search.h>
+#include <string.h>
+//#if !defined(__ANDROID__) && !defined(__APPLE__)
+//#include <threads.h>
+//#else
+#define thread_local _Thread_local
+//#endif /* ! __ANDROID__ */
+
+#define ERR_SET_EXISTS -2
+#define ERR_SET_NOT_FOUND -3
+
+/* FIXME: buffer overflow when this is too small... investigate */
+#define BUFSIZE 1024
+
+static inline
+int
+int_cmp(const int x, const int y)
+{
+ return x - y;
+}
+
+static inline
+int
+int_snprintf(char * buf, size_t size, int value) {
+ return snprintf(buf, size, "%d", value);
+}
+
+static inline
+int
+string_snprintf(char * buf, size_t size, const char * s) {
+ return snprintf(buf, size, "%s", s);
+}
+
+static inline
+int
+generic_snprintf(char * buf, size_t size, const void * value) {
+ return snprintf(buf, BUFSIZE, "%p", value);
+}
+
+typedef int(*cmp_t)(const void * x, const void * y);
+
+#define TYPEDEF_SET_H(NAME, T) \
+ \
+typedef struct { \
+ size_t size; \
+ void * root; \
+} NAME ## _t; \
+ \
+int NAME ## _initialize(NAME ## _t * set); \
+ \
+int NAME ## _finalize(NAME ## _t * set); \
+ \
+NAME ## _t * NAME ## _create(); \
+ \
+void NAME ## _free(NAME ## _t * set); \
+ \
+int NAME ## _add(NAME ## _t * set, const T element); \
+ \
+int NAME ## _remove(NAME ## _t * set, const T search, T * element); \
+ \
+int NAME ## _get(const NAME ## _t * set, const T search, T * element); \
+ \
+int NAME ## _get_array(const NAME ## _t * set, T ** element); \
+ \
+void NAME ## _dump(NAME ## _t * set);
+
+
+
+
+#define TYPEDEF_SET(NAME, T, CMP, SNPRINTF) \
+int \
+NAME ## _initialize(NAME ## _t * set) \
+{ \
+ set->root = NULL; \
+ set->size = 0; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _finalize(NAME ## _t * set) \
+{ \
+ T * array; \
+ int n = NAME ## _get_array(set, &array); \
+ if (n < 0) \
+ return -1; \
+ for (unsigned i = 0; i < n; i++) { \
+ T element = array[i]; \
+ NAME ## _remove(set, element, NULL); \
+ } \
+ free(array); \
+ return 0; \
+} \
+ \
+NAME ## _t * \
+NAME ## _create() \
+{ \
+ NAME ## _t * set = malloc(sizeof(NAME ## _t)); \
+ if (!set) \
+ goto ERR_MALLOC; \
+ \
+ if (NAME ## _initialize(set) < 0) \
+ goto ERR_INITIALIZE; \
+ \
+ return set; \
+ \
+ERR_INITIALIZE: \
+ free(set); \
+ERR_MALLOC: \
+ return NULL; \
+} \
+ \
+void \
+NAME ## _free(NAME ## _t * set) \
+{ \
+ NAME ## _finalize(set); \
+ free(set); \
+} \
+ \
+int \
+NAME ## _add(NAME ## _t * set, const T element) \
+{ \
+ void * ptr = tsearch(element, &set->root, (cmp_t)CMP); \
+ if (!ptr) \
+ return -1; \
+ set->size++; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _remove(NAME ## _t * set, const T search, T * element) \
+{ \
+ T * found = tfind(search, &set->root, (cmp_t)CMP); \
+ if (!found) \
+ return ERR_SET_NOT_FOUND; \
+ if (element) \
+ *element = *found; \
+ tdelete(search, &set->root, (cmp_t)CMP); \
+ set->size--; \
+ return 0; \
+} \
+ \
+int \
+NAME ## _get(const NAME ## _t * set, const T search, T * element) \
+{ \
+ T * found = tfind(search, &set->root, (cmp_t)CMP); \
+ if (element) \
+ *element = found ? *found : NULL; \
+ return 0; \
+} \
+ \
+static void \
+NAME ## _dump_node(const void *nodep, const VISIT which, \
+ const int depth) \
+{ \
+ char buf[BUFSIZE]; \
+ switch (which) { \
+ case preorder: \
+ case endorder: \
+ break; \
+ case postorder: \
+ case leaf: \
+ SNPRINTF(buf, BUFSIZE, *(T*)nodep); \
+ INFO("%s", buf); \
+ break; \
+ } \
+} \
+ \
+void \
+NAME ## _dump(NAME ## _t * set) { \
+ twalk(set->root, NAME ## _dump_node); \
+} \
+ \
+thread_local \
+T * NAME ## _array_pos = NULL; \
+ \
+static void \
+NAME ## _add_node_to_array(const void *nodep, const VISIT which, \
+ const int depth) \
+{ \
+ if (!NAME ## _array_pos) \
+ return; \
+ switch (which) { \
+ case preorder: \
+ case endorder: \
+ break; \
+ case postorder: \
+ case leaf: \
+ *NAME ## _array_pos = *(T*)nodep; \
+ NAME ## _array_pos++; \
+ break; \
+ } \
+} \
+ \
+int \
+NAME ## _get_array(const NAME ## _t * set, T ** element) \
+{ \
+ if (!element) \
+ goto END; \
+ *element = malloc(set->size * sizeof(T)); \
+ if (!*element) \
+ return -1; \
+ NAME ## _array_pos = *element; \
+ twalk(set->root, NAME ## _add_node_to_array); \
+ NAME ## _array_pos = NULL; \
+END: \
+ return set->size; \
+}
+
+#endif /* UTIL_SET_H */