Browse Source

Extract core part of smartlist code into its own library.

The smartlist_core library now contains only the parts of smartlists
that are needed for the logging library.  This resolves the
circularity between "container" and "log".

The "containers" library still uses the logging code, and has the
higher-level smartlist functions.
Nick Mathewson 5 years ago
parent
commit
b1de1e7a77

+ 2 - 0
.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

+ 4 - 2
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 \

+ 1 - 0
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

+ 1 - 0
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

+ 0 - 290
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 <stdlib.h>
 #include <string.h>
 
-/** 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 <b>sl</b> can hold at least <b>size</b> 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(<b>pattern</b>, <b>...</b>)
  * to <b>sl</b>. */
 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 <b>smartlist_remove</b>, 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 <b>sl</b> 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 <b>sl</b>. */
 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 <b>sl</b> has some element E such that
  * !strcmp(E,<b>element</b>)
  */
@@ -399,131 +234,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2)
     smartlist_remove(sl1, sl2->list[i]);
 }
 
-/** Remove the <b>idx</b>th element of sl; if idx is not the last
- * element, swap the last element of sl into the <b>idx</b>th 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 <b>idx</b>th element of sl; if idx is not the last element,
- * moving all subsequent elements back one space. Return the old value
- * of the <b>idx</b>th 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 <b>val</b> as the new <b>idx</b>th element of
- * <b>sl</b>, moving all items previously at <b>idx</b> 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 <b>str</b> along all occurrences of <b>sep</b>,
- * appending the (newly allocated) split strings, in order, to
- * <b>sl</b>.  Return the number of strings added to <b>sl</b>.
- *
- * If <b>flags</b>&amp;SPLIT_SKIP_SPACE is true, remove initial and
- * trailing space from each entry.
- * If <b>flags</b>&amp;SPLIT_IGNORE_BLANK is true, remove any entries
- * of length 0.
- * If <b>flags</b>&amp;SPLIT_STRIP_SPACE is true, strip spaces from each
- * split string.
- *
- * If <b>max</b>\>0, divide the string into no more than <b>max</b> pieces. If
- * <b>sep</b> 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 <b>sl</b>, in order, separated by <b>join</b>.  If
  * <b>terminate</b> is true, also terminate the string with <b>join</b>.

+ 3 - 204
src/lib/container/smartlist.h

@@ -6,50 +6,19 @@
 #ifndef TOR_SMARTLIST_H
 #define TOR_SMARTLIST_H
 
-#include <stddef.h>
 #include <stdarg.h>
 
-#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 {
-  /** @{ */
-  /** <b>list</b> has enough capacity to store exactly <b>capacity</b> elements
-   * before it needs to be resized.  Only the first <b>num_used</b> (\<=
-   * 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 <stdlib.h>
-/** 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 <b>idx</b>th 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 <b>idx1</b> and <b>idx2</b> of the
- * smartlist <b>sl</b>. */
-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 <b>sl</b>, in order.  For each item,
- * assign it to a new local variable of type <b>type</b> named <b>var</b>, and
- * execute the statements inside the loop body.  Inside the loop, the loop
- * index can be accessed as <b>var</b>_sl_idx and the length of the list can
- * be accessed as <b>var</b>_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:
- * <pre>
- *   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);
- * </pre>
- *
- * Example use (advanced):
- * <pre>
- *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
- *     if (!strcmp(cp, "junk")) {
- *       tor_free(cp);
- *       SMARTLIST_DEL_CURRENT(list, cp);
- *     }
- *   } SMARTLIST_FOREACH_END(cp);
- * </pre>
- */
-/* 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:
- *
- * <pre>
- * 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);
- * </pre>
- *
- * <pre>
- * {
- *   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;
- *     }
- *   }
- * }
- * </pre>
- */
-#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
- * <b>cmd</b> 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 <b>sl</b> indexed
- * with the variable <b>var</b>, 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 <b>sl</b> indexed
- * with the variable <b>var</b>, 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 <b>sl</b> indexed
- * with the variable <b>var</b>, replace the current element with <b>val</b>.
- * Does not deallocate the current value of <b>var</b>.
- */
-#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 <b>cmpexpr</b>), and such that one list (<b>sl1</b>) has no

+ 1 - 1
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

+ 4 - 2
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"

+ 2 - 1
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"

+ 7 - 0
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

+ 21 - 0
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

+ 237 - 0
src/lib/smartlist_core/smartlist_core.c

@@ -0,0 +1,237 @@
+/* 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"
+
+#define tor_assert raw_assert
+
+#include <stdlib.h>
+#include <string.h>
+
+/** 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 <b>sl</b> can hold at least <b>size</b> 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);
+}
+
+/** 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 <b>smartlist_remove</b>, 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 <b>sl</b> 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;
+}
+
+
+/** 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 <b>idx</b>th element of sl; if idx is not the last
+ * element, swap the last element of sl into the <b>idx</b>th 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 <b>idx</b>th element of sl; if idx is not the last element,
+ * moving all subsequent elements back one space. Return the old value
+ * of the <b>idx</b>th 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 <b>val</b> as the new <b>idx</b>th element of
+ * <b>sl</b>, moving all items previously at <b>idx</b> 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;
+  }
+}

+ 95 - 0
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 <stddef.h>
+
+#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 {
+  /** @{ */
+  /** <b>list</b> has enough capacity to store exactly <b>capacity</b> elements
+   * before it needs to be resized.  Only the first <b>num_used</b> (\<=
+   * 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 <stdlib.h>
+/** 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 <b>idx</b>th 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 <b>idx1</b> and <b>idx2</b> of the
+ * smartlist <b>sl</b>. */
+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) */

+ 128 - 0
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 <b>sl</b>, in order.  For each item,
+ * assign it to a new local variable of type <b>type</b> named <b>var</b>, and
+ * execute the statements inside the loop body.  Inside the loop, the loop
+ * index can be accessed as <b>var</b>_sl_idx and the length of the list can
+ * be accessed as <b>var</b>_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:
+ * <pre>
+ *   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);
+ * </pre>
+ *
+ * Example use (advanced):
+ * <pre>
+ *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
+ *     if (!strcmp(cp, "junk")) {
+ *       tor_free(cp);
+ *       SMARTLIST_DEL_CURRENT(list, cp);
+ *     }
+ *   } SMARTLIST_FOREACH_END(cp);
+ * </pre>
+ */
+/* 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:
+ *
+ * <pre>
+ * 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);
+ * </pre>
+ *
+ * <pre>
+ * {
+ *   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;
+ *     }
+ *   }
+ * }
+ * </pre>
+ */
+#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
+ * <b>cmd</b> 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 <b>sl</b> indexed
+ * with the variable <b>var</b>, 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 <b>sl</b> indexed
+ * with the variable <b>var</b>, 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 <b>sl</b> indexed
+ * with the variable <b>var</b>, replace the current element with <b>val</b>.
+ * Does not deallocate the current value of <b>var</b>.
+ */
+#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \
+  STMT_BEGIN                                    \
+    smartlist_set(sl, var ## _sl_idx, val);     \
+  STMT_END
+
+#endif /* !defined(TOR_SMARTLIST_FOREACH_H) */

+ 89 - 0
src/lib/smartlist_core/smartlist_split.c

@@ -0,0 +1,89 @@
+/* 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 <string.h>
+
+#define tor_assert raw_assert
+
+/**
+ * Split a string <b>str</b> along all occurrences of <b>sep</b>,
+ * appending the (newly allocated) split strings, in order, to
+ * <b>sl</b>.  Return the number of strings added to <b>sl</b>.
+ *
+ * If <b>flags</b>&amp;SPLIT_SKIP_SPACE is true, remove initial and
+ * trailing space from each entry.
+ * If <b>flags</b>&amp;SPLIT_IGNORE_BLANK is true, remove any entries
+ * of length 0.
+ * If <b>flags</b>&amp;SPLIT_STRIP_SPACE is true, strip spaces from each
+ * split string.
+ *
+ * If <b>max</b>\>0, divide the string into no more than <b>max</b> pieces. If
+ * <b>sep</b> 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;
+}

+ 15 - 0
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