diff --git a/.gitignore b/.gitignore index 025d7202d1..34f8454423 100644 --- a/.gitignore +++ b/.gitignore @@ -185,6 +185,8 @@ uptime-*.json /src/lib/libtor-malloc-testing.a /src/lib/libtor-string.a /src/lib/libtor-string-testing.a +/src/lib/libtor-smartlist-core.a +/src/lib/libtor-smartlist-core-testing.a /src/lib/libtor-tls.a /src/lib/libtor-tls-testing.a /src/lib/libtor-trace.a diff --git a/Makefile.am b/Makefile.am index 61451ed264..482189eebd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,11 +40,12 @@ endif # "Common" libraries used to link tor's utility code. TOR_UTIL_LIBS = \ src/common/libor.a \ + src/lib/libtor-container.a \ src/lib/libtor-log.a \ src/lib/libtor-lock.a \ src/lib/libtor-fdio.a \ - src/lib/libtor-container.a \ src/lib/libtor-string.a \ + src/lib/libtor-smartlist-core.a \ src/lib/libtor-malloc.a \ src/lib/libtor-wallclock.a \ src/lib/libtor-err.a \ @@ -55,11 +56,12 @@ TOR_UTIL_LIBS = \ # and tests) TOR_UTIL_TESTING_LIBS = \ src/common/libor-testing.a \ + src/lib/libtor-container-testing.a \ src/lib/libtor-log-testing.a \ src/lib/libtor-lock-testing.a \ src/lib/libtor-fdio-testing.a \ - src/lib/libtor-container-testing.a \ src/lib/libtor-string-testing.a \ + src/lib/libtor-smartlist-core-testing.a \ src/lib/libtor-malloc-testing.a \ src/lib/libtor-wallclock-testing.a \ src/lib/libtor-err-testing.a \ diff --git a/src/include.am b/src/include.am index 1d2a037e90..9e89fc8e06 100644 --- a/src/include.am +++ b/src/include.am @@ -13,6 +13,7 @@ include src/lib/lock/include.am include src/lib/log/include.am include src/lib/malloc/include.am include src/lib/string/include.am +include src/lib/smartlist_core/include.am include src/lib/testsupport/include.am include src/lib/tls/include.am include src/lib/trace/include.am diff --git a/src/lib/container/.may_include b/src/lib/container/.may_include index 1114ad8453..2eecb503cd 100644 --- a/src/lib/container/.may_include +++ b/src/lib/container/.may_include @@ -5,6 +5,7 @@ lib/ctime/*.h lib/defs/*.h lib/malloc/*.h lib/err/*.h +lib/smartlist_core/*.h lib/string/*.h lib/testsupport/testsupport.h lib/intmath/*.h diff --git a/src/lib/container/smartlist.c b/src/lib/container/smartlist.c index 3c0844384a..9025cab9f6 100644 --- a/src/lib/container/smartlist.c +++ b/src/lib/container/smartlist.c @@ -9,7 +9,6 @@ * with helper functions to use smartlists. **/ -#include "lib/malloc/util_malloc.h" #include "lib/container/smartlist.h" #include "lib/err/torerr.h" #include "lib/malloc/util_malloc.h" @@ -24,108 +23,6 @@ #include #include -/** All newly allocated smartlists have this capacity. */ -#define SMARTLIST_DEFAULT_CAPACITY 16 - -/** Allocate and return an empty smartlist. - */ -MOCK_IMPL(smartlist_t *, -smartlist_new,(void)) -{ - smartlist_t *sl = tor_malloc(sizeof(smartlist_t)); - sl->num_used = 0; - sl->capacity = SMARTLIST_DEFAULT_CAPACITY; - sl->list = tor_calloc(sizeof(void *), sl->capacity); - return sl; -} - -/** Deallocate a smartlist. Does not release storage associated with the - * list's elements. - */ -MOCK_IMPL(void, -smartlist_free_,(smartlist_t *sl)) -{ - if (!sl) - return; - tor_free(sl->list); - tor_free(sl); -} - -/** Remove all elements from the list. - */ -void -smartlist_clear(smartlist_t *sl) -{ - memset(sl->list, 0, sizeof(void *) * sl->num_used); - sl->num_used = 0; -} - -#if SIZE_MAX < INT_MAX -#error "We don't support systems where size_t is smaller than int." -#endif - -/** Make sure that sl can hold at least size entries. */ -static inline void -smartlist_ensure_capacity(smartlist_t *sl, size_t size) -{ - /* Set MAX_CAPACITY to MIN(INT_MAX, SIZE_MAX / sizeof(void*)) */ -#if (SIZE_MAX/SIZEOF_VOID_P) > INT_MAX -#define MAX_CAPACITY (INT_MAX) -#else -#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*)))) -#endif - - raw_assert(size <= MAX_CAPACITY); - - if (size > (size_t) sl->capacity) { - size_t higher = (size_t) sl->capacity; - if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) { - higher = MAX_CAPACITY; - } else { - while (size > higher) - higher *= 2; - } - sl->list = tor_reallocarray(sl->list, sizeof(void *), - ((size_t)higher)); - memset(sl->list + sl->capacity, 0, - sizeof(void *) * (higher - sl->capacity)); - sl->capacity = (int) higher; - } -#undef ASSERT_CAPACITY -#undef MAX_CAPACITY -} - -/** Append element to the end of the list. */ -void -smartlist_add(smartlist_t *sl, void *element) -{ - smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1); - sl->list[sl->num_used++] = element; -} - -/** Append each element from S2 to the end of S1. */ -void -smartlist_add_all(smartlist_t *s1, const smartlist_t *s2) -{ - size_t new_size = (size_t)s1->num_used + (size_t)s2->num_used; - tor_assert(new_size >= (size_t) s1->num_used); /* check for overflow. */ - smartlist_ensure_capacity(s1, new_size); - memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*)); - tor_assert(new_size <= INT_MAX); /* redundant. */ - s1->num_used = (int) new_size; -} - -/** Append a copy of string to sl */ -void -smartlist_add_strdup(struct smartlist_t *sl, const char *string) -{ - char *copy; - - copy = tor_strdup(string); - - smartlist_add(sl, copy); -} - /** Append the string produced by tor_asprintf(pattern, ...) * to sl. */ void @@ -150,56 +47,6 @@ smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern, smartlist_add(sl, str); } -/** Remove all elements E from sl such that E==element. Preserve - * the order of any elements before E, but elements after E can be - * rearranged. - */ -void -smartlist_remove(smartlist_t *sl, const void *element) -{ - int i; - if (element == NULL) - return; - for (i=0; i < sl->num_used; i++) - if (sl->list[i] == element) { - sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ - i--; /* so we process the new i'th element */ - sl->list[sl->num_used] = NULL; - } -} - -/** As smartlist_remove, but do not change the order of - * any elements not removed */ -void -smartlist_remove_keeporder(smartlist_t *sl, const void *element) -{ - int i, j, num_used_orig = sl->num_used; - if (element == NULL) - return; - - for (i=j=0; j < num_used_orig; ++j) { - if (sl->list[j] == element) { - --sl->num_used; - } else { - sl->list[i++] = sl->list[j]; - } - } -} - -/** If sl is nonempty, remove and return the final element. Otherwise, - * return NULL. */ -void * -smartlist_pop_last(smartlist_t *sl) -{ - tor_assert(sl); - if (sl->num_used) { - void *tmp = sl->list[--sl->num_used]; - sl->list[sl->num_used] = NULL; - return tmp; - } else - return NULL; -} - /** Reverse the order of the items in sl. */ void smartlist_reverse(smartlist_t *sl) @@ -232,18 +79,6 @@ smartlist_string_remove(smartlist_t *sl, const char *element) } } -/** Return true iff some element E of sl has E==element. - */ -int -smartlist_contains(const smartlist_t *sl, const void *element) -{ - int i; - for (i=0; i < sl->num_used; i++) - if (sl->list[i] == element) - return 1; - return 0; -} - /** Return true iff sl has some element E such that * !strcmp(E,element) */ @@ -399,131 +234,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2) smartlist_remove(sl1, sl2->list[i]); } -/** Remove the idxth element of sl; if idx is not the last - * element, swap the last element of sl into the idxth space. - */ -void -smartlist_del(smartlist_t *sl, int idx) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx < sl->num_used); - sl->list[idx] = sl->list[--sl->num_used]; - sl->list[sl->num_used] = NULL; -} - -/** Remove the idxth element of sl; if idx is not the last element, - * moving all subsequent elements back one space. Return the old value - * of the idxth element. - */ -void -smartlist_del_keeporder(smartlist_t *sl, int idx) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx < sl->num_used); - --sl->num_used; - if (idx < sl->num_used) - memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx)); - sl->list[sl->num_used] = NULL; -} - -/** Insert the value val as the new idxth element of - * sl, moving all items previously at idx or later - * forward one space. - */ -void -smartlist_insert(smartlist_t *sl, int idx, void *val) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx <= sl->num_used); - if (idx == sl->num_used) { - smartlist_add(sl, val); - } else { - smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1); - /* Move other elements away */ - if (idx < sl->num_used) - memmove(sl->list + idx + 1, sl->list + idx, - sizeof(void*)*(sl->num_used-idx)); - sl->num_used++; - sl->list[idx] = val; - } -} - -/** - * Split a string str along all occurrences of sep, - * appending the (newly allocated) split strings, in order, to - * sl. Return the number of strings added to sl. - * - * If flags&SPLIT_SKIP_SPACE is true, remove initial and - * trailing space from each entry. - * If flags&SPLIT_IGNORE_BLANK is true, remove any entries - * of length 0. - * If flags&SPLIT_STRIP_SPACE is true, strip spaces from each - * split string. - * - * If max\>0, divide the string into no more than max pieces. If - * sep is NULL, split on any sequence of horizontal space. - */ -int -smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, - int flags, int max) -{ - const char *cp, *end, *next; - int n = 0; - - tor_assert(sl); - tor_assert(str); - - cp = str; - while (1) { - if (flags&SPLIT_SKIP_SPACE) { - while (TOR_ISSPACE(*cp)) ++cp; - } - - if (max>0 && n == max-1) { - end = strchr(cp,'\0'); - } else if (sep) { - end = strstr(cp,sep); - if (!end) - end = strchr(cp,'\0'); - } else { - for (end = cp; *end && *end != '\t' && *end != ' '; ++end) - ; - } - - tor_assert(end); - - if (!*end) { - next = NULL; - } else if (sep) { - next = end+strlen(sep); - } else { - next = end+1; - while (*next == '\t' || *next == ' ') - ++next; - } - - if (flags&SPLIT_SKIP_SPACE) { - while (end > cp && TOR_ISSPACE(*(end-1))) - --end; - } - if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) { - char *string = tor_strndup(cp, end-cp); - if (flags&SPLIT_STRIP_SPACE) - tor_strstrip(string, " "); - smartlist_add(sl, string); - ++n; - } - if (!next) - break; - cp = next; - } - - return n; -} - /** Allocate and return a new string containing the concatenation of * the elements of sl, in order, separated by join. If * terminate is true, also terminate the string with join. diff --git a/src/lib/container/smartlist.h b/src/lib/container/smartlist.h index c202e134f9..76ecbda0a2 100644 --- a/src/lib/container/smartlist.h +++ b/src/lib/container/smartlist.h @@ -6,50 +6,19 @@ #ifndef TOR_SMARTLIST_H #define TOR_SMARTLIST_H -#include #include -#include "lib/cc/compat_compiler.h" -#include "lib/cc/torint.h" -#include "lib/testsupport/testsupport.h" +#include "lib/smartlist_core/smartlist_core.h" +#include "lib/smartlist_core/smartlist_foreach.h" +#include "lib/smartlist_core/smartlist_split.h" -/** A resizeable list of pointers, with associated helpful functionality. - * - * The members of this struct are exposed only so that macros and inlines can - * use them; all access to smartlist internals should go through the functions - * and macros defined here. - **/ -typedef struct smartlist_t { - /** @{ */ - /** list has enough capacity to store exactly capacity elements - * before it needs to be resized. Only the first num_used (\<= - * capacity) elements point to valid data. - */ - void **list; - int num_used; - int capacity; - /** @} */ -} smartlist_t; - -MOCK_DECL(smartlist_t *, smartlist_new, (void)); -MOCK_DECL(void, smartlist_free_, (smartlist_t *sl)); -#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl)) - -void smartlist_clear(smartlist_t *sl); -void smartlist_add(smartlist_t *sl, void *element); -void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2); -void smartlist_add_strdup(struct smartlist_t *sl, const char *string); void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...) CHECK_PRINTF(2, 3); void smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern, va_list args) CHECK_PRINTF(2, 0); -void smartlist_remove(smartlist_t *sl, const void *element); -void smartlist_remove_keeporder(smartlist_t *sl, const void *element); -void *smartlist_pop_last(smartlist_t *sl); void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_contains(const smartlist_t *sl, const void *element); int smartlist_contains_string(const smartlist_t *sl, const char *element); int smartlist_pos(const smartlist_t *sl, const void *element); int smartlist_string_pos(const smartlist_t *, const char *elt); @@ -62,52 +31,6 @@ int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); -/* smartlist_choose() is defined in crypto.[ch] */ -#ifdef DEBUG_SMARTLIST -#include "lib/err/torerr.h" -#include -/** Return the number of items in sl. - */ -static inline int smartlist_len(const smartlist_t *sl); -static inline int smartlist_len(const smartlist_t *sl) { - raw_assert(sl); - return (sl)->num_used; -} -/** Return the idxth element of sl. - */ -static inline void *smartlist_get(const smartlist_t *sl, int idx); -static inline void *smartlist_get(const smartlist_t *sl, int idx) { - raw_assert(sl); - raw_assert(idx>=0); - raw_assert(sl->num_used > idx); - return sl->list[idx]; -} -static inline void smartlist_set(smartlist_t *sl, int idx, void *val) { - raw_assert(sl); - raw_assert(idx>=0); - raw_assert(sl->num_used > idx); - sl->list[idx] = val; -} -#else /* !(defined(DEBUG_SMARTLIST)) */ -#define smartlist_len(sl) ((sl)->num_used) -#define smartlist_get(sl, idx) ((sl)->list[idx]) -#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val)) -#endif /* defined(DEBUG_SMARTLIST) */ - -/** Exchange the elements at indices idx1 and idx2 of the - * smartlist sl. */ -static inline void smartlist_swap(smartlist_t *sl, int idx1, int idx2) -{ - if (idx1 != idx2) { - void *elt = smartlist_get(sl, idx1); - smartlist_set(sl, idx1, smartlist_get(sl, idx2)); - smartlist_set(sl, idx2, elt); - } -} - -void smartlist_del(smartlist_t *sl, int idx); -void smartlist_del_keeporder(smartlist_t *sl, int idx); -void smartlist_insert(smartlist_t *sl, int idx, void *val); void smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b)); void *smartlist_get_most_frequent_(const smartlist_t *sl, @@ -153,136 +76,12 @@ void smartlist_pqueue_assert_ok(smartlist_t *sl, int (*compare)(const void *a, const void *b), int idx_field_offset); -#define SPLIT_SKIP_SPACE 0x01 -#define SPLIT_IGNORE_BLANK 0x02 -#define SPLIT_STRIP_SPACE 0x04 -int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, - int flags, int max); char *smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, size_t *len_out) ATTR_MALLOC; char *smartlist_join_strings2(smartlist_t *sl, const char *join, size_t join_len, int terminate, size_t *len_out) ATTR_MALLOC; -/** Iterate over the items in a smartlist sl, in order. For each item, - * assign it to a new local variable of type type named var, and - * execute the statements inside the loop body. Inside the loop, the loop - * index can be accessed as var_sl_idx and the length of the list can - * be accessed as var_sl_len. - * - * NOTE: Do not change the length of the list while the loop is in progress, - * unless you adjust the _sl_len variable correspondingly. See second example - * below. - * - * Example use: - *
- *   smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
- *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
- *     printf("%d: %s\n", cp_sl_idx, cp);
- *     tor_free(cp);
- *   } SMARTLIST_FOREACH_END(cp);
- *   smartlist_free(list);
- * 
- * - * Example use (advanced): - *
- *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
- *     if (!strcmp(cp, "junk")) {
- *       tor_free(cp);
- *       SMARTLIST_DEL_CURRENT(list, cp);
- *     }
- *   } SMARTLIST_FOREACH_END(cp);
- * 
- */ -/* Note: these macros use token pasting, and reach into smartlist internals. - * This can make them a little daunting. Here's the approximate unpacking of - * the above examples, for entertainment value: - * - *
- * smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
- * {
- *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
- *   char *cp;
- *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
- *     cp = smartlist_get(list, cp_sl_idx);
- *     printf("%d: %s\n", cp_sl_idx, cp);
- *     tor_free(cp);
- *   }
- * }
- * smartlist_free(list);
- * 
- * - *
- * {
- *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
- *   char *cp;
- *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
- *     cp = smartlist_get(list, cp_sl_idx);
- *     if (!strcmp(cp, "junk")) {
- *       tor_free(cp);
- *       smartlist_del(list, cp_sl_idx);
- *       --cp_sl_idx;
- *       --cp_sl_len;
- *     }
- *   }
- * }
- * 
- */ -#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \ - STMT_BEGIN \ - int var ## _sl_idx, var ## _sl_len=(sl)->num_used; \ - type var; \ - for (var ## _sl_idx = 0; var ## _sl_idx < var ## _sl_len; \ - ++var ## _sl_idx) { \ - var = (sl)->list[var ## _sl_idx]; - -#define SMARTLIST_FOREACH_END(var) \ - var = NULL; \ - (void) var ## _sl_idx; \ - } STMT_END - -/** - * An alias for SMARTLIST_FOREACH_BEGIN and SMARTLIST_FOREACH_END, using - * cmd as the loop body. This wrapper is here for convenience with - * very short loops. - * - * By convention, we do not use this for loops which nest, or for loops over - * 10 lines or so. Use SMARTLIST_FOREACH_{BEGIN,END} for those. - */ -#define SMARTLIST_FOREACH(sl, type, var, cmd) \ - SMARTLIST_FOREACH_BEGIN(sl,type,var) { \ - cmd; \ - } SMARTLIST_FOREACH_END(var) - -/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed - * with the variable var, remove the current element in a way that - * won't confuse the loop. */ -#define SMARTLIST_DEL_CURRENT(sl, var) \ - STMT_BEGIN \ - smartlist_del(sl, var ## _sl_idx); \ - --var ## _sl_idx; \ - --var ## _sl_len; \ - STMT_END - -/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed - * with the variable var, remove the current element in a way that - * won't confuse the loop. */ -#define SMARTLIST_DEL_CURRENT_KEEPORDER(sl, var) \ - STMT_BEGIN \ - smartlist_del_keeporder(sl, var ## _sl_idx); \ - --var ## _sl_idx; \ - --var ## _sl_len; \ - STMT_END - -/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed - * with the variable var, replace the current element with val. - * Does not deallocate the current value of var. - */ -#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \ - STMT_BEGIN \ - smartlist_set(sl, var ## _sl_idx, val); \ - STMT_END - /* Helper: Given two lists of items, possibly of different types, such that * both lists are sorted on some common field (as determined by a comparison * expression cmpexpr), and such that one list (sl1) has no diff --git a/src/lib/log/.may_include b/src/lib/log/.may_include index 36a164cce0..852173aab3 100644 --- a/src/lib/log/.may_include +++ b/src/lib/log/.may_include @@ -1,7 +1,7 @@ orconfig.h lib/cc/*.h -lib/container/smartlist.h +lib/smartlist_core/*.h lib/err/*.h lib/fdio/*.h lib/intmath/*.h diff --git a/src/lib/log/torlog.c b/src/lib/log/torlog.c index 5709dd8199..bffffbf1bd 100644 --- a/src/lib/log/torlog.c +++ b/src/lib/log/torlog.c @@ -5,7 +5,7 @@ /* See LICENSE for licensing information */ /** - * \file log.c + * \file torlog.c * \brief Functions to send messages to log files or the console. **/ @@ -34,7 +34,9 @@ #include "lib/log/torlog.h" #include "lib/log/ratelim.h" #include "lib/lock/compat_mutex.h" -#include "lib/container/smartlist.h" +#include "lib/smartlist_core/smartlist_core.h" +#include "lib/smartlist_core/smartlist_foreach.h" +#include "lib/smartlist_core/smartlist_split.h" #include "lib/err/torerr.h" #include "lib/intmath/bits.h" #include "lib/string/compat_string.h" diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c index 161b65e0bf..78af08f022 100644 --- a/src/lib/log/util_bug.c +++ b/src/lib/log/util_bug.c @@ -12,7 +12,8 @@ #include "lib/log/torlog.h" #include "lib/err/backtrace.h" #ifdef TOR_UNIT_TESTS -#include "lib/container/smartlist.h" +#include "lib/smartlist_core/smartlist_core.h" +#include "lib/smartlist_core/smartlist_foreach.h" #endif #include "lib/malloc/util_malloc.h" #include "lib/string/printf.h" diff --git a/src/lib/smartlist_core/.may_include b/src/lib/smartlist_core/.may_include new file mode 100644 index 0000000000..a8507761a4 --- /dev/null +++ b/src/lib/smartlist_core/.may_include @@ -0,0 +1,7 @@ +orconfig.h +lib/cc/*.h +lib/malloc/*.h +lib/err/*.h +lib/string/*.h +lib/smartlist_core/*.h +lib/testsupport/testsupport.h diff --git a/src/lib/smartlist_core/include.am b/src/lib/smartlist_core/include.am new file mode 100644 index 0000000000..9ee93fdee7 --- /dev/null +++ b/src/lib/smartlist_core/include.am @@ -0,0 +1,21 @@ + +noinst_LIBRARIES += src/lib/libtor-smartlist-core.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-smartlist-core-testing.a +endif + +src_lib_libtor_smartlist_core_a_SOURCES = \ + src/lib/smartlist_core/smartlist_core.c \ + src/lib/smartlist_core/smartlist_split.c + +src_lib_libtor_smartlist_core_testing_a_SOURCES = \ + $(src_lib_libtor_smartlist_core_a_SOURCES) +src_lib_libtor_smartlist_core_testing_a_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_smartlist_core_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +noinst_HEADERS += \ + src/lib/smartlist_core/smartlist_core.h \ + src/lib/smartlist_core/smartlist_foreach.h \ + src/lib/smartlist_core/smartlist_split.h diff --git a/src/lib/smartlist_core/smartlist_core.c b/src/lib/smartlist_core/smartlist_core.c new file mode 100644 index 0000000000..b9c5f728ce --- /dev/null +++ b/src/lib/smartlist_core/smartlist_core.c @@ -0,0 +1,234 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file smartlist_core.c + * \brief Implements the core functionality of a smartlist (a resizeable + * dynamic array). For more functionality and helper functions, see the + * container library. + **/ + +#include "lib/err/torerr.h" +#include "lib/malloc/util_malloc.h" +#include "lib/smartlist_core/smartlist_core.h" + +#include +#include + +/** All newly allocated smartlists have this capacity. */ +#define SMARTLIST_DEFAULT_CAPACITY 16 + +/** Allocate and return an empty smartlist. + */ +MOCK_IMPL(smartlist_t *, +smartlist_new,(void)) +{ + smartlist_t *sl = tor_malloc(sizeof(smartlist_t)); + sl->num_used = 0; + sl->capacity = SMARTLIST_DEFAULT_CAPACITY; + sl->list = tor_calloc(sizeof(void *), sl->capacity); + return sl; +} + +/** Deallocate a smartlist. Does not release storage associated with the + * list's elements. + */ +MOCK_IMPL(void, +smartlist_free_,(smartlist_t *sl)) +{ + if (!sl) + return; + tor_free(sl->list); + tor_free(sl); +} + +/** Remove all elements from the list. + */ +void +smartlist_clear(smartlist_t *sl) +{ + memset(sl->list, 0, sizeof(void *) * sl->num_used); + sl->num_used = 0; +} + +#if SIZE_MAX < INT_MAX +#error "We don't support systems where size_t is smaller than int." +#endif + +/** Make sure that sl can hold at least size entries. */ +static inline void +smartlist_ensure_capacity(smartlist_t *sl, size_t size) +{ + /* Set MAX_CAPACITY to MIN(INT_MAX, SIZE_MAX / sizeof(void*)) */ +#if (SIZE_MAX/SIZEOF_VOID_P) > INT_MAX +#define MAX_CAPACITY (INT_MAX) +#else +#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*)))) +#endif + + raw_assert(size <= MAX_CAPACITY); + + if (size > (size_t) sl->capacity) { + size_t higher = (size_t) sl->capacity; + if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) { + higher = MAX_CAPACITY; + } else { + while (size > higher) + higher *= 2; + } + sl->list = tor_reallocarray(sl->list, sizeof(void *), + ((size_t)higher)); + memset(sl->list + sl->capacity, 0, + sizeof(void *) * (higher - sl->capacity)); + sl->capacity = (int) higher; + } +#undef ASSERT_CAPACITY +#undef MAX_CAPACITY +} + +/** Append element to the end of the list. */ +void +smartlist_add(smartlist_t *sl, void *element) +{ + smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1); + sl->list[sl->num_used++] = element; +} + +/** Append each element from S2 to the end of S1. */ +void +smartlist_add_all(smartlist_t *s1, const smartlist_t *s2) +{ + size_t new_size = (size_t)s1->num_used + (size_t)s2->num_used; + raw_assert(new_size >= (size_t) s1->num_used); /* check for overflow. */ + smartlist_ensure_capacity(s1, new_size); + memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*)); + raw_assert(new_size <= INT_MAX); /* redundant. */ + s1->num_used = (int) new_size; +} + +/** Append a copy of string to sl */ +void +smartlist_add_strdup(struct smartlist_t *sl, const char *string) +{ + char *copy; + + copy = tor_strdup(string); + + smartlist_add(sl, copy); +} + +/** Remove all elements E from sl such that E==element. Preserve + * the order of any elements before E, but elements after E can be + * rearranged. + */ +void +smartlist_remove(smartlist_t *sl, const void *element) +{ + int i; + if (element == NULL) + return; + for (i=0; i < sl->num_used; i++) + if (sl->list[i] == element) { + sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ + i--; /* so we process the new i'th element */ + sl->list[sl->num_used] = NULL; + } +} + +/** As smartlist_remove, but do not change the order of + * any elements not removed */ +void +smartlist_remove_keeporder(smartlist_t *sl, const void *element) +{ + int i, j, num_used_orig = sl->num_used; + if (element == NULL) + return; + + for (i=j=0; j < num_used_orig; ++j) { + if (sl->list[j] == element) { + --sl->num_used; + } else { + sl->list[i++] = sl->list[j]; + } + } +} + +/** If sl is nonempty, remove and return the final element. Otherwise, + * return NULL. */ +void * +smartlist_pop_last(smartlist_t *sl) +{ + raw_assert(sl); + if (sl->num_used) { + void *tmp = sl->list[--sl->num_used]; + sl->list[sl->num_used] = NULL; + return tmp; + } else + return NULL; +} + +/** Return true iff some element E of sl has E==element. + */ +int +smartlist_contains(const smartlist_t *sl, const void *element) +{ + int i; + for (i=0; i < sl->num_used; i++) + if (sl->list[i] == element) + return 1; + return 0; +} + +/** Remove the idxth element of sl; if idx is not the last + * element, swap the last element of sl into the idxth space. + */ +void +smartlist_del(smartlist_t *sl, int idx) +{ + raw_assert(sl); + raw_assert(idx>=0); + raw_assert(idx < sl->num_used); + sl->list[idx] = sl->list[--sl->num_used]; + sl->list[sl->num_used] = NULL; +} + +/** Remove the idxth element of sl; if idx is not the last element, + * moving all subsequent elements back one space. Return the old value + * of the idxth element. + */ +void +smartlist_del_keeporder(smartlist_t *sl, int idx) +{ + raw_assert(sl); + raw_assert(idx>=0); + raw_assert(idx < sl->num_used); + --sl->num_used; + if (idx < sl->num_used) + memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx)); + sl->list[sl->num_used] = NULL; +} + +/** Insert the value val as the new idxth element of + * sl, moving all items previously at idx or later + * forward one space. + */ +void +smartlist_insert(smartlist_t *sl, int idx, void *val) +{ + raw_assert(sl); + raw_assert(idx>=0); + raw_assert(idx <= sl->num_used); + if (idx == sl->num_used) { + smartlist_add(sl, val); + } else { + smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1); + /* Move other elements away */ + if (idx < sl->num_used) + memmove(sl->list + idx + 1, sl->list + idx, + sizeof(void*)*(sl->num_used-idx)); + sl->num_used++; + sl->list[idx] = val; + } +} diff --git a/src/lib/smartlist_core/smartlist_core.h b/src/lib/smartlist_core/smartlist_core.h new file mode 100644 index 0000000000..b1adf2ebdb --- /dev/null +++ b/src/lib/smartlist_core/smartlist_core.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_SMARTLIST_CORE_H +#define TOR_SMARTLIST_CORE_H + +#include + +#include "lib/cc/compat_compiler.h" +#include "lib/cc/torint.h" +#include "lib/testsupport/testsupport.h" + +/** A resizeable list of pointers, with associated helpful functionality. + * + * The members of this struct are exposed only so that macros and inlines can + * use them; all access to smartlist internals should go through the functions + * and macros defined here. + **/ +typedef struct smartlist_t { + /** @{ */ + /** list has enough capacity to store exactly capacity elements + * before it needs to be resized. Only the first num_used (\<= + * capacity) elements point to valid data. + */ + void **list; + int num_used; + int capacity; + /** @} */ +} smartlist_t; + +MOCK_DECL(smartlist_t *, smartlist_new, (void)); +MOCK_DECL(void, smartlist_free_, (smartlist_t *sl)); +#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl)) + +void smartlist_clear(smartlist_t *sl); +void smartlist_add(smartlist_t *sl, void *element); +void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2); +void smartlist_add_strdup(struct smartlist_t *sl, const char *string); + +void smartlist_remove(smartlist_t *sl, const void *element); +void smartlist_remove_keeporder(smartlist_t *sl, const void *element); +void *smartlist_pop_last(smartlist_t *sl); + +int smartlist_contains(const smartlist_t *sl, const void *element); + +/* smartlist_choose() is defined in crypto.[ch] */ +#ifdef DEBUG_SMARTLIST +#include "lib/err/torerr.h" +#include +/** Return the number of items in sl. + */ +static inline int smartlist_len(const smartlist_t *sl); +static inline int smartlist_len(const smartlist_t *sl) { + raw_assert(sl); + return (sl)->num_used; +} +/** Return the idxth element of sl. + */ +static inline void *smartlist_get(const smartlist_t *sl, int idx); +static inline void *smartlist_get(const smartlist_t *sl, int idx) { + raw_assert(sl); + raw_assert(idx>=0); + raw_assert(sl->num_used > idx); + return sl->list[idx]; +} +static inline void smartlist_set(smartlist_t *sl, int idx, void *val) { + raw_assert(sl); + raw_assert(idx>=0); + raw_assert(sl->num_used > idx); + sl->list[idx] = val; +} +#else /* !(defined(DEBUG_SMARTLIST)) */ +#define smartlist_len(sl) ((sl)->num_used) +#define smartlist_get(sl, idx) ((sl)->list[idx]) +#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val)) +#endif /* defined(DEBUG_SMARTLIST) */ + +/** Exchange the elements at indices idx1 and idx2 of the + * smartlist sl. */ +static inline void smartlist_swap(smartlist_t *sl, int idx1, int idx2) +{ + if (idx1 != idx2) { + void *elt = smartlist_get(sl, idx1); + smartlist_set(sl, idx1, smartlist_get(sl, idx2)); + smartlist_set(sl, idx2, elt); + } +} + +void smartlist_del(smartlist_t *sl, int idx); +void smartlist_del_keeporder(smartlist_t *sl, int idx); +void smartlist_insert(smartlist_t *sl, int idx, void *val); + +#endif /* !defined(TOR_CONTAINER_H) */ diff --git a/src/lib/smartlist_core/smartlist_foreach.h b/src/lib/smartlist_core/smartlist_foreach.h new file mode 100644 index 0000000000..4bef36d99c --- /dev/null +++ b/src/lib/smartlist_core/smartlist_foreach.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_SMARTLIST_FOREACH_H +#define TOR_SMARTLIST_FOREACH_H + +/** Iterate over the items in a smartlist sl, in order. For each item, + * assign it to a new local variable of type type named var, and + * execute the statements inside the loop body. Inside the loop, the loop + * index can be accessed as var_sl_idx and the length of the list can + * be accessed as var_sl_len. + * + * NOTE: Do not change the length of the list while the loop is in progress, + * unless you adjust the _sl_len variable correspondingly. See second example + * below. + * + * Example use: + *
+ *   smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
+ *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
+ *     printf("%d: %s\n", cp_sl_idx, cp);
+ *     tor_free(cp);
+ *   } SMARTLIST_FOREACH_END(cp);
+ *   smartlist_free(list);
+ * 
+ * + * Example use (advanced): + *
+ *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
+ *     if (!strcmp(cp, "junk")) {
+ *       tor_free(cp);
+ *       SMARTLIST_DEL_CURRENT(list, cp);
+ *     }
+ *   } SMARTLIST_FOREACH_END(cp);
+ * 
+ */ +/* Note: these macros use token pasting, and reach into smartlist internals. + * This can make them a little daunting. Here's the approximate unpacking of + * the above examples, for entertainment value: + * + *
+ * smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
+ * {
+ *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
+ *   char *cp;
+ *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
+ *     cp = smartlist_get(list, cp_sl_idx);
+ *     printf("%d: %s\n", cp_sl_idx, cp);
+ *     tor_free(cp);
+ *   }
+ * }
+ * smartlist_free(list);
+ * 
+ * + *
+ * {
+ *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
+ *   char *cp;
+ *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
+ *     cp = smartlist_get(list, cp_sl_idx);
+ *     if (!strcmp(cp, "junk")) {
+ *       tor_free(cp);
+ *       smartlist_del(list, cp_sl_idx);
+ *       --cp_sl_idx;
+ *       --cp_sl_len;
+ *     }
+ *   }
+ * }
+ * 
+ */ +#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \ + STMT_BEGIN \ + int var ## _sl_idx, var ## _sl_len=(sl)->num_used; \ + type var; \ + for (var ## _sl_idx = 0; var ## _sl_idx < var ## _sl_len; \ + ++var ## _sl_idx) { \ + var = (sl)->list[var ## _sl_idx]; + +#define SMARTLIST_FOREACH_END(var) \ + var = NULL; \ + (void) var ## _sl_idx; \ + } STMT_END + +/** + * An alias for SMARTLIST_FOREACH_BEGIN and SMARTLIST_FOREACH_END, using + * cmd as the loop body. This wrapper is here for convenience with + * very short loops. + * + * By convention, we do not use this for loops which nest, or for loops over + * 10 lines or so. Use SMARTLIST_FOREACH_{BEGIN,END} for those. + */ +#define SMARTLIST_FOREACH(sl, type, var, cmd) \ + SMARTLIST_FOREACH_BEGIN(sl,type,var) { \ + cmd; \ + } SMARTLIST_FOREACH_END(var) + +/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed + * with the variable var, remove the current element in a way that + * won't confuse the loop. */ +#define SMARTLIST_DEL_CURRENT(sl, var) \ + STMT_BEGIN \ + smartlist_del(sl, var ## _sl_idx); \ + --var ## _sl_idx; \ + --var ## _sl_len; \ + STMT_END + +/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed + * with the variable var, remove the current element in a way that + * won't confuse the loop. */ +#define SMARTLIST_DEL_CURRENT_KEEPORDER(sl, var) \ + STMT_BEGIN \ + smartlist_del_keeporder(sl, var ## _sl_idx); \ + --var ## _sl_idx; \ + --var ## _sl_len; \ + STMT_END + +/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed + * with the variable var, replace the current element with val. + * Does not deallocate the current value of var. + */ +#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \ + STMT_BEGIN \ + smartlist_set(sl, var ## _sl_idx, val); \ + STMT_END + +#endif /* !defined(TOR_SMARTLIST_FOREACH_H) */ diff --git a/src/lib/smartlist_core/smartlist_split.c b/src/lib/smartlist_core/smartlist_split.c new file mode 100644 index 0000000000..b9340e7924 --- /dev/null +++ b/src/lib/smartlist_core/smartlist_split.c @@ -0,0 +1,87 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "lib/smartlist_core/smartlist_core.h" +#include "lib/smartlist_core/smartlist_split.h" + +#include "lib/err/torerr.h" +#include "lib/string/util_string.h" +#include "lib/string/compat_ctype.h" +#include "lib/malloc/util_malloc.h" + +#include + +/** + * Split a string str along all occurrences of sep, + * appending the (newly allocated) split strings, in order, to + * sl. Return the number of strings added to sl. + * + * If flags&SPLIT_SKIP_SPACE is true, remove initial and + * trailing space from each entry. + * If flags&SPLIT_IGNORE_BLANK is true, remove any entries + * of length 0. + * If flags&SPLIT_STRIP_SPACE is true, strip spaces from each + * split string. + * + * If max\>0, divide the string into no more than max pieces. If + * sep is NULL, split on any sequence of horizontal space. + */ +int +smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, + int flags, int max) +{ + const char *cp, *end, *next; + int n = 0; + + raw_assert(sl); + raw_assert(str); + + cp = str; + while (1) { + if (flags&SPLIT_SKIP_SPACE) { + while (TOR_ISSPACE(*cp)) ++cp; + } + + if (max>0 && n == max-1) { + end = strchr(cp,'\0'); + } else if (sep) { + end = strstr(cp,sep); + if (!end) + end = strchr(cp,'\0'); + } else { + for (end = cp; *end && *end != '\t' && *end != ' '; ++end) + ; + } + + raw_assert(end); + + if (!*end) { + next = NULL; + } else if (sep) { + next = end+strlen(sep); + } else { + next = end+1; + while (*next == '\t' || *next == ' ') + ++next; + } + + if (flags&SPLIT_SKIP_SPACE) { + while (end > cp && TOR_ISSPACE(*(end-1))) + --end; + } + if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) { + char *string = tor_strndup(cp, end-cp); + if (flags&SPLIT_STRIP_SPACE) + tor_strstrip(string, " "); + smartlist_add(sl, string); + ++n; + } + if (!next) + break; + cp = next; + } + + return n; +} diff --git a/src/lib/smartlist_core/smartlist_split.h b/src/lib/smartlist_core/smartlist_split.h new file mode 100644 index 0000000000..8ed2abafb8 --- /dev/null +++ b/src/lib/smartlist_core/smartlist_split.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_SMARTLIST_SPLIT_H +#define TOR_SMARTLIST_SPLIT_H + +#define SPLIT_SKIP_SPACE 0x01 +#define SPLIT_IGNORE_BLANK 0x02 +#define SPLIT_STRIP_SPACE 0x04 +int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, + int flags, int max); + +#endif diff --git a/src/rust/build.rs b/src/rust/build.rs index b43fbdceb7..5be093bf8c 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -155,6 +155,7 @@ pub fn main() { cfg.component("tor-lock"); cfg.component("tor-fdio"); cfg.component("tor-container-testing"); + cfg.component("tor-smartlist-core-testing"); cfg.component("tor-string-testing"); cfg.component("tor-malloc"); cfg.component("tor-wallclock");