[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor/master] Split container.c based on container types, and minimize includes



commit 657ff55408d82f49fc9599cb662702cdc0995f08
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date:   Thu Jun 21 16:25:31 2018 -0400

    Split container.c based on container types, and minimize includes
    
    Minimizing includes revealed other places includes were necessary.
---
 src/common/util_bug.h                          |   1 +
 src/lib/container/bitarray.h                   |  80 +++
 src/lib/container/bloomfilt.c                  |  49 ++
 src/lib/container/bloomfilt.h                  |  58 ++
 src/lib/container/container.h                  | 745 +------------------------
 src/lib/container/include.am                   |  12 +-
 src/lib/container/map.c                        | 413 ++++++++++++++
 src/lib/container/map.h                        | 255 +++++++++
 src/lib/container/order.c                      |  51 ++
 src/lib/container/order.h                      |  54 ++
 src/lib/container/{container.c => smartlist.c} | 461 +--------------
 src/lib/container/smartlist.h                  | 351 ++++++++++++
 src/lib/malloc/util_malloc.c                   |   1 -
 src/lib/malloc/util_malloc.h                   |   2 +
 14 files changed, 1332 insertions(+), 1201 deletions(-)

diff --git a/src/common/util_bug.h b/src/common/util_bug.h
index 9659f59b7..fb5ab25c1 100644
--- a/src/common/util_bug.h
+++ b/src/common/util_bug.h
@@ -37,6 +37,7 @@
 #define TOR_UTIL_BUG_H
 
 #include "orconfig.h"
+#include <stdlib.h>
 #include "common/compat.h"
 #include "lib/testsupport/testsupport.h"
 
diff --git a/src/lib/container/bitarray.h b/src/lib/container/bitarray.h
new file mode 100644
index 000000000..2cea433fb
--- /dev/null
+++ b/src/lib/container/bitarray.h
@@ -0,0 +1,80 @@
+/* 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_BITARRAY_H
+#define TOR_BITARRAY_H
+
+#include "orconfig.h"
+#include <string.h>
+#include "lib/cc/torint.h"
+#include "lib/malloc/util_malloc.h"
+
+#if SIZEOF_INT == 4
+#define BITARRAY_SHIFT 5
+#elif SIZEOF_INT == 8
+#define BITARRAY_SHIFT 6
+#else
+#error "int is neither 4 nor 8 bytes. I can't deal with that."
+#endif /* SIZEOF_INT == 4 || ... */
+#define BITARRAY_MASK ((1u<<BITARRAY_SHIFT)-1)
+
+/** A random-access array of one-bit-wide elements. */
+typedef unsigned int bitarray_t;
+/** Create a new bit array that can hold <b>n_bits</b> bits. */
+static inline bitarray_t *
+bitarray_init_zero(unsigned int n_bits)
+{
+  /* round up to the next int. */
+  size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT;
+  return tor_calloc(sz, sizeof(unsigned int));
+}
+/** Expand <b>ba</b> from holding <b>n_bits_old</b> to <b>n_bits_new</b>,
+ * clearing all new bits.  Returns a possibly changed pointer to the
+ * bitarray. */
+static inline bitarray_t *
+bitarray_expand(bitarray_t *ba,
+                unsigned int n_bits_old, unsigned int n_bits_new)
+{
+  size_t sz_old = (n_bits_old+BITARRAY_MASK) >> BITARRAY_SHIFT;
+  size_t sz_new = (n_bits_new+BITARRAY_MASK) >> BITARRAY_SHIFT;
+  char *ptr;
+  if (sz_new <= sz_old)
+    return ba;
+  ptr = tor_reallocarray(ba, sz_new, sizeof(unsigned int));
+  /* This memset does nothing to the older excess bytes.  But they were
+   * already set to 0 by bitarry_init_zero. */
+  memset(ptr+sz_old*sizeof(unsigned int), 0,
+         (sz_new-sz_old)*sizeof(unsigned int));
+  return (bitarray_t*) ptr;
+}
+/** Free the bit array <b>ba</b>. */
+static inline void
+bitarray_free_(bitarray_t *ba)
+{
+  tor_free(ba);
+}
+#define bitarray_free(ba) FREE_AND_NULL(bitarray_t, bitarray_free_, (ba))
+
+/** Set the <b>bit</b>th bit in <b>b</b> to 1. */
+static inline void
+bitarray_set(bitarray_t *b, int bit)
+{
+  b[bit >> BITARRAY_SHIFT] |= (1u << (bit & BITARRAY_MASK));
+}
+/** Set the <b>bit</b>th bit in <b>b</b> to 0. */
+static inline void
+bitarray_clear(bitarray_t *b, int bit)
+{
+  b[bit >> BITARRAY_SHIFT] &= ~ (1u << (bit & BITARRAY_MASK));
+}
+/** Return true iff <b>bit</b>th bit in <b>b</b> is nonzero.  NOTE: does
+ * not necessarily return 1 on true. */
+static inline unsigned int
+bitarray_is_set(bitarray_t *b, int bit)
+{
+  return b[bit >> BITARRAY_SHIFT] & (1u << (bit & BITARRAY_MASK));
+}
+
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/container/bloomfilt.c b/src/lib/container/bloomfilt.c
new file mode 100644
index 000000000..4847408ee
--- /dev/null
+++ b/src/lib/container/bloomfilt.c
@@ -0,0 +1,49 @@
+/* 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 container.c
+ * \brief Implements a smartlist (a resizable array) along
+ * with helper functions to use smartlists.  Also includes
+ * hash table implementations of a string-to-void* map, and of
+ * a digest-to-void* map.
+ **/
+
+#include <stdlib.h>
+#include "lib/malloc/util_malloc.h"
+#include "lib/container/bloomfilt.h"
+#include "common/util.h" // For tor_log2
+
+/** Return a newly allocated digestset_t, optimized to hold a total of
+ * <b>max_elements</b> digests with a reasonably low false positive weight. */
+digestset_t *
+digestset_new(int max_elements)
+{
+  /* The probability of false positives is about P=(1 - exp(-kn/m))^k, where k
+   * is the number of hash functions per entry, m is the bits in the array,
+   * and n is the number of elements inserted.  For us, k==4, n<=max_elements,
+   * and m==n_bits= approximately max_elements*32.  This gives
+   *   P<(1-exp(-4*n/(32*n)))^4 == (1-exp(1/-8))^4 == .00019
+   *
+   * It would be more optimal in space vs false positives to get this false
+   * positive rate by going for k==13, and m==18.5n, but we also want to
+   * conserve CPU, and k==13 is pretty big.
+   */
+  int n_bits = 1u << (tor_log2(max_elements)+5);
+  digestset_t *r = tor_malloc(sizeof(digestset_t));
+  r->mask = n_bits - 1;
+  r->ba = bitarray_init_zero(n_bits);
+  return r;
+}
+
+/** Free all storage held in <b>set</b>. */
+void
+digestset_free_(digestset_t *set)
+{
+  if (!set)
+    return;
+  bitarray_free(set->ba);
+  tor_free(set);
+}
diff --git a/src/lib/container/bloomfilt.h b/src/lib/container/bloomfilt.h
new file mode 100644
index 000000000..adcdb10fc
--- /dev/null
+++ b/src/lib/container/bloomfilt.h
@@ -0,0 +1,58 @@
+/* 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_BLOOMFILT_H
+#define TOR_BLOOMFILT_H
+
+#include "orconfig.h"
+#include "lib/cc/torint.h"
+#include "lib/container/bitarray.h"
+#include "siphash.h"
+
+/** A set of digests, implemented as a Bloom filter. */
+typedef struct {
+  int mask; /**< One less than the number of bits in <b>ba</b>; always one less
+             * than a power of two. */
+  bitarray_t *ba; /**< A bit array to implement the Bloom filter. */
+} digestset_t;
+
+#define BIT(n) ((n) & set->mask)
+/** Add the digest <b>digest</b> to <b>set</b>. */
+static inline void
+digestset_add(digestset_t *set, const char *digest)
+{
+  const uint64_t x = siphash24g(digest, 20);
+  const uint32_t d1 = (uint32_t) x;
+  const uint32_t d2 = (uint32_t)( (x>>16) + x);
+  const uint32_t d3 = (uint32_t)( (x>>32) + x);
+  const uint32_t d4 = (uint32_t)( (x>>48) + x);
+  bitarray_set(set->ba, BIT(d1));
+  bitarray_set(set->ba, BIT(d2));
+  bitarray_set(set->ba, BIT(d3));
+  bitarray_set(set->ba, BIT(d4));
+}
+
+/** If <b>digest</b> is in <b>set</b>, return nonzero.  Otherwise,
+ * <em>probably</em> return zero. */
+static inline int
+digestset_contains(const digestset_t *set, const char *digest)
+{
+  const uint64_t x = siphash24g(digest, 20);
+  const uint32_t d1 = (uint32_t) x;
+  const uint32_t d2 = (uint32_t)( (x>>16) + x);
+  const uint32_t d3 = (uint32_t)( (x>>32) + x);
+  const uint32_t d4 = (uint32_t)( (x>>48) + x);
+  return bitarray_is_set(set->ba, BIT(d1)) &&
+         bitarray_is_set(set->ba, BIT(d2)) &&
+         bitarray_is_set(set->ba, BIT(d3)) &&
+         bitarray_is_set(set->ba, BIT(d4));
+}
+#undef BIT
+
+digestset_t *digestset_new(int max_elements);
+void digestset_free_(digestset_t* set);
+#define digestset_free(set) FREE_AND_NULL(digestset_t, digestset_free_, (set))
+
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/container/container.h b/src/lib/container/container.h
index c45bfc359..ba8983846 100644
--- a/src/lib/container/container.h
+++ b/src/lib/container/container.h
@@ -6,745 +6,10 @@
 #ifndef TOR_CONTAINER_H
 #define TOR_CONTAINER_H
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "lib/cc/compat_compiler.h"
-#include "lib/cc/torint.h"
-#include "lib/testsupport/testsupport.h"
-#include "lib/malloc/util_malloc.h"
-#include "common/util_bug.h"
-#include "siphash.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);
-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);
-int smartlist_contains_string_case(const smartlist_t *sl, const char *element);
-int smartlist_contains_int_as_string(const smartlist_t *sl, int num);
-int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2);
-int smartlist_contains_digest(const smartlist_t *sl, const char *element);
-int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2);
-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
-/** 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) {
-  tor_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) {
-  tor_assert(sl);
-  tor_assert(idx>=0);
-  tor_assert(sl->num_used > idx);
-  return sl->list[idx];
-}
-static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
-  tor_assert(sl);
-  tor_assert(idx>=0);
-  tor_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,
-                    int (*compare)(const void **a, const void **b),
-                    int *count_out);
-#define smartlist_get_most_frequent(sl, compare) \
-  smartlist_get_most_frequent_((sl), (compare), NULL)
-void smartlist_uniq(smartlist_t *sl,
-                    int (*compare)(const void **a, const void **b),
-                    void (*free_fn)(void *elt));
-
-void smartlist_sort_strings(smartlist_t *sl);
-void smartlist_sort_digests(smartlist_t *sl);
-void smartlist_sort_digests256(smartlist_t *sl);
-void smartlist_sort_pointers(smartlist_t *sl);
-
-const char *smartlist_get_most_frequent_string(smartlist_t *sl);
-const char *smartlist_get_most_frequent_string_(smartlist_t *sl,
-                                                int *count_out);
-const uint8_t *smartlist_get_most_frequent_digest256(smartlist_t *sl);
-
-void smartlist_uniq_strings(smartlist_t *sl);
-void smartlist_uniq_digests(smartlist_t *sl);
-void smartlist_uniq_digests256(smartlist_t *sl);
-void *smartlist_bsearch(smartlist_t *sl, const void *key,
-                        int (*compare)(const void *key, const void **member));
-int smartlist_bsearch_idx(const smartlist_t *sl, const void *key,
-                          int (*compare)(const void *key, const void **member),
-                          int *found_out);
-
-void smartlist_pqueue_add(smartlist_t *sl,
-                          int (*compare)(const void *a, const void *b),
-                          int idx_field_offset,
-                          void *item);
-void *smartlist_pqueue_pop(smartlist_t *sl,
-                           int (*compare)(const void *a, const void *b),
-                           int idx_field_offset);
-void smartlist_pqueue_remove(smartlist_t *sl,
-                             int (*compare)(const void *a, const void *b),
-                             int idx_field_offset,
-                             void *item);
-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
- * duplicates on the common field, loop through the lists in lockstep, and
- * execute <b>unmatched_var2</b> on items in var2 that do not appear in
- * var1.
- *
- * WARNING: It isn't safe to add remove elements from either list while the
- * loop is in progress.
- *
- * Example use:
- *  SMARTLIST_FOREACH_JOIN(routerstatus_list, routerstatus_t *, rs,
- *                     routerinfo_list, routerinfo_t *, ri,
- *                    tor_memcmp(rs->identity_digest, ri->identity_digest, 20),
- *                     log_info(LD_GENERAL,"No match for %s", ri->nickname)) {
- *    log_info(LD_GENERAL, "%s matches routerstatus %p", ri->nickname, rs);
- * } SMARTLIST_FOREACH_JOIN_END(rs, ri);
- **/
-/* The example above unpacks (approximately) to:
- *  int rs_sl_idx = 0, rs_sl_len = smartlist_len(routerstatus_list);
- *  int ri_sl_idx, ri_sl_len = smartlist_len(routerinfo_list);
- *  int rs_ri_cmp;
- *  routerstatus_t *rs;
- *  routerinfo_t *ri;
- *  for (; ri_sl_idx < ri_sl_len; ++ri_sl_idx) {
- *    ri = smartlist_get(routerinfo_list, ri_sl_idx);
- *    while (rs_sl_idx < rs_sl_len) {
- *      rs = smartlist_get(routerstatus_list, rs_sl_idx);
- *      rs_ri_cmp = tor_memcmp(rs->identity_digest, ri->identity_digest, 20);
- *      if (rs_ri_cmp > 0) {
- *        break;
- *      } else if (rs_ri_cmp == 0) {
- *        goto matched_ri;
- *      } else {
- *        ++rs_sl_idx;
- *      }
- *    }
- *    log_info(LD_GENERAL,"No match for %s", ri->nickname);
- *    continue;
- *   matched_ri: {
- *    log_info(LD_GENERAL,"%s matches with routerstatus %p",ri->nickname,rs);
- *    }
- *  }
- */
-#define SMARTLIST_FOREACH_JOIN(sl1, type1, var1, sl2, type2, var2,      \
-                                cmpexpr, unmatched_var2)                \
-  STMT_BEGIN                                                            \
-  int var1 ## _sl_idx = 0, var1 ## _sl_len=(sl1)->num_used;             \
-  int var2 ## _sl_idx = 0, var2 ## _sl_len=(sl2)->num_used;             \
-  int var1 ## _ ## var2 ## _cmp;                                        \
-  type1 var1;                                                           \
-  type2 var2;                                                           \
-  for (; var2##_sl_idx < var2##_sl_len; ++var2##_sl_idx) {              \
-    var2 = (sl2)->list[var2##_sl_idx];                                  \
-    while (var1##_sl_idx < var1##_sl_len) {                             \
-      var1 = (sl1)->list[var1##_sl_idx];                                \
-      var1##_##var2##_cmp = (cmpexpr);                                  \
-      if (var1##_##var2##_cmp > 0) {                                    \
-        break;                                                          \
-      } else if (var1##_##var2##_cmp == 0) {                            \
-        goto matched_##var2;                                            \
-      } else {                                                          \
-        ++var1##_sl_idx;                                                \
-      }                                                                 \
-    }                                                                   \
-    /* Ran out of v1, or no match for var2. */                          \
-    unmatched_var2;                                                     \
-    continue;                                                           \
-    matched_##var2: ;                                                   \
-
-#define SMARTLIST_FOREACH_JOIN_END(var1, var2)  \
-  }                                             \
-  STMT_END
-
-#define DECLARE_MAP_FNS(maptype, keytype, prefix)                       \
-  typedef struct maptype maptype;                                       \
-  typedef struct prefix##entry_t *prefix##iter_t;                       \
-  MOCK_DECL(maptype*, prefix##new, (void));                             \
-  void* prefix##set(maptype *map, keytype key, void *val);              \
-  void* prefix##get(const maptype *map, keytype key);                   \
-  void* prefix##remove(maptype *map, keytype key);                      \
-  MOCK_DECL(void, prefix##free_, (maptype *map, void (*free_val)(void*))); \
-  int prefix##isempty(const maptype *map);                              \
-  int prefix##size(const maptype *map);                                 \
-  prefix##iter_t *prefix##iter_init(maptype *map);                      \
-  prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter); \
-  prefix##iter_t *prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter); \
-  void prefix##iter_get(prefix##iter_t *iter, keytype *keyp, void **valp); \
-  int prefix##iter_done(prefix##iter_t *iter);                          \
-  void prefix##assert_ok(const maptype *map)
-
-/* Map from const char * to void *. Implemented with a hash table. */
-DECLARE_MAP_FNS(strmap_t, const char *, strmap_);
-/* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */
-DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_);
-/* Map from const uint8_t[DIGEST256_LEN] to void *. Implemented with a hash
- * table. */
-DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_);
-
-#define MAP_FREE_AND_NULL(maptype, map, fn)     \
-  do {                                          \
-    maptype ## _free_((map), (fn));             \
-    (map) = NULL;                               \
-  } while (0)
-
-#define strmap_free(map, fn) MAP_FREE_AND_NULL(strmap, (map), (fn))
-#define digestmap_free(map, fn) MAP_FREE_AND_NULL(digestmap, (map), (fn))
-#define digest256map_free(map, fn) MAP_FREE_AND_NULL(digest256map, (map), (fn))
-
-#undef DECLARE_MAP_FNS
-
-/** Iterates over the key-value pairs in a map <b>map</b> in order.
- * <b>prefix</b> is as for DECLARE_MAP_FNS (i.e., strmap_ or digestmap_).
- * The map's keys and values are of type keytype and valtype respectively;
- * each iteration assigns them to keyvar and valvar.
- *
- * Example use:
- *   MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) {
- *     // use k and r
- *   } MAP_FOREACH_END.
- */
-/* Unpacks to, approximately:
- * {
- *   digestmap_iter_t *k_iter;
- *   for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter);
- *        k_iter = digestmap_iter_next(m, k_iter)) {
- *     const char *k;
- *     void *r_voidp;
- *     routerinfo_t *r;
- *     digestmap_iter_get(k_iter, &k, &r_voidp);
- *     r = r_voidp;
- *     // use k and r
- *   }
- * }
- */
-#define MAP_FOREACH(prefix, map, keytype, keyvar, valtype, valvar)      \
-  STMT_BEGIN                                                            \
-    prefix##iter_t *keyvar##_iter;                                      \
-    for (keyvar##_iter = prefix##iter_init(map);                        \
-         !prefix##iter_done(keyvar##_iter);                             \
-         keyvar##_iter = prefix##iter_next(map, keyvar##_iter)) {       \
-      keytype keyvar;                                                   \
-      void *valvar##_voidp;                                             \
-      valtype valvar;                                                   \
-      prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp);        \
-      valvar = valvar##_voidp;
-
-/** As MAP_FOREACH, except allows members to be removed from the map
- * during the iteration via MAP_DEL_CURRENT.  Example use:
- *
- * Example use:
- *   MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) {
- *      if (is_very_old(r))
- *       MAP_DEL_CURRENT(k);
- *   } MAP_FOREACH_END.
- **/
-/* Unpacks to, approximately:
- * {
- *   digestmap_iter_t *k_iter;
- *   int k_del=0;
- *   for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter);
- *        k_iter = k_del ? digestmap_iter_next(m, k_iter)
- *                       : digestmap_iter_next_rmv(m, k_iter)) {
- *     const char *k;
- *     void *r_voidp;
- *     routerinfo_t *r;
- *     k_del=0;
- *     digestmap_iter_get(k_iter, &k, &r_voidp);
- *     r = r_voidp;
- *     if (is_very_old(r)) {
- *       k_del = 1;
- *     }
- *   }
- * }
- */
-#define MAP_FOREACH_MODIFY(prefix, map, keytype, keyvar, valtype, valvar) \
-  STMT_BEGIN                                                            \
-    prefix##iter_t *keyvar##_iter;                                      \
-    int keyvar##_del=0;                                                 \
-    for (keyvar##_iter = prefix##iter_init(map);                        \
-         !prefix##iter_done(keyvar##_iter);                             \
-         keyvar##_iter = keyvar##_del ?                                 \
-           prefix##iter_next_rmv(map, keyvar##_iter) :                  \
-           prefix##iter_next(map, keyvar##_iter)) {                     \
-      keytype keyvar;                                                   \
-      void *valvar##_voidp;                                             \
-      valtype valvar;                                                   \
-      keyvar##_del=0;                                                   \
-      prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp);        \
-      valvar = valvar##_voidp;
-
-/** Used with MAP_FOREACH_MODIFY to remove the currently-iterated-upon
- * member of the map.  */
-#define MAP_DEL_CURRENT(keyvar)                   \
-  STMT_BEGIN                                      \
-    keyvar##_del = 1;                             \
-  STMT_END
-
-/** Used to end a MAP_FOREACH() block. */
-#define MAP_FOREACH_END } STMT_END ;
-
-/** As MAP_FOREACH, but does not require declaration of prefix or keytype.
- * Example use:
- *   DIGESTMAP_FOREACH(m, k, routerinfo_t *, r) {
- *     // use k and r
- *   } DIGESTMAP_FOREACH_END.
- */
-#define DIGESTMAP_FOREACH(map, keyvar, valtype, valvar)                 \
-  MAP_FOREACH(digestmap_, map, const char *, keyvar, valtype, valvar)
-
-/** As MAP_FOREACH_MODIFY, but does not require declaration of prefix or
- * keytype.
- * Example use:
- *   DIGESTMAP_FOREACH_MODIFY(m, k, routerinfo_t *, r) {
- *      if (is_very_old(r))
- *       MAP_DEL_CURRENT(k);
- *   } DIGESTMAP_FOREACH_END.
- */
-#define DIGESTMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)          \
-  MAP_FOREACH_MODIFY(digestmap_, map, const char *, keyvar, valtype, valvar)
-/** Used to end a DIGESTMAP_FOREACH() block. */
-#define DIGESTMAP_FOREACH_END MAP_FOREACH_END
-
-#define DIGEST256MAP_FOREACH(map, keyvar, valtype, valvar)               \
-  MAP_FOREACH(digest256map_, map, const uint8_t *, keyvar, valtype, valvar)
-#define DIGEST256MAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)       \
-  MAP_FOREACH_MODIFY(digest256map_, map, const uint8_t *,               \
-                     keyvar, valtype, valvar)
-#define DIGEST256MAP_FOREACH_END MAP_FOREACH_END
-
-#define STRMAP_FOREACH(map, keyvar, valtype, valvar)                 \
-  MAP_FOREACH(strmap_, map, const char *, keyvar, valtype, valvar)
-#define STRMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)          \
-  MAP_FOREACH_MODIFY(strmap_, map, const char *, keyvar, valtype, valvar)
-#define STRMAP_FOREACH_END MAP_FOREACH_END
-
-void* strmap_set_lc(strmap_t *map, const char *key, void *val);
-void* strmap_get_lc(const strmap_t *map, const char *key);
-void* strmap_remove_lc(strmap_t *map, const char *key);
-
-#define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype)           \
-  typedef struct maptype maptype;                                       \
-  typedef struct prefix##iter_t *prefix##iter_t;                        \
-  ATTR_UNUSED static inline maptype*                                    \
-  prefix##new(void)                                                     \
-  {                                                                     \
-    return (maptype*)digestmap_new();                                   \
-  }                                                                     \
-  ATTR_UNUSED static inline digestmap_t*                                \
-  prefix##to_digestmap(maptype *map)                                    \
-  {                                                                     \
-    return (digestmap_t*)map;                                           \
-  }                                                                     \
-  ATTR_UNUSED static inline valtype*                                    \
-  prefix##get(maptype *map, const char *key)     \
-  {                                                                     \
-    return (valtype*)digestmap_get((digestmap_t*)map, key);             \
-  }                                                                     \
-  ATTR_UNUSED static inline valtype*                                    \
-  prefix##set(maptype *map, const char *key, valtype *val)              \
-  {                                                                     \
-    return (valtype*)digestmap_set((digestmap_t*)map, key, val);        \
-  }                                                                     \
-  ATTR_UNUSED static inline valtype*                                    \
-  prefix##remove(maptype *map, const char *key)                         \
-  {                                                                     \
-    return (valtype*)digestmap_remove((digestmap_t*)map, key);          \
-  }                                                                     \
-  ATTR_UNUSED static inline void                                        \
-  prefix##f##ree_(maptype *map, void (*free_val)(void*))                \
-  {                                                                     \
-    digestmap_free_((digestmap_t*)map, free_val);                       \
-  }                                                                     \
-  ATTR_UNUSED static inline int                                         \
-  prefix##isempty(maptype *map)                                         \
-  {                                                                     \
-    return digestmap_isempty((digestmap_t*)map);                        \
-  }                                                                     \
-  ATTR_UNUSED static inline int                                         \
-  prefix##size(maptype *map)                                            \
-  {                                                                     \
-    return digestmap_size((digestmap_t*)map);                           \
-  }                                                                     \
-  ATTR_UNUSED static inline                                             \
-  prefix##iter_t *prefix##iter_init(maptype *map)                       \
-  {                                                                     \
-    return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map);    \
-  }                                                                     \
-  ATTR_UNUSED static inline                                             \
-  prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter) \
-  {                                                                     \
-    return (prefix##iter_t*) digestmap_iter_next(                       \
-                       (digestmap_t*)map, (digestmap_iter_t*)iter);     \
-  }                                                                     \
-  ATTR_UNUSED static inline prefix##iter_t*                             \
-  prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter)             \
-  {                                                                     \
-    return (prefix##iter_t*) digestmap_iter_next_rmv(                   \
-                       (digestmap_t*)map, (digestmap_iter_t*)iter);     \
-  }                                                                     \
-  ATTR_UNUSED static inline void                                        \
-  prefix##iter_get(prefix##iter_t *iter,                                \
-                   const char **keyp,                                   \
-                   valtype **valp)                                      \
-  {                                                                     \
-    void *v;                                                            \
-    digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v);             \
-    *valp = v;                                                          \
-  }                                                                     \
-  ATTR_UNUSED static inline int                                         \
-  prefix##iter_done(prefix##iter_t *iter)                               \
-  {                                                                     \
-    return digestmap_iter_done((digestmap_iter_t*)iter);                \
-  }
-
-#if SIZEOF_INT == 4
-#define BITARRAY_SHIFT 5
-#elif SIZEOF_INT == 8
-#define BITARRAY_SHIFT 6
-#else
-#error "int is neither 4 nor 8 bytes. I can't deal with that."
-#endif /* SIZEOF_INT == 4 || ... */
-#define BITARRAY_MASK ((1u<<BITARRAY_SHIFT)-1)
-
-/** A random-access array of one-bit-wide elements. */
-typedef unsigned int bitarray_t;
-/** Create a new bit array that can hold <b>n_bits</b> bits. */
-static inline bitarray_t *
-bitarray_init_zero(unsigned int n_bits)
-{
-  /* round up to the next int. */
-  size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT;
-  return tor_calloc(sz, sizeof(unsigned int));
-}
-/** Expand <b>ba</b> from holding <b>n_bits_old</b> to <b>n_bits_new</b>,
- * clearing all new bits.  Returns a possibly changed pointer to the
- * bitarray. */
-static inline bitarray_t *
-bitarray_expand(bitarray_t *ba,
-                unsigned int n_bits_old, unsigned int n_bits_new)
-{
-  size_t sz_old = (n_bits_old+BITARRAY_MASK) >> BITARRAY_SHIFT;
-  size_t sz_new = (n_bits_new+BITARRAY_MASK) >> BITARRAY_SHIFT;
-  char *ptr;
-  if (sz_new <= sz_old)
-    return ba;
-  ptr = tor_reallocarray(ba, sz_new, sizeof(unsigned int));
-  /* This memset does nothing to the older excess bytes.  But they were
-   * already set to 0 by bitarry_init_zero. */
-  memset(ptr+sz_old*sizeof(unsigned int), 0,
-         (sz_new-sz_old)*sizeof(unsigned int));
-  return (bitarray_t*) ptr;
-}
-/** Free the bit array <b>ba</b>. */
-static inline void
-bitarray_free_(bitarray_t *ba)
-{
-  tor_free(ba);
-}
-#define bitarray_free(ba) FREE_AND_NULL(bitarray_t, bitarray_free_, (ba))
-
-/** Set the <b>bit</b>th bit in <b>b</b> to 1. */
-static inline void
-bitarray_set(bitarray_t *b, int bit)
-{
-  b[bit >> BITARRAY_SHIFT] |= (1u << (bit & BITARRAY_MASK));
-}
-/** Set the <b>bit</b>th bit in <b>b</b> to 0. */
-static inline void
-bitarray_clear(bitarray_t *b, int bit)
-{
-  b[bit >> BITARRAY_SHIFT] &= ~ (1u << (bit & BITARRAY_MASK));
-}
-/** Return true iff <b>bit</b>th bit in <b>b</b> is nonzero.  NOTE: does
- * not necessarily return 1 on true. */
-static inline unsigned int
-bitarray_is_set(bitarray_t *b, int bit)
-{
-  return b[bit >> BITARRAY_SHIFT] & (1u << (bit & BITARRAY_MASK));
-}
-
-/** A set of digests, implemented as a Bloom filter. */
-typedef struct {
-  int mask; /**< One less than the number of bits in <b>ba</b>; always one less
-             * than a power of two. */
-  bitarray_t *ba; /**< A bit array to implement the Bloom filter. */
-} digestset_t;
-
-#define BIT(n) ((n) & set->mask)
-/** Add the digest <b>digest</b> to <b>set</b>. */
-static inline void
-digestset_add(digestset_t *set, const char *digest)
-{
-  const uint64_t x = siphash24g(digest, 20);
-  const uint32_t d1 = (uint32_t) x;
-  const uint32_t d2 = (uint32_t)( (x>>16) + x);
-  const uint32_t d3 = (uint32_t)( (x>>32) + x);
-  const uint32_t d4 = (uint32_t)( (x>>48) + x);
-  bitarray_set(set->ba, BIT(d1));
-  bitarray_set(set->ba, BIT(d2));
-  bitarray_set(set->ba, BIT(d3));
-  bitarray_set(set->ba, BIT(d4));
-}
-
-/** If <b>digest</b> is in <b>set</b>, return nonzero.  Otherwise,
- * <em>probably</em> return zero. */
-static inline int
-digestset_contains(const digestset_t *set, const char *digest)
-{
-  const uint64_t x = siphash24g(digest, 20);
-  const uint32_t d1 = (uint32_t) x;
-  const uint32_t d2 = (uint32_t)( (x>>16) + x);
-  const uint32_t d3 = (uint32_t)( (x>>32) + x);
-  const uint32_t d4 = (uint32_t)( (x>>48) + x);
-  return bitarray_is_set(set->ba, BIT(d1)) &&
-         bitarray_is_set(set->ba, BIT(d2)) &&
-         bitarray_is_set(set->ba, BIT(d3)) &&
-         bitarray_is_set(set->ba, BIT(d4));
-}
-#undef BIT
-
-digestset_t *digestset_new(int max_elements);
-void digestset_free_(digestset_t* set);
-#define digestset_free(set) FREE_AND_NULL(digestset_t, digestset_free_, (set))
-
-/* These functions, given an <b>array</b> of <b>n_elements</b>, return the
- * <b>nth</b> lowest element. <b>nth</b>=0 gives the lowest element;
- * <b>n_elements</b>-1 gives the highest; and (<b>n_elements</b>-1) / 2 gives
- * the median.  As a side effect, the elements of <b>array</b> are sorted. */
-int find_nth_int(int *array, int n_elements, int nth);
-time_t find_nth_time(time_t *array, int n_elements, int nth);
-double find_nth_double(double *array, int n_elements, int nth);
-int32_t find_nth_int32(int32_t *array, int n_elements, int nth);
-uint32_t find_nth_uint32(uint32_t *array, int n_elements, int nth);
-long find_nth_long(long *array, int n_elements, int nth);
-static inline int
-median_int(int *array, int n_elements)
-{
-  return find_nth_int(array, n_elements, (n_elements-1)/2);
-}
-static inline time_t
-median_time(time_t *array, int n_elements)
-{
-  return find_nth_time(array, n_elements, (n_elements-1)/2);
-}
-static inline double
-median_double(double *array, int n_elements)
-{
-  return find_nth_double(array, n_elements, (n_elements-1)/2);
-}
-static inline uint32_t
-median_uint32(uint32_t *array, int n_elements)
-{
-  return find_nth_uint32(array, n_elements, (n_elements-1)/2);
-}
-static inline int32_t
-median_int32(int32_t *array, int n_elements)
-{
-  return find_nth_int32(array, n_elements, (n_elements-1)/2);
-}
-
-static inline uint32_t
-third_quartile_uint32(uint32_t *array, int n_elements)
-{
-  return find_nth_uint32(array, n_elements, (n_elements*3)/4);
-}
+#include "lib/container/smartlist.h"
+#include "lib/container/map.h"
+#include "lib/container/bitarray.h"
+#include "lib/container/bloomfilt.h"
+#include "lib/container/order.h"
 
 #endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/container/include.am b/src/lib/container/include.am
index d7648c80c..e2994674a 100644
--- a/src/lib/container/include.am
+++ b/src/lib/container/include.am
@@ -6,7 +6,10 @@ noinst_LIBRARIES += src/lib/libtor-container-testing.a
 endif
 
 src_lib_libtor_container_a_SOURCES =			\
-	src/lib/container/container.c
+	src/lib/container/bloomfilt.c			\
+	src/lib/container/map.c				\
+	src/lib/container/order.c			\
+	src/lib/container/smartlist.c
 
 src_lib_libtor_container_testing_a_SOURCES = \
 	$(src_lib_libtor_container_a_SOURCES)
@@ -14,4 +17,9 @@ src_lib_libtor_container_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
 src_lib_libtor_container_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 noinst_HEADERS +=					\
-	src/lib/container/container.h
+	src/lib/container/bitarray.h			\
+	src/lib/container/bloomfilt.h			\
+	src/lib/container/container.h			\
+	src/lib/container/map.h				\
+	src/lib/container/order.h			\
+	src/lib/container/smartlist.h
diff --git a/src/lib/container/map.c b/src/lib/container/map.c
new file mode 100644
index 000000000..227fb8f5e
--- /dev/null
+++ b/src/lib/container/map.c
@@ -0,0 +1,413 @@
+/* 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 container.c
+ * \brief Implements a smartlist (a resizable array) along
+ * with helper functions to use smartlists.  Also includes
+ * hash table implementations of a string-to-void* map, and of
+ * a digest-to-void* map.
+ **/
+
+#include "lib/container/map.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/crypt_ops/crypto_digest.h"
+
+#include "common/util_bug.h"
+#include "common/util.h" // For strlower
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ht.h"
+
+/** Helper: Declare an entry type and a map type to implement a mapping using
+ * ht.h.  The map type will be called <b>maptype</b>.  The key part of each
+ * entry is declared using the C declaration <b>keydecl</b>.  All functions
+ * and types associated with the map get prefixed with <b>prefix</b> */
+#define DEFINE_MAP_STRUCTS(maptype, keydecl, prefix)      \
+  typedef struct prefix ## entry_t {                      \
+    HT_ENTRY(prefix ## entry_t) node;                     \
+    void *val;                                            \
+    keydecl;                                              \
+  } prefix ## entry_t;                                    \
+  struct maptype {                                        \
+    HT_HEAD(prefix ## impl, prefix ## entry_t) head;      \
+  }
+
+DEFINE_MAP_STRUCTS(strmap_t, char *key, strmap_);
+DEFINE_MAP_STRUCTS(digestmap_t, char key[DIGEST_LEN], digestmap_);
+DEFINE_MAP_STRUCTS(digest256map_t, uint8_t key[DIGEST256_LEN], digest256map_);
+
+/** Helper: compare strmap_entry_t objects by key value. */
+static inline int
+strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b)
+{
+  return !strcmp(a->key, b->key);
+}
+
+/** Helper: return a hash value for a strmap_entry_t. */
+static inline unsigned int
+strmap_entry_hash(const strmap_entry_t *a)
+{
+  return (unsigned) siphash24g(a->key, strlen(a->key));
+}
+
+/** Helper: compare digestmap_entry_t objects by key value. */
+static inline int
+digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b)
+{
+  return tor_memeq(a->key, b->key, DIGEST_LEN);
+}
+
+/** Helper: return a hash value for a digest_map_t. */
+static inline unsigned int
+digestmap_entry_hash(const digestmap_entry_t *a)
+{
+  return (unsigned) siphash24g(a->key, DIGEST_LEN);
+}
+
+/** Helper: compare digestmap_entry_t objects by key value. */
+static inline int
+digest256map_entries_eq(const digest256map_entry_t *a,
+                        const digest256map_entry_t *b)
+{
+  return tor_memeq(a->key, b->key, DIGEST256_LEN);
+}
+
+/** Helper: return a hash value for a digest_map_t. */
+static inline unsigned int
+digest256map_entry_hash(const digest256map_entry_t *a)
+{
+  return (unsigned) siphash24g(a->key, DIGEST256_LEN);
+}
+
+HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
+             strmap_entries_eq)
+HT_GENERATE2(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
+             strmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+
+HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
+             digestmap_entries_eq)
+HT_GENERATE2(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
+             digestmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+
+HT_PROTOTYPE(digest256map_impl, digest256map_entry_t, node,
+             digest256map_entry_hash,
+             digest256map_entries_eq)
+HT_GENERATE2(digest256map_impl, digest256map_entry_t, node,
+             digest256map_entry_hash,
+             digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+
+#define strmap_entry_free(ent) \
+  FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent))
+#define digestmap_entry_free(ent) \
+  FREE_AND_NULL(digestmap_entry_t, digestmap_entry_free_, (ent))
+#define digest256map_entry_free(ent) \
+  FREE_AND_NULL(digest256map_entry_t, digest256map_entry_free_, (ent))
+
+static inline void
+strmap_entry_free_(strmap_entry_t *ent)
+{
+  tor_free(ent->key);
+  tor_free(ent);
+}
+static inline void
+digestmap_entry_free_(digestmap_entry_t *ent)
+{
+  tor_free(ent);
+}
+static inline void
+digest256map_entry_free_(digest256map_entry_t *ent)
+{
+  tor_free(ent);
+}
+
+static inline void
+strmap_assign_tmp_key(strmap_entry_t *ent, const char *key)
+{
+  ent->key = (char*)key;
+}
+static inline void
+digestmap_assign_tmp_key(digestmap_entry_t *ent, const char *key)
+{
+  memcpy(ent->key, key, DIGEST_LEN);
+}
+static inline void
+digest256map_assign_tmp_key(digest256map_entry_t *ent, const uint8_t *key)
+{
+  memcpy(ent->key, key, DIGEST256_LEN);
+}
+static inline void
+strmap_assign_key(strmap_entry_t *ent, const char *key)
+{
+  ent->key = tor_strdup(key);
+}
+static inline void
+digestmap_assign_key(digestmap_entry_t *ent, const char *key)
+{
+  memcpy(ent->key, key, DIGEST_LEN);
+}
+static inline void
+digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key)
+{
+  memcpy(ent->key, key, DIGEST256_LEN);
+}
+
+/**
+ * Macro: implement all the functions for a map that are declared in
+ * container.h by the DECLARE_MAP_FNS() macro.  You must additionally define a
+ * prefix_entry_free_() function to free entries (and their keys), a
+ * prefix_assign_tmp_key() function to temporarily set a stack-allocated
+ * entry to hold a key, and a prefix_assign_key() function to set a
+ * heap-allocated entry to hold a key.
+ */
+#define IMPLEMENT_MAP_FNS(maptype, keytype, prefix)                     \
+  /** Create and return a new empty map. */                             \
+  MOCK_IMPL(maptype *,                                                  \
+  prefix##_new,(void))                                                  \
+  {                                                                     \
+    maptype *result;                                                    \
+    result = tor_malloc(sizeof(maptype));                               \
+    HT_INIT(prefix##_impl, &result->head);                              \
+    return result;                                                      \
+  }                                                                     \
+                                                                        \
+  /** Return the item from <b>map</b> whose key matches <b>key</b>, or  \
+   * NULL if no such value exists. */                                   \
+  void *                                                                \
+  prefix##_get(const maptype *map, const keytype key)                   \
+  {                                                                     \
+    prefix ##_entry_t *resolve;                                         \
+    prefix ##_entry_t search;                                           \
+    tor_assert(map);                                                    \
+    tor_assert(key);                                                    \
+    prefix ##_assign_tmp_key(&search, key);                             \
+    resolve = HT_FIND(prefix ##_impl, &map->head, &search);             \
+    if (resolve) {                                                      \
+      return resolve->val;                                              \
+    } else {                                                            \
+      return NULL;                                                      \
+    }                                                                   \
+  }                                                                     \
+                                                                        \
+  /** Add an entry to <b>map</b> mapping <b>key</b> to <b>val</b>;      \
+   * return the previous value, or NULL if no such value existed. */     \
+  void *                                                                \
+  prefix##_set(maptype *map, const keytype key, void *val)              \
+  {                                                                     \
+    prefix##_entry_t search;                                            \
+    void *oldval;                                                       \
+    tor_assert(map);                                                    \
+    tor_assert(key);                                                    \
+    tor_assert(val);                                                    \
+    prefix##_assign_tmp_key(&search, key);                              \
+    /* We a lot of our time in this function, so the code below is */   \
+    /* meant to optimize the check/alloc/set cycle by avoiding the two */\
+    /* trips to the hash table that we would do in the unoptimized */   \
+    /* version of this code. (Each of HT_INSERT and HT_FIND calls */     \
+    /* HT_SET_HASH and HT_FIND_P.) */                                   \
+    HT_FIND_OR_INSERT_(prefix##_impl, node, prefix##_entry_hash,        \
+                       &(map->head),                                    \
+                       prefix##_entry_t, &search, ptr,                  \
+                       {                                                \
+                         /* we found an entry. */                       \
+                         oldval = (*ptr)->val;                          \
+                         (*ptr)->val = val;                             \
+                         return oldval;                                 \
+                       },                                               \
+                       {                                                \
+                         /* We didn't find the entry. */                \
+                         prefix##_entry_t *newent =                     \
+                           tor_malloc_zero(sizeof(prefix##_entry_t));   \
+                         prefix##_assign_key(newent, key);              \
+                         newent->val = val;                             \
+                         HT_FOI_INSERT_(node, &(map->head),             \
+                            &search, newent, ptr);                      \
+                         return NULL;                                   \
+    });                                                                 \
+  }                                                                     \
+                                                                        \
+  /** Remove the value currently associated with <b>key</b> from the map. \
+   * Return the value if one was set, or NULL if there was no entry for \
+   * <b>key</b>.                                                        \
+   *                                                                    \
+   * Note: you must free any storage associated with the returned value. \
+   */                                                                   \
+  void *                                                                \
+  prefix##_remove(maptype *map, const keytype key)                      \
+  {                                                                     \
+    prefix##_entry_t *resolve;                                          \
+    prefix##_entry_t search;                                            \
+    void *oldval;                                                       \
+    tor_assert(map);                                                    \
+    tor_assert(key);                                                    \
+    prefix##_assign_tmp_key(&search, key);                              \
+    resolve = HT_REMOVE(prefix##_impl, &map->head, &search);            \
+    if (resolve) {                                                      \
+      oldval = resolve->val;                                            \
+      prefix##_entry_free(resolve);                                     \
+      return oldval;                                                    \
+    } else {                                                            \
+      return NULL;                                                      \
+    }                                                                   \
+  }                                                                     \
+                                                                        \
+  /** Return the number of elements in <b>map</b>. */                   \
+  int                                                                   \
+  prefix##_size(const maptype *map)                                     \
+  {                                                                     \
+    return HT_SIZE(&map->head);                                         \
+  }                                                                     \
+                                                                        \
+  /** Return true iff <b>map</b> has no entries. */                     \
+  int                                                                   \
+  prefix##_isempty(const maptype *map)                                  \
+  {                                                                     \
+    return HT_EMPTY(&map->head);                                        \
+  }                                                                     \
+                                                                        \
+  /** Assert that <b>map</b> is not corrupt. */                         \
+  void                                                                  \
+  prefix##_assert_ok(const maptype *map)                                \
+  {                                                                     \
+    tor_assert(!prefix##_impl_HT_REP_IS_BAD_(&map->head));              \
+  }                                                                     \
+                                                                        \
+  /** Remove all entries from <b>map</b>, and deallocate storage for    \
+   * those entries.  If free_val is provided, invoked it every value in \
+   * <b>map</b>. */                                                     \
+  MOCK_IMPL(void,                                                       \
+  prefix##_free_, (maptype *map, void (*free_val)(void*)))              \
+  {                                                                     \
+    prefix##_entry_t **ent, **next, *this;                              \
+    if (!map)                                                           \
+      return;                                                           \
+    for (ent = HT_START(prefix##_impl, &map->head); ent != NULL;        \
+         ent = next) {                                                  \
+      this = *ent;                                                      \
+      next = HT_NEXT_RMV(prefix##_impl, &map->head, ent);               \
+      if (free_val)                                                     \
+        free_val(this->val);                                            \
+      prefix##_entry_free(this);                                        \
+    }                                                                   \
+    tor_assert(HT_EMPTY(&map->head));                                   \
+    HT_CLEAR(prefix##_impl, &map->head);                                \
+    tor_free(map);                                                      \
+  }                                                                     \
+                                                                        \
+  /** return an <b>iterator</b> pointer to the front of a map.          \
+   *                                                                    \
+   * Iterator example:                                                  \
+   *                                                                    \
+   * \code                                                              \
+   * // uppercase values in "map", removing empty values.               \
+   *                                                                    \
+   * strmap_iter_t *iter;                                               \
+   * const char *key;                                                   \
+   * void *val;                                                         \
+   * char *cp;                                                          \
+   *                                                                    \
+   * for (iter = strmap_iter_init(map); !strmap_iter_done(iter); ) {    \
+   *    strmap_iter_get(iter, &key, &val);                              \
+   *    cp = (char*)val;                                                \
+   *    if (!*cp) {                                                     \
+   *       iter = strmap_iter_next_rmv(map,iter);                       \
+   *       free(val);                                                   \
+   *    } else {                                                        \
+   *       for (;*cp;cp++) *cp = TOR_TOUPPER(*cp);                      \
+   */                                                                   \
+  prefix##_iter_t *                                                     \
+  prefix##_iter_init(maptype *map)                                      \
+  {                                                                     \
+    tor_assert(map);                                                    \
+    return HT_START(prefix##_impl, &map->head);                         \
+  }                                                                     \
+                                                                        \
+  /** Advance <b>iter</b> a single step to the next entry, and return   \
+   * its new value. */                                                  \
+  prefix##_iter_t *                                                     \
+  prefix##_iter_next(maptype *map, prefix##_iter_t *iter)               \
+  {                                                                     \
+    tor_assert(map);                                                    \
+    tor_assert(iter);                                                   \
+    return HT_NEXT(prefix##_impl, &map->head, iter);                    \
+  }                                                                     \
+  /** Advance <b>iter</b> a single step to the next entry, removing the \
+   * current entry, and return its new value. */                        \
+  prefix##_iter_t *                                                     \
+  prefix##_iter_next_rmv(maptype *map, prefix##_iter_t *iter)           \
+  {                                                                     \
+    prefix##_entry_t *rmv;                                              \
+    tor_assert(map);                                                    \
+    tor_assert(iter);                                                   \
+    tor_assert(*iter);                                                  \
+    rmv = *iter;                                                        \
+    iter = HT_NEXT_RMV(prefix##_impl, &map->head, iter);                \
+    prefix##_entry_free(rmv);                                           \
+    return iter;                                                        \
+  }                                                                     \
+  /** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed    \
+   * to by iter. */                                                     \
+  void                                                                  \
+  prefix##_iter_get(prefix##_iter_t *iter, const keytype *keyp,         \
+                    void **valp)                                        \
+  {                                                                     \
+    tor_assert(iter);                                                   \
+    tor_assert(*iter);                                                  \
+    tor_assert(keyp);                                                   \
+    tor_assert(valp);                                                   \
+    *keyp = (*iter)->key;                                               \
+    *valp = (*iter)->val;                                               \
+  }                                                                     \
+  /** Return true iff <b>iter</b> has advanced past the last entry of   \
+   * <b>map</b>. */                                                     \
+  int                                                                   \
+  prefix##_iter_done(prefix##_iter_t *iter)                             \
+  {                                                                     \
+    return iter == NULL;                                                \
+  }
+
+IMPLEMENT_MAP_FNS(strmap_t, char *, strmap)
+IMPLEMENT_MAP_FNS(digestmap_t, char *, digestmap)
+IMPLEMENT_MAP_FNS(digest256map_t, uint8_t *, digest256map)
+
+/** Same as strmap_set, but first converts <b>key</b> to lowercase. */
+void *
+strmap_set_lc(strmap_t *map, const char *key, void *val)
+{
+  /* We could be a little faster by using strcasecmp instead, and a separate
+   * type, but I don't think it matters. */
+  void *v;
+  char *lc_key = tor_strdup(key);
+  tor_strlower(lc_key);
+  v = strmap_set(map,lc_key,val);
+  tor_free(lc_key);
+  return v;
+}
+
+/** Same as strmap_get, but first converts <b>key</b> to lowercase. */
+void *
+strmap_get_lc(const strmap_t *map, const char *key)
+{
+  void *v;
+  char *lc_key = tor_strdup(key);
+  tor_strlower(lc_key);
+  v = strmap_get(map,lc_key);
+  tor_free(lc_key);
+  return v;
+}
+
+/** Same as strmap_remove, but first converts <b>key</b> to lowercase */
+void *
+strmap_remove_lc(strmap_t *map, const char *key)
+{
+  void *v;
+  char *lc_key = tor_strdup(key);
+  tor_strlower(lc_key);
+  v = strmap_remove(map,lc_key);
+  tor_free(lc_key);
+  return v;
+}
diff --git a/src/lib/container/map.h b/src/lib/container/map.h
new file mode 100644
index 000000000..ac21c2871
--- /dev/null
+++ b/src/lib/container/map.h
@@ -0,0 +1,255 @@
+/* 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_MAP_H
+#define TOR_MAP_H
+
+#include "lib/testsupport/testsupport.h"
+#include "lib/cc/torint.h"
+
+#include "siphash.h"
+
+#define DECLARE_MAP_FNS(maptype, keytype, prefix)                       \
+  typedef struct maptype maptype;                                       \
+  typedef struct prefix##entry_t *prefix##iter_t;                       \
+  MOCK_DECL(maptype*, prefix##new, (void));                             \
+  void* prefix##set(maptype *map, keytype key, void *val);              \
+  void* prefix##get(const maptype *map, keytype key);                   \
+  void* prefix##remove(maptype *map, keytype key);                      \
+  MOCK_DECL(void, prefix##free_, (maptype *map, void (*free_val)(void*))); \
+  int prefix##isempty(const maptype *map);                              \
+  int prefix##size(const maptype *map);                                 \
+  prefix##iter_t *prefix##iter_init(maptype *map);                      \
+  prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter); \
+  prefix##iter_t *prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter); \
+  void prefix##iter_get(prefix##iter_t *iter, keytype *keyp, void **valp); \
+  int prefix##iter_done(prefix##iter_t *iter);                          \
+  void prefix##assert_ok(const maptype *map)
+
+/* Map from const char * to void *. Implemented with a hash table. */
+DECLARE_MAP_FNS(strmap_t, const char *, strmap_);
+/* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */
+DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_);
+/* Map from const uint8_t[DIGEST256_LEN] to void *. Implemented with a hash
+ * table. */
+DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_);
+
+#define MAP_FREE_AND_NULL(maptype, map, fn)     \
+  do {                                          \
+    maptype ## _free_((map), (fn));             \
+    (map) = NULL;                               \
+  } while (0)
+
+#define strmap_free(map, fn) MAP_FREE_AND_NULL(strmap, (map), (fn))
+#define digestmap_free(map, fn) MAP_FREE_AND_NULL(digestmap, (map), (fn))
+#define digest256map_free(map, fn) MAP_FREE_AND_NULL(digest256map, (map), (fn))
+
+#undef DECLARE_MAP_FNS
+
+/** Iterates over the key-value pairs in a map <b>map</b> in order.
+ * <b>prefix</b> is as for DECLARE_MAP_FNS (i.e., strmap_ or digestmap_).
+ * The map's keys and values are of type keytype and valtype respectively;
+ * each iteration assigns them to keyvar and valvar.
+ *
+ * Example use:
+ *   MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) {
+ *     // use k and r
+ *   } MAP_FOREACH_END.
+ */
+/* Unpacks to, approximately:
+ * {
+ *   digestmap_iter_t *k_iter;
+ *   for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter);
+ *        k_iter = digestmap_iter_next(m, k_iter)) {
+ *     const char *k;
+ *     void *r_voidp;
+ *     routerinfo_t *r;
+ *     digestmap_iter_get(k_iter, &k, &r_voidp);
+ *     r = r_voidp;
+ *     // use k and r
+ *   }
+ * }
+ */
+#define MAP_FOREACH(prefix, map, keytype, keyvar, valtype, valvar)      \
+  STMT_BEGIN                                                            \
+    prefix##iter_t *keyvar##_iter;                                      \
+    for (keyvar##_iter = prefix##iter_init(map);                        \
+         !prefix##iter_done(keyvar##_iter);                             \
+         keyvar##_iter = prefix##iter_next(map, keyvar##_iter)) {       \
+      keytype keyvar;                                                   \
+      void *valvar##_voidp;                                             \
+      valtype valvar;                                                   \
+      prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp);        \
+      valvar = valvar##_voidp;
+
+/** As MAP_FOREACH, except allows members to be removed from the map
+ * during the iteration via MAP_DEL_CURRENT.  Example use:
+ *
+ * Example use:
+ *   MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) {
+ *      if (is_very_old(r))
+ *       MAP_DEL_CURRENT(k);
+ *   } MAP_FOREACH_END.
+ **/
+/* Unpacks to, approximately:
+ * {
+ *   digestmap_iter_t *k_iter;
+ *   int k_del=0;
+ *   for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter);
+ *        k_iter = k_del ? digestmap_iter_next(m, k_iter)
+ *                       : digestmap_iter_next_rmv(m, k_iter)) {
+ *     const char *k;
+ *     void *r_voidp;
+ *     routerinfo_t *r;
+ *     k_del=0;
+ *     digestmap_iter_get(k_iter, &k, &r_voidp);
+ *     r = r_voidp;
+ *     if (is_very_old(r)) {
+ *       k_del = 1;
+ *     }
+ *   }
+ * }
+ */
+#define MAP_FOREACH_MODIFY(prefix, map, keytype, keyvar, valtype, valvar) \
+  STMT_BEGIN                                                            \
+    prefix##iter_t *keyvar##_iter;                                      \
+    int keyvar##_del=0;                                                 \
+    for (keyvar##_iter = prefix##iter_init(map);                        \
+         !prefix##iter_done(keyvar##_iter);                             \
+         keyvar##_iter = keyvar##_del ?                                 \
+           prefix##iter_next_rmv(map, keyvar##_iter) :                  \
+           prefix##iter_next(map, keyvar##_iter)) {                     \
+      keytype keyvar;                                                   \
+      void *valvar##_voidp;                                             \
+      valtype valvar;                                                   \
+      keyvar##_del=0;                                                   \
+      prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp);        \
+      valvar = valvar##_voidp;
+
+/** Used with MAP_FOREACH_MODIFY to remove the currently-iterated-upon
+ * member of the map.  */
+#define MAP_DEL_CURRENT(keyvar)                   \
+  STMT_BEGIN                                      \
+    keyvar##_del = 1;                             \
+  STMT_END
+
+/** Used to end a MAP_FOREACH() block. */
+#define MAP_FOREACH_END } STMT_END ;
+
+/** As MAP_FOREACH, but does not require declaration of prefix or keytype.
+ * Example use:
+ *   DIGESTMAP_FOREACH(m, k, routerinfo_t *, r) {
+ *     // use k and r
+ *   } DIGESTMAP_FOREACH_END.
+ */
+#define DIGESTMAP_FOREACH(map, keyvar, valtype, valvar)                 \
+  MAP_FOREACH(digestmap_, map, const char *, keyvar, valtype, valvar)
+
+/** As MAP_FOREACH_MODIFY, but does not require declaration of prefix or
+ * keytype.
+ * Example use:
+ *   DIGESTMAP_FOREACH_MODIFY(m, k, routerinfo_t *, r) {
+ *      if (is_very_old(r))
+ *       MAP_DEL_CURRENT(k);
+ *   } DIGESTMAP_FOREACH_END.
+ */
+#define DIGESTMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)          \
+  MAP_FOREACH_MODIFY(digestmap_, map, const char *, keyvar, valtype, valvar)
+/** Used to end a DIGESTMAP_FOREACH() block. */
+#define DIGESTMAP_FOREACH_END MAP_FOREACH_END
+
+#define DIGEST256MAP_FOREACH(map, keyvar, valtype, valvar)               \
+  MAP_FOREACH(digest256map_, map, const uint8_t *, keyvar, valtype, valvar)
+#define DIGEST256MAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)       \
+  MAP_FOREACH_MODIFY(digest256map_, map, const uint8_t *,               \
+                     keyvar, valtype, valvar)
+#define DIGEST256MAP_FOREACH_END MAP_FOREACH_END
+
+#define STRMAP_FOREACH(map, keyvar, valtype, valvar)                 \
+  MAP_FOREACH(strmap_, map, const char *, keyvar, valtype, valvar)
+#define STRMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)          \
+  MAP_FOREACH_MODIFY(strmap_, map, const char *, keyvar, valtype, valvar)
+#define STRMAP_FOREACH_END MAP_FOREACH_END
+
+void* strmap_set_lc(strmap_t *map, const char *key, void *val);
+void* strmap_get_lc(const strmap_t *map, const char *key);
+void* strmap_remove_lc(strmap_t *map, const char *key);
+
+#define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype)           \
+  typedef struct maptype maptype;                                       \
+  typedef struct prefix##iter_t *prefix##iter_t;                        \
+  ATTR_UNUSED static inline maptype*                                    \
+  prefix##new(void)                                                     \
+  {                                                                     \
+    return (maptype*)digestmap_new();                                   \
+  }                                                                     \
+  ATTR_UNUSED static inline digestmap_t*                                \
+  prefix##to_digestmap(maptype *map)                                    \
+  {                                                                     \
+    return (digestmap_t*)map;                                           \
+  }                                                                     \
+  ATTR_UNUSED static inline valtype*                                    \
+  prefix##get(maptype *map, const char *key)     \
+  {                                                                     \
+    return (valtype*)digestmap_get((digestmap_t*)map, key);             \
+  }                                                                     \
+  ATTR_UNUSED static inline valtype*                                    \
+  prefix##set(maptype *map, const char *key, valtype *val)              \
+  {                                                                     \
+    return (valtype*)digestmap_set((digestmap_t*)map, key, val);        \
+  }                                                                     \
+  ATTR_UNUSED static inline valtype*                                    \
+  prefix##remove(maptype *map, const char *key)                         \
+  {                                                                     \
+    return (valtype*)digestmap_remove((digestmap_t*)map, key);          \
+  }                                                                     \
+  ATTR_UNUSED static inline void                                        \
+  prefix##f##ree_(maptype *map, void (*free_val)(void*))                \
+  {                                                                     \
+    digestmap_free_((digestmap_t*)map, free_val);                       \
+  }                                                                     \
+  ATTR_UNUSED static inline int                                         \
+  prefix##isempty(maptype *map)                                         \
+  {                                                                     \
+    return digestmap_isempty((digestmap_t*)map);                        \
+  }                                                                     \
+  ATTR_UNUSED static inline int                                         \
+  prefix##size(maptype *map)                                            \
+  {                                                                     \
+    return digestmap_size((digestmap_t*)map);                           \
+  }                                                                     \
+  ATTR_UNUSED static inline                                             \
+  prefix##iter_t *prefix##iter_init(maptype *map)                       \
+  {                                                                     \
+    return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map);    \
+  }                                                                     \
+  ATTR_UNUSED static inline                                             \
+  prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter) \
+  {                                                                     \
+    return (prefix##iter_t*) digestmap_iter_next(                       \
+                       (digestmap_t*)map, (digestmap_iter_t*)iter);     \
+  }                                                                     \
+  ATTR_UNUSED static inline prefix##iter_t*                             \
+  prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter)             \
+  {                                                                     \
+    return (prefix##iter_t*) digestmap_iter_next_rmv(                   \
+                       (digestmap_t*)map, (digestmap_iter_t*)iter);     \
+  }                                                                     \
+  ATTR_UNUSED static inline void                                        \
+  prefix##iter_get(prefix##iter_t *iter,                                \
+                   const char **keyp,                                   \
+                   valtype **valp)                                      \
+  {                                                                     \
+    void *v;                                                            \
+    digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v);             \
+    *valp = v;                                                          \
+  }                                                                     \
+  ATTR_UNUSED static inline int                                         \
+  prefix##iter_done(prefix##iter_t *iter)                               \
+  {                                                                     \
+    return digestmap_iter_done((digestmap_iter_t*)iter);                \
+  }
+
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/container/order.c b/src/lib/container/order.c
new file mode 100644
index 000000000..599f83300
--- /dev/null
+++ b/src/lib/container/order.c
@@ -0,0 +1,51 @@
+/* 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 container.c
+ * \brief Implements a smartlist (a resizable array) along
+ * with helper functions to use smartlists.  Also includes
+ * hash table implementations of a string-to-void* map, and of
+ * a digest-to-void* map.
+ **/
+
+#include <stdlib.h>
+
+#include "lib/container/order.h"
+#include "common/util_bug.h"
+
+/** Declare a function called <b>funcname</b> that acts as a find_nth_FOO
+ * function for an array of type <b>elt_t</b>*.
+ *
+ * NOTE: The implementation kind of sucks: It's O(n log n), whereas finding
+ * the kth element of an n-element list can be done in O(n).  Then again, this
+ * implementation is not in critical path, and it is obviously correct. */
+#define IMPLEMENT_ORDER_FUNC(funcname, elt_t)                   \
+  static int                                                    \
+  _cmp_ ## elt_t(const void *_a, const void *_b)                \
+  {                                                             \
+    const elt_t *a = _a, *b = _b;                               \
+    if (*a<*b)                                                  \
+      return -1;                                                \
+    else if (*a>*b)                                             \
+      return 1;                                                 \
+    else                                                        \
+      return 0;                                                 \
+  }                                                             \
+  elt_t                                                         \
+  funcname(elt_t *array, int n_elements, int nth)               \
+  {                                                             \
+    tor_assert(nth >= 0);                                       \
+    tor_assert(nth < n_elements);                               \
+    qsort(array, n_elements, sizeof(elt_t), _cmp_ ##elt_t);     \
+    return array[nth];                                          \
+  }
+
+IMPLEMENT_ORDER_FUNC(find_nth_int, int)
+IMPLEMENT_ORDER_FUNC(find_nth_time, time_t)
+IMPLEMENT_ORDER_FUNC(find_nth_double, double)
+IMPLEMENT_ORDER_FUNC(find_nth_uint32, uint32_t)
+IMPLEMENT_ORDER_FUNC(find_nth_int32, int32_t)
+IMPLEMENT_ORDER_FUNC(find_nth_long, long)
diff --git a/src/lib/container/order.h b/src/lib/container/order.h
new file mode 100644
index 000000000..bd23750d5
--- /dev/null
+++ b/src/lib/container/order.h
@@ -0,0 +1,54 @@
+/* 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_ORDER_H
+#define TOR_ORDER_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+/* These functions, given an <b>array</b> of <b>n_elements</b>, return the
+ * <b>nth</b> lowest element. <b>nth</b>=0 gives the lowest element;
+ * <b>n_elements</b>-1 gives the highest; and (<b>n_elements</b>-1) / 2 gives
+ * the median.  As a side effect, the elements of <b>array</b> are sorted. */
+int find_nth_int(int *array, int n_elements, int nth);
+time_t find_nth_time(time_t *array, int n_elements, int nth);
+double find_nth_double(double *array, int n_elements, int nth);
+int32_t find_nth_int32(int32_t *array, int n_elements, int nth);
+uint32_t find_nth_uint32(uint32_t *array, int n_elements, int nth);
+long find_nth_long(long *array, int n_elements, int nth);
+static inline int
+median_int(int *array, int n_elements)
+{
+  return find_nth_int(array, n_elements, (n_elements-1)/2);
+}
+static inline time_t
+median_time(time_t *array, int n_elements)
+{
+  return find_nth_time(array, n_elements, (n_elements-1)/2);
+}
+static inline double
+median_double(double *array, int n_elements)
+{
+  return find_nth_double(array, n_elements, (n_elements-1)/2);
+}
+static inline uint32_t
+median_uint32(uint32_t *array, int n_elements)
+{
+  return find_nth_uint32(array, n_elements, (n_elements-1)/2);
+}
+static inline int32_t
+median_int32(int32_t *array, int n_elements)
+{
+  return find_nth_int32(array, n_elements, (n_elements-1)/2);
+}
+
+static inline uint32_t
+third_quartile_uint32(uint32_t *array, int n_elements)
+{
+  return find_nth_uint32(array, n_elements, (n_elements*3)/4);
+}
+
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/container/container.c b/src/lib/container/smartlist.c
similarity index 56%
rename from src/lib/container/container.c
rename to src/lib/container/smartlist.c
index d31044fe2..6aef72854 100644
--- a/src/lib/container/container.c
+++ b/src/lib/container/smartlist.c
@@ -11,15 +11,15 @@
  * a digest-to-void* map.
  **/
 
+#include "lib/malloc/util_malloc.h"
+#include "lib/container/smartlist.h"
 #include "common/util.h"
-#include "lib/container/container.h"
 #include "lib/crypt_ops/crypto_digest.h"
+#include "lib/ctime/di_ops.h"
 
 #include <stdlib.h>
 #include <string.h>
 
-#include "ht.h"
-
 /** All newly allocated smartlists have this capacity. */
 #define SMARTLIST_DEFAULT_CAPACITY 16
 
@@ -1092,458 +1092,3 @@ smartlist_uniq_digests256(smartlist_t *sl)
 {
   smartlist_uniq(sl, compare_digests256_, tor_free_);
 }
-
-/** Helper: Declare an entry type and a map type to implement a mapping using
- * ht.h.  The map type will be called <b>maptype</b>.  The key part of each
- * entry is declared using the C declaration <b>keydecl</b>.  All functions
- * and types associated with the map get prefixed with <b>prefix</b> */
-#define DEFINE_MAP_STRUCTS(maptype, keydecl, prefix)      \
-  typedef struct prefix ## entry_t {                      \
-    HT_ENTRY(prefix ## entry_t) node;                     \
-    void *val;                                            \
-    keydecl;                                              \
-  } prefix ## entry_t;                                    \
-  struct maptype {                                        \
-    HT_HEAD(prefix ## impl, prefix ## entry_t) head;      \
-  }
-
-DEFINE_MAP_STRUCTS(strmap_t, char *key, strmap_);
-DEFINE_MAP_STRUCTS(digestmap_t, char key[DIGEST_LEN], digestmap_);
-DEFINE_MAP_STRUCTS(digest256map_t, uint8_t key[DIGEST256_LEN], digest256map_);
-
-/** Helper: compare strmap_entry_t objects by key value. */
-static inline int
-strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b)
-{
-  return !strcmp(a->key, b->key);
-}
-
-/** Helper: return a hash value for a strmap_entry_t. */
-static inline unsigned int
-strmap_entry_hash(const strmap_entry_t *a)
-{
-  return (unsigned) siphash24g(a->key, strlen(a->key));
-}
-
-/** Helper: compare digestmap_entry_t objects by key value. */
-static inline int
-digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b)
-{
-  return tor_memeq(a->key, b->key, DIGEST_LEN);
-}
-
-/** Helper: return a hash value for a digest_map_t. */
-static inline unsigned int
-digestmap_entry_hash(const digestmap_entry_t *a)
-{
-  return (unsigned) siphash24g(a->key, DIGEST_LEN);
-}
-
-/** Helper: compare digestmap_entry_t objects by key value. */
-static inline int
-digest256map_entries_eq(const digest256map_entry_t *a,
-                        const digest256map_entry_t *b)
-{
-  return tor_memeq(a->key, b->key, DIGEST256_LEN);
-}
-
-/** Helper: return a hash value for a digest_map_t. */
-static inline unsigned int
-digest256map_entry_hash(const digest256map_entry_t *a)
-{
-  return (unsigned) siphash24g(a->key, DIGEST256_LEN);
-}
-
-HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
-             strmap_entries_eq)
-HT_GENERATE2(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
-             strmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
-
-HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
-             digestmap_entries_eq)
-HT_GENERATE2(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
-             digestmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
-
-HT_PROTOTYPE(digest256map_impl, digest256map_entry_t, node,
-             digest256map_entry_hash,
-             digest256map_entries_eq)
-HT_GENERATE2(digest256map_impl, digest256map_entry_t, node,
-             digest256map_entry_hash,
-             digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_)
-
-#define strmap_entry_free(ent) \
-  FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent))
-#define digestmap_entry_free(ent) \
-  FREE_AND_NULL(digestmap_entry_t, digestmap_entry_free_, (ent))
-#define digest256map_entry_free(ent) \
-  FREE_AND_NULL(digest256map_entry_t, digest256map_entry_free_, (ent))
-
-static inline void
-strmap_entry_free_(strmap_entry_t *ent)
-{
-  tor_free(ent->key);
-  tor_free(ent);
-}
-static inline void
-digestmap_entry_free_(digestmap_entry_t *ent)
-{
-  tor_free(ent);
-}
-static inline void
-digest256map_entry_free_(digest256map_entry_t *ent)
-{
-  tor_free(ent);
-}
-
-static inline void
-strmap_assign_tmp_key(strmap_entry_t *ent, const char *key)
-{
-  ent->key = (char*)key;
-}
-static inline void
-digestmap_assign_tmp_key(digestmap_entry_t *ent, const char *key)
-{
-  memcpy(ent->key, key, DIGEST_LEN);
-}
-static inline void
-digest256map_assign_tmp_key(digest256map_entry_t *ent, const uint8_t *key)
-{
-  memcpy(ent->key, key, DIGEST256_LEN);
-}
-static inline void
-strmap_assign_key(strmap_entry_t *ent, const char *key)
-{
-  ent->key = tor_strdup(key);
-}
-static inline void
-digestmap_assign_key(digestmap_entry_t *ent, const char *key)
-{
-  memcpy(ent->key, key, DIGEST_LEN);
-}
-static inline void
-digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key)
-{
-  memcpy(ent->key, key, DIGEST256_LEN);
-}
-
-/**
- * Macro: implement all the functions for a map that are declared in
- * container.h by the DECLARE_MAP_FNS() macro.  You must additionally define a
- * prefix_entry_free_() function to free entries (and their keys), a
- * prefix_assign_tmp_key() function to temporarily set a stack-allocated
- * entry to hold a key, and a prefix_assign_key() function to set a
- * heap-allocated entry to hold a key.
- */
-#define IMPLEMENT_MAP_FNS(maptype, keytype, prefix)                     \
-  /** Create and return a new empty map. */                             \
-  MOCK_IMPL(maptype *,                                                  \
-  prefix##_new,(void))                                                  \
-  {                                                                     \
-    maptype *result;                                                    \
-    result = tor_malloc(sizeof(maptype));                               \
-    HT_INIT(prefix##_impl, &result->head);                              \
-    return result;                                                      \
-  }                                                                     \
-                                                                        \
-  /** Return the item from <b>map</b> whose key matches <b>key</b>, or  \
-   * NULL if no such value exists. */                                   \
-  void *                                                                \
-  prefix##_get(const maptype *map, const keytype key)                   \
-  {                                                                     \
-    prefix ##_entry_t *resolve;                                         \
-    prefix ##_entry_t search;                                           \
-    tor_assert(map);                                                    \
-    tor_assert(key);                                                    \
-    prefix ##_assign_tmp_key(&search, key);                             \
-    resolve = HT_FIND(prefix ##_impl, &map->head, &search);             \
-    if (resolve) {                                                      \
-      return resolve->val;                                              \
-    } else {                                                            \
-      return NULL;                                                      \
-    }                                                                   \
-  }                                                                     \
-                                                                        \
-  /** Add an entry to <b>map</b> mapping <b>key</b> to <b>val</b>;      \
-   * return the previous value, or NULL if no such value existed. */     \
-  void *                                                                \
-  prefix##_set(maptype *map, const keytype key, void *val)              \
-  {                                                                     \
-    prefix##_entry_t search;                                            \
-    void *oldval;                                                       \
-    tor_assert(map);                                                    \
-    tor_assert(key);                                                    \
-    tor_assert(val);                                                    \
-    prefix##_assign_tmp_key(&search, key);                              \
-    /* We a lot of our time in this function, so the code below is */   \
-    /* meant to optimize the check/alloc/set cycle by avoiding the two */\
-    /* trips to the hash table that we would do in the unoptimized */   \
-    /* version of this code. (Each of HT_INSERT and HT_FIND calls */     \
-    /* HT_SET_HASH and HT_FIND_P.) */                                   \
-    HT_FIND_OR_INSERT_(prefix##_impl, node, prefix##_entry_hash,        \
-                       &(map->head),                                    \
-                       prefix##_entry_t, &search, ptr,                  \
-                       {                                                \
-                         /* we found an entry. */                       \
-                         oldval = (*ptr)->val;                          \
-                         (*ptr)->val = val;                             \
-                         return oldval;                                 \
-                       },                                               \
-                       {                                                \
-                         /* We didn't find the entry. */                \
-                         prefix##_entry_t *newent =                     \
-                           tor_malloc_zero(sizeof(prefix##_entry_t));   \
-                         prefix##_assign_key(newent, key);              \
-                         newent->val = val;                             \
-                         HT_FOI_INSERT_(node, &(map->head),             \
-                            &search, newent, ptr);                      \
-                         return NULL;                                   \
-    });                                                                 \
-  }                                                                     \
-                                                                        \
-  /** Remove the value currently associated with <b>key</b> from the map. \
-   * Return the value if one was set, or NULL if there was no entry for \
-   * <b>key</b>.                                                        \
-   *                                                                    \
-   * Note: you must free any storage associated with the returned value. \
-   */                                                                   \
-  void *                                                                \
-  prefix##_remove(maptype *map, const keytype key)                      \
-  {                                                                     \
-    prefix##_entry_t *resolve;                                          \
-    prefix##_entry_t search;                                            \
-    void *oldval;                                                       \
-    tor_assert(map);                                                    \
-    tor_assert(key);                                                    \
-    prefix##_assign_tmp_key(&search, key);                              \
-    resolve = HT_REMOVE(prefix##_impl, &map->head, &search);            \
-    if (resolve) {                                                      \
-      oldval = resolve->val;                                            \
-      prefix##_entry_free(resolve);                                     \
-      return oldval;                                                    \
-    } else {                                                            \
-      return NULL;                                                      \
-    }                                                                   \
-  }                                                                     \
-                                                                        \
-  /** Return the number of elements in <b>map</b>. */                   \
-  int                                                                   \
-  prefix##_size(const maptype *map)                                     \
-  {                                                                     \
-    return HT_SIZE(&map->head);                                         \
-  }                                                                     \
-                                                                        \
-  /** Return true iff <b>map</b> has no entries. */                     \
-  int                                                                   \
-  prefix##_isempty(const maptype *map)                                  \
-  {                                                                     \
-    return HT_EMPTY(&map->head);                                        \
-  }                                                                     \
-                                                                        \
-  /** Assert that <b>map</b> is not corrupt. */                         \
-  void                                                                  \
-  prefix##_assert_ok(const maptype *map)                                \
-  {                                                                     \
-    tor_assert(!prefix##_impl_HT_REP_IS_BAD_(&map->head));              \
-  }                                                                     \
-                                                                        \
-  /** Remove all entries from <b>map</b>, and deallocate storage for    \
-   * those entries.  If free_val is provided, invoked it every value in \
-   * <b>map</b>. */                                                     \
-  MOCK_IMPL(void,                                                       \
-  prefix##_free_, (maptype *map, void (*free_val)(void*)))              \
-  {                                                                     \
-    prefix##_entry_t **ent, **next, *this;                              \
-    if (!map)                                                           \
-      return;                                                           \
-    for (ent = HT_START(prefix##_impl, &map->head); ent != NULL;        \
-         ent = next) {                                                  \
-      this = *ent;                                                      \
-      next = HT_NEXT_RMV(prefix##_impl, &map->head, ent);               \
-      if (free_val)                                                     \
-        free_val(this->val);                                            \
-      prefix##_entry_free(this);                                        \
-    }                                                                   \
-    tor_assert(HT_EMPTY(&map->head));                                   \
-    HT_CLEAR(prefix##_impl, &map->head);                                \
-    tor_free(map);                                                      \
-  }                                                                     \
-                                                                        \
-  /** return an <b>iterator</b> pointer to the front of a map.          \
-   *                                                                    \
-   * Iterator example:                                                  \
-   *                                                                    \
-   * \code                                                              \
-   * // uppercase values in "map", removing empty values.               \
-   *                                                                    \
-   * strmap_iter_t *iter;                                               \
-   * const char *key;                                                   \
-   * void *val;                                                         \
-   * char *cp;                                                          \
-   *                                                                    \
-   * for (iter = strmap_iter_init(map); !strmap_iter_done(iter); ) {    \
-   *    strmap_iter_get(iter, &key, &val);                              \
-   *    cp = (char*)val;                                                \
-   *    if (!*cp) {                                                     \
-   *       iter = strmap_iter_next_rmv(map,iter);                       \
-   *       free(val);                                                   \
-   *    } else {                                                        \
-   *       for (;*cp;cp++) *cp = TOR_TOUPPER(*cp);                      \
-   */                                                                   \
-  prefix##_iter_t *                                                     \
-  prefix##_iter_init(maptype *map)                                      \
-  {                                                                     \
-    tor_assert(map);                                                    \
-    return HT_START(prefix##_impl, &map->head);                         \
-  }                                                                     \
-                                                                        \
-  /** Advance <b>iter</b> a single step to the next entry, and return   \
-   * its new value. */                                                  \
-  prefix##_iter_t *                                                     \
-  prefix##_iter_next(maptype *map, prefix##_iter_t *iter)               \
-  {                                                                     \
-    tor_assert(map);                                                    \
-    tor_assert(iter);                                                   \
-    return HT_NEXT(prefix##_impl, &map->head, iter);                    \
-  }                                                                     \
-  /** Advance <b>iter</b> a single step to the next entry, removing the \
-   * current entry, and return its new value. */                        \
-  prefix##_iter_t *                                                     \
-  prefix##_iter_next_rmv(maptype *map, prefix##_iter_t *iter)           \
-  {                                                                     \
-    prefix##_entry_t *rmv;                                              \
-    tor_assert(map);                                                    \
-    tor_assert(iter);                                                   \
-    tor_assert(*iter);                                                  \
-    rmv = *iter;                                                        \
-    iter = HT_NEXT_RMV(prefix##_impl, &map->head, iter);                \
-    prefix##_entry_free(rmv);                                           \
-    return iter;                                                        \
-  }                                                                     \
-  /** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed    \
-   * to by iter. */                                                     \
-  void                                                                  \
-  prefix##_iter_get(prefix##_iter_t *iter, const keytype *keyp,         \
-                    void **valp)                                        \
-  {                                                                     \
-    tor_assert(iter);                                                   \
-    tor_assert(*iter);                                                  \
-    tor_assert(keyp);                                                   \
-    tor_assert(valp);                                                   \
-    *keyp = (*iter)->key;                                               \
-    *valp = (*iter)->val;                                               \
-  }                                                                     \
-  /** Return true iff <b>iter</b> has advanced past the last entry of   \
-   * <b>map</b>. */                                                     \
-  int                                                                   \
-  prefix##_iter_done(prefix##_iter_t *iter)                             \
-  {                                                                     \
-    return iter == NULL;                                                \
-  }
-
-IMPLEMENT_MAP_FNS(strmap_t, char *, strmap)
-IMPLEMENT_MAP_FNS(digestmap_t, char *, digestmap)
-IMPLEMENT_MAP_FNS(digest256map_t, uint8_t *, digest256map)
-
-/** Same as strmap_set, but first converts <b>key</b> to lowercase. */
-void *
-strmap_set_lc(strmap_t *map, const char *key, void *val)
-{
-  /* We could be a little faster by using strcasecmp instead, and a separate
-   * type, but I don't think it matters. */
-  void *v;
-  char *lc_key = tor_strdup(key);
-  tor_strlower(lc_key);
-  v = strmap_set(map,lc_key,val);
-  tor_free(lc_key);
-  return v;
-}
-
-/** Same as strmap_get, but first converts <b>key</b> to lowercase. */
-void *
-strmap_get_lc(const strmap_t *map, const char *key)
-{
-  void *v;
-  char *lc_key = tor_strdup(key);
-  tor_strlower(lc_key);
-  v = strmap_get(map,lc_key);
-  tor_free(lc_key);
-  return v;
-}
-
-/** Same as strmap_remove, but first converts <b>key</b> to lowercase */
-void *
-strmap_remove_lc(strmap_t *map, const char *key)
-{
-  void *v;
-  char *lc_key = tor_strdup(key);
-  tor_strlower(lc_key);
-  v = strmap_remove(map,lc_key);
-  tor_free(lc_key);
-  return v;
-}
-
-/** Declare a function called <b>funcname</b> that acts as a find_nth_FOO
- * function for an array of type <b>elt_t</b>*.
- *
- * NOTE: The implementation kind of sucks: It's O(n log n), whereas finding
- * the kth element of an n-element list can be done in O(n).  Then again, this
- * implementation is not in critical path, and it is obviously correct. */
-#define IMPLEMENT_ORDER_FUNC(funcname, elt_t)                   \
-  static int                                                    \
-  _cmp_ ## elt_t(const void *_a, const void *_b)                \
-  {                                                             \
-    const elt_t *a = _a, *b = _b;                               \
-    if (*a<*b)                                                  \
-      return -1;                                                \
-    else if (*a>*b)                                             \
-      return 1;                                                 \
-    else                                                        \
-      return 0;                                                 \
-  }                                                             \
-  elt_t                                                         \
-  funcname(elt_t *array, int n_elements, int nth)               \
-  {                                                             \
-    tor_assert(nth >= 0);                                       \
-    tor_assert(nth < n_elements);                               \
-    qsort(array, n_elements, sizeof(elt_t), _cmp_ ##elt_t);     \
-    return array[nth];                                          \
-  }
-
-IMPLEMENT_ORDER_FUNC(find_nth_int, int)
-IMPLEMENT_ORDER_FUNC(find_nth_time, time_t)
-IMPLEMENT_ORDER_FUNC(find_nth_double, double)
-IMPLEMENT_ORDER_FUNC(find_nth_uint32, uint32_t)
-IMPLEMENT_ORDER_FUNC(find_nth_int32, int32_t)
-IMPLEMENT_ORDER_FUNC(find_nth_long, long)
-
-/** Return a newly allocated digestset_t, optimized to hold a total of
- * <b>max_elements</b> digests with a reasonably low false positive weight. */
-digestset_t *
-digestset_new(int max_elements)
-{
-  /* The probability of false positives is about P=(1 - exp(-kn/m))^k, where k
-   * is the number of hash functions per entry, m is the bits in the array,
-   * and n is the number of elements inserted.  For us, k==4, n<=max_elements,
-   * and m==n_bits= approximately max_elements*32.  This gives
-   *   P<(1-exp(-4*n/(32*n)))^4 == (1-exp(1/-8))^4 == .00019
-   *
-   * It would be more optimal in space vs false positives to get this false
-   * positive rate by going for k==13, and m==18.5n, but we also want to
-   * conserve CPU, and k==13 is pretty big.
-   */
-  int n_bits = 1u << (tor_log2(max_elements)+5);
-  digestset_t *r = tor_malloc(sizeof(digestset_t));
-  r->mask = n_bits - 1;
-  r->ba = bitarray_init_zero(n_bits);
-  return r;
-}
-
-/** Free all storage held in <b>set</b>. */
-void
-digestset_free_(digestset_t *set)
-{
-  if (!set)
-    return;
-  bitarray_free(set->ba);
-  tor_free(set);
-}
diff --git a/src/lib/container/smartlist.h b/src/lib/container/smartlist.h
new file mode 100644
index 000000000..7b80a9fed
--- /dev/null
+++ b/src/lib/container/smartlist.h
@@ -0,0 +1,351 @@
+/* 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_H
+#define TOR_SMARTLIST_H
+
+#include <stddef.h>
+#include "lib/cc/compat_compiler.h"
+#include "common/util_bug.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);
+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);
+int smartlist_contains_string_case(const smartlist_t *sl, const char *element);
+int smartlist_contains_int_as_string(const smartlist_t *sl, int num);
+int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2);
+int smartlist_contains_digest(const smartlist_t *sl, const char *element);
+int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2);
+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
+/** 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) {
+  tor_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) {
+  tor_assert(sl);
+  tor_assert(idx>=0);
+  tor_assert(sl->num_used > idx);
+  return sl->list[idx];
+}
+static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
+  tor_assert(sl);
+  tor_assert(idx>=0);
+  tor_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,
+                    int (*compare)(const void **a, const void **b),
+                    int *count_out);
+#define smartlist_get_most_frequent(sl, compare) \
+  smartlist_get_most_frequent_((sl), (compare), NULL)
+void smartlist_uniq(smartlist_t *sl,
+                    int (*compare)(const void **a, const void **b),
+                    void (*free_fn)(void *elt));
+
+void smartlist_sort_strings(smartlist_t *sl);
+void smartlist_sort_digests(smartlist_t *sl);
+void smartlist_sort_digests256(smartlist_t *sl);
+void smartlist_sort_pointers(smartlist_t *sl);
+
+const char *smartlist_get_most_frequent_string(smartlist_t *sl);
+const char *smartlist_get_most_frequent_string_(smartlist_t *sl,
+                                                int *count_out);
+const uint8_t *smartlist_get_most_frequent_digest256(smartlist_t *sl);
+
+void smartlist_uniq_strings(smartlist_t *sl);
+void smartlist_uniq_digests(smartlist_t *sl);
+void smartlist_uniq_digests256(smartlist_t *sl);
+void *smartlist_bsearch(smartlist_t *sl, const void *key,
+                        int (*compare)(const void *key, const void **member));
+int smartlist_bsearch_idx(const smartlist_t *sl, const void *key,
+                          int (*compare)(const void *key, const void **member),
+                          int *found_out);
+
+void smartlist_pqueue_add(smartlist_t *sl,
+                          int (*compare)(const void *a, const void *b),
+                          int idx_field_offset,
+                          void *item);
+void *smartlist_pqueue_pop(smartlist_t *sl,
+                           int (*compare)(const void *a, const void *b),
+                           int idx_field_offset);
+void smartlist_pqueue_remove(smartlist_t *sl,
+                             int (*compare)(const void *a, const void *b),
+                             int idx_field_offset,
+                             void *item);
+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
+ * duplicates on the common field, loop through the lists in lockstep, and
+ * execute <b>unmatched_var2</b> on items in var2 that do not appear in
+ * var1.
+ *
+ * WARNING: It isn't safe to add remove elements from either list while the
+ * loop is in progress.
+ *
+ * Example use:
+ *  SMARTLIST_FOREACH_JOIN(routerstatus_list, routerstatus_t *, rs,
+ *                     routerinfo_list, routerinfo_t *, ri,
+ *                    tor_memcmp(rs->identity_digest, ri->identity_digest, 20),
+ *                     log_info(LD_GENERAL,"No match for %s", ri->nickname)) {
+ *    log_info(LD_GENERAL, "%s matches routerstatus %p", ri->nickname, rs);
+ * } SMARTLIST_FOREACH_JOIN_END(rs, ri);
+ **/
+/* The example above unpacks (approximately) to:
+ *  int rs_sl_idx = 0, rs_sl_len = smartlist_len(routerstatus_list);
+ *  int ri_sl_idx, ri_sl_len = smartlist_len(routerinfo_list);
+ *  int rs_ri_cmp;
+ *  routerstatus_t *rs;
+ *  routerinfo_t *ri;
+ *  for (; ri_sl_idx < ri_sl_len; ++ri_sl_idx) {
+ *    ri = smartlist_get(routerinfo_list, ri_sl_idx);
+ *    while (rs_sl_idx < rs_sl_len) {
+ *      rs = smartlist_get(routerstatus_list, rs_sl_idx);
+ *      rs_ri_cmp = tor_memcmp(rs->identity_digest, ri->identity_digest, 20);
+ *      if (rs_ri_cmp > 0) {
+ *        break;
+ *      } else if (rs_ri_cmp == 0) {
+ *        goto matched_ri;
+ *      } else {
+ *        ++rs_sl_idx;
+ *      }
+ *    }
+ *    log_info(LD_GENERAL,"No match for %s", ri->nickname);
+ *    continue;
+ *   matched_ri: {
+ *    log_info(LD_GENERAL,"%s matches with routerstatus %p",ri->nickname,rs);
+ *    }
+ *  }
+ */
+#define SMARTLIST_FOREACH_JOIN(sl1, type1, var1, sl2, type2, var2,      \
+                                cmpexpr, unmatched_var2)                \
+  STMT_BEGIN                                                            \
+  int var1 ## _sl_idx = 0, var1 ## _sl_len=(sl1)->num_used;             \
+  int var2 ## _sl_idx = 0, var2 ## _sl_len=(sl2)->num_used;             \
+  int var1 ## _ ## var2 ## _cmp;                                        \
+  type1 var1;                                                           \
+  type2 var2;                                                           \
+  for (; var2##_sl_idx < var2##_sl_len; ++var2##_sl_idx) {              \
+    var2 = (sl2)->list[var2##_sl_idx];                                  \
+    while (var1##_sl_idx < var1##_sl_len) {                             \
+      var1 = (sl1)->list[var1##_sl_idx];                                \
+      var1##_##var2##_cmp = (cmpexpr);                                  \
+      if (var1##_##var2##_cmp > 0) {                                    \
+        break;                                                          \
+      } else if (var1##_##var2##_cmp == 0) {                            \
+        goto matched_##var2;                                            \
+      } else {                                                          \
+        ++var1##_sl_idx;                                                \
+      }                                                                 \
+    }                                                                   \
+    /* Ran out of v1, or no match for var2. */                          \
+    unmatched_var2;                                                     \
+    continue;                                                           \
+    matched_##var2: ;                                                   \
+
+#define SMARTLIST_FOREACH_JOIN_END(var1, var2)  \
+  }                                             \
+  STMT_END
+
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/lib/malloc/util_malloc.c b/src/lib/malloc/util_malloc.c
index 4a0b3a933..f3b0e50c7 100644
--- a/src/lib/malloc/util_malloc.c
+++ b/src/lib/malloc/util_malloc.c
@@ -12,7 +12,6 @@
 #include "orconfig.h"
 
 #include <stdlib.h>
-#include <stddef.h>
 #include <string.h>
 
 #include "lib/testsupport/testsupport.h"
diff --git a/src/lib/malloc/util_malloc.h b/src/lib/malloc/util_malloc.h
index 3da40351c..88ecc0453 100644
--- a/src/lib/malloc/util_malloc.h
+++ b/src/lib/malloc/util_malloc.h
@@ -11,6 +11,8 @@
 #ifndef TOR_UTIL_MALLOC_H
 #define TOR_UTIL_MALLOC_H
 
+#include <stddef.h>
+#include <stdlib.h>
 #include "lib/cc/compat_compiler.h"
 
 /* Memory management */



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits