commit a76f6457e330d14d09d490d2c73f47ac2c7369c6 Author: Zack Weinberg zackw@panix.com Date: Tue Jul 19 09:56:07 2011 -0700
Import container library from Tor.
* src/container.c, src/container.h, src/ht.h, src/test/unittest_container.c: New files. * Makefile.am: Add new files (and some old files that got missed). * src/test/unittest.c: Add test group for container library.
* configure.ac: Check size of int. Check for 'inline'. * util.h: Include stdint.h (unconditionally for now). (xstrndup, ui64_log2, ascii_isspace, ascii_strstrip, ascii_strlower): New functions required by container.c. * util.c: Define said new functions. * crypt.h, crypt.c (random_int): Another function needed by container.c. --- Makefile.am | 5 + configure.ac | 18 +- src/container.c | 1441 +++++++++++++++++++++++++++++++++++++++++ src/container.h | 685 ++++++++++++++++++++ src/crypt.c | 24 + src/crypt.h | 5 + src/ht.h | 471 ++++++++++++++ src/test/unittest.c | 2 + src/test/unittest_container.c | 772 ++++++++++++++++++++++ src/util.c | 74 +++ src/util.h | 19 +- 11 files changed, 3514 insertions(+), 2 deletions(-)
diff --git a/Makefile.am b/Makefile.am index 521ba24..ff518ad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ noinst_LIBRARIES = libobfsproxy.a noinst_PROGRAMS = unittests
libobfsproxy_a_SOURCES = \ + src/container.c \ src/crypt.c \ src/network.c \ src/protocol.c \ @@ -26,12 +27,16 @@ obfsproxy_SOURCES = \ unittests_SOURCES = \ src/test/tinytest.c \ src/test/unittest.c \ + src/test/unittest_container.c \ src/test/unittest_crypt.c \ src/test/unittest_obfs2.c \ src/test/unittest_socks.c
noinst_HEADERS = \ + src/container.h \ src/crypt.h \ + src/ht.h \ + src/main.h \ src/network.h \ src/protocol.h \ src/sha256.h \ diff --git a/configure.ac b/configure.ac index 32b7138..fc82507 100644 --- a/configure.ac +++ b/configure.ac @@ -1,13 +1,22 @@ AC_PREREQ([2.61])dnl Possibly earlier will do, but this is what I have AC_INIT([obsproxy], [0.0]) AC_CONFIG_SRCDIR([src/main.c]) - AM_INIT_AUTOMAKE([foreign])
+dnl The stock definition of AC_INCLUDES_DEFAULT performs a whole bunch +dnl of completely unnecessary checks *even if* you override its +dnl mostly-useless default header list at invocation time. +dnl Replace it with a version that does nothing unless requested. +m4_pushdef([AC_INCLUDES_DEFAULT], [$1]) + +### Programs ### + AC_PROG_CC AC_PROG_RANLIB PKG_PROG_PKG_CONFIG
+### Libraries ### + PKG_CHECK_MODULES([libevent], [libevent >= 2.0]) # Presently no need for libssl, only libcrypto. PKG_CHECK_MODULES([libcrypto], [libcrypto >= 0.9.7]) @@ -28,6 +37,13 @@ LIBS="$libevent_LIBS $libcrypto_LIBS" AX_LIB_WINSOCK2 LIBS="$save_LIBS"
+### C features ### + +AC_C_INLINE +AC_CHECK_SIZEOF(int) + +### Output ### + AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([config.h]) AC_OUTPUT diff --git a/src/container.c b/src/container.c new file mode 100644 index 0000000..b66a819 --- /dev/null +++ b/src/container.c @@ -0,0 +1,1441 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2011, 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 "util.h" +#include "container.h" +#include "crypt.h" +#include "ht.h" + +/** All newly allocated smartlists have this capacity. */ +#define SMARTLIST_DEFAULT_CAPACITY 16 + +/** Allocate and return an empty smartlist. + */ +smartlist_t * +smartlist_create(void) +{ + smartlist_t *sl = xmalloc(sizeof(smartlist_t)); + sl->num_used = 0; + sl->capacity = SMARTLIST_DEFAULT_CAPACITY; + sl->list = xmalloc(sizeof(void *) * sl->capacity); + return sl; +} + +/** Deallocate a smartlist. Does not release storage associated with the + * list's elements. + */ +void +smartlist_free(smartlist_t *sl) +{ + if (!sl) + return; + free(sl->list); + free(sl); +} + +/** Remove all elements from the list. + */ +void +smartlist_clear(smartlist_t *sl) +{ + sl->num_used = 0; +} + +/** Make sure that <b>sl</b> can hold at least <b>size</b> entries. */ +static inline void +smartlist_ensure_capacity(smartlist_t *sl, int size) +{ + if (size > sl->capacity) { + int higher = sl->capacity * 2; + while (size > higher) + higher *= 2; + obfs_assert(higher > 0); /* detect overflow */ + sl->capacity = higher; + sl->list = xrealloc(sl->list, sizeof(void*)*sl->capacity); + } +} + +/** Append element to the end of the list. */ +void +smartlist_add(smartlist_t *sl, void *element) +{ + smartlist_ensure_capacity(sl, sl->num_used+1); + sl->list[sl->num_used++] = element; +} + +/** Append each element from S2 to the end of S1. */ +void +smartlist_add_all(smartlist_t *s1, const smartlist_t *s2) +{ + int new_size = s1->num_used + s2->num_used; + obfs_assert(new_size >= s1->num_used); /* check for overflow. */ + smartlist_ensure_capacity(s1, new_size); + memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*)); + s1->num_used = new_size; +} + +/** Remove all elements E from sl such that E==element. Preserve + * the order of any elements before E, but elements after E can be + * rearranged. + */ +void +smartlist_remove(smartlist_t *sl, const void *element) +{ + int i; + if (element == NULL) + return; + for (i=0; i < sl->num_used; i++) + if (sl->list[i] == element) { + sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ + i--; /* so we process the new i'th element */ + } +} + +/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise, + * return NULL. */ +void * +smartlist_pop_last(smartlist_t *sl) +{ + obfs_assert(sl); + if (sl->num_used) + return sl->list[--sl->num_used]; + else + return NULL; +} + +/** Reverse the order of the items in <b>sl</b>. */ +void +smartlist_reverse(smartlist_t *sl) +{ + int i, j; + void *tmp; + obfs_assert(sl); + for (i = 0, j = sl->num_used-1; i < j; ++i, --j) { + tmp = sl->list[i]; + sl->list[i] = sl->list[j]; + sl->list[j] = tmp; + } +} + +/** If there are any strings in sl equal to element, remove and free them. + * Does not preserve order. */ +void +smartlist_string_remove(smartlist_t *sl, const char *element) +{ + int i; + obfs_assert(sl); + obfs_assert(element); + for (i = 0; i < sl->num_used; ++i) { + if (!strcmp(element, sl->list[i])) { + free(sl->list[i]); + sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ + i--; /* so we process the new i'th element */ + } + } +} + +/** Return true iff some element E of sl has E==element. + */ +int +smartlist_isin(const smartlist_t *sl, const void *element) +{ + int i; + for (i=0; i < sl->num_used; i++) + if (sl->list[i] == element) + return 1; + return 0; +} + +/** Return true iff <b>sl</b> has some element E such that + * !strcmp(E,<b>element</b>) + */ +int +smartlist_string_isin(const smartlist_t *sl, const char *element) +{ + int i; + if (!sl) return 0; + for (i=0; i < sl->num_used; i++) + if (strcmp((const char*)sl->list[i],element)==0) + return 1; + return 0; +} + +/** If <b>element</b> is equal to an element of <b>sl</b>, return that + * element's index. Otherwise, return -1. */ +int +smartlist_string_pos(const smartlist_t *sl, const char *element) +{ + int i; + if (!sl) return -1; + for (i=0; i < sl->num_used; i++) + if (strcmp((const char*)sl->list[i],element)==0) + return i; + return -1; +} + +/** Return true iff <b>sl</b> has some element E such that + * !strcasecmp(E,<b>element</b>) + */ +int +smartlist_string_isin_case(const smartlist_t *sl, const char *element) +{ + int i; + if (!sl) return 0; + for (i=0; i < sl->num_used; i++) + if (strcasecmp((const char*)sl->list[i],element)==0) + return 1; + return 0; +} + +/** Return true iff <b>sl</b> has some element E such that E is equal + * to the decimal encoding of <b>num</b>. + */ +int +smartlist_string_num_isin(const smartlist_t *sl, int num) +{ + char buf[32]; /* long enough for 64-bit int, and then some. */ + obfs_snprintf(buf,sizeof(buf),"%d", num); + return smartlist_string_isin(sl, buf); +} + +/** Return true iff the two lists contain the same strings in the same + * order, or if they are both NULL. */ +int +smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) +{ + if (sl1 == NULL) + return sl2 == NULL; + if (sl2 == NULL) + return 0; + if (smartlist_len(sl1) != smartlist_len(sl2)) + return 0; + SMARTLIST_FOREACH(sl1, const char *, cp1, { + const char *cp2 = smartlist_get(sl2, cp1_sl_idx); + if (strcmp(cp1, cp2)) + return 0; + }); + return 1; +} + +/** Return true iff <b>sl</b> has some element E such that + * !memcmp(E,<b>element</b>,SHA256_LENGTH) + */ +int +smartlist_digest_isin(const smartlist_t *sl, const char *element) +{ + int i; + if (!sl) return 0; + for (i=0; i < sl->num_used; i++) + if (!memcmp((const char*)sl->list[i],element,SHA256_LENGTH)) + return 1; + return 0; +} + +/** Return true iff some element E of sl2 has smartlist_isin(sl1,E). + */ +int +smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) +{ + int i; + for (i=0; i < sl2->num_used; i++) + if (smartlist_isin(sl1, sl2->list[i])) + return 1; + return 0; +} + +/** Remove every element E of sl1 such that !smartlist_isin(sl2,E). + * Does not preserve the order of sl1. + */ +void +smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2) +{ + int i; + for (i=0; i < sl1->num_used; i++) + if (!smartlist_isin(sl2, sl1->list[i])) { + sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */ + i--; /* so we process the new i'th element */ + } +} + +/** Remove every element E of sl1 such that smartlist_isin(sl2,E). + * Does not preserve the order of sl1. + */ +void +smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2) +{ + int i; + for (i=0; i < sl2->num_used; i++) + smartlist_remove(sl1, sl2->list[i]); +} + +/** Remove the <b>idx</b>th element of sl; if idx is not the last + * element, swap the last element of sl into the <b>idx</b>th space. + */ +void +smartlist_del(smartlist_t *sl, int idx) +{ + obfs_assert(sl); + obfs_assert(idx>=0); + obfs_assert(idx < sl->num_used); + sl->list[idx] = sl->list[--sl->num_used]; +} + +/** Remove the <b>idx</b>th element of sl; if idx is not the last element, + * moving all subsequent elements back one space. Return the old value + * of the <b>idx</b>th element. + */ +void +smartlist_del_keeporder(smartlist_t *sl, int idx) +{ + obfs_assert(sl); + obfs_assert(idx>=0); + obfs_assert(idx < sl->num_used); + --sl->num_used; + if (idx < sl->num_used) + memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx)); +} + +/** Insert the value <b>val</b> as the new <b>idx</b>th element of + * <b>sl</b>, moving all items previously at <b>idx</b> or later + * forward one space. + */ +void +smartlist_insert(smartlist_t *sl, int idx, void *val) +{ + obfs_assert(sl); + obfs_assert(idx>=0); + obfs_assert(idx <= sl->num_used); + if (idx == sl->num_used) { + smartlist_add(sl, val); + } else { + smartlist_ensure_capacity(sl, sl->num_used+1); + /* Move other elements away */ + if (idx < sl->num_used) + memmove(sl->list + idx + 1, sl->list + idx, + sizeof(void*)*(sl->num_used-idx)); + sl->num_used++; + sl->list[idx] = val; + } +} + +/** + * Split a string <b>str</b> along all occurrences of <b>sep</b>, + * appending the (newly allocated) split strings, in order, to + * <b>sl</b>. Return the number of strings added to <b>sl</b>. + * + * If <b>flags</b>&SPLIT_SKIP_SPACE is true, remove initial and + * trailing space from each entry. + * If <b>flags</b>&SPLIT_IGNORE_BLANK is true, remove any entries + * of length 0. + * If <b>flags</b>&SPLIT_STRIP_SPACE is true, strip spaces from each + * split string. + * + * If <b>max</b>>0, divide the string into no more than <b>max</b> pieces. If + * <b>sep</b> is NULL, split on any sequence of horizontal space. + */ +int +smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, + int flags, int max) +{ + const char *cp, *end, *next; + int n = 0; + + obfs_assert(sl); + obfs_assert(str); + + cp = str; + while (1) { + if (flags&SPLIT_SKIP_SPACE) { + while (ascii_isspace(*cp)) ++cp; + } + + if (max>0 && n == max-1) { + end = strchr(cp,'\0'); + } else if (sep) { + end = strstr(cp,sep); + if (!end) + end = strchr(cp,'\0'); + } else { + for (end = cp; *end && *end != '\t' && *end != ' '; ++end) + ; + } + + obfs_assert(end); + + if (!*end) { + next = NULL; + } else if (sep) { + next = end+strlen(sep); + } else { + next = end+1; + while (*next == '\t' || *next == ' ') + ++next; + } + + if (flags&SPLIT_SKIP_SPACE) { + while (end > cp && ascii_isspace(*(end-1))) + --end; + } + if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) { + char *string = xstrndup(cp, end-cp); + if (flags&SPLIT_STRIP_SPACE) + ascii_strstrip(string, " "); + smartlist_add(sl, string); + ++n; + } + if (!next) + break; + cp = next; + } + + return n; +} + +/** Allocate and return a new string containing the concatenation of + * the elements of <b>sl</b>, in order, separated by <b>join</b>. If + * <b>terminate</b> is true, also terminate the string with <b>join</b>. + * If <b>len_out</b> is not NULL, set <b>len_out</b> to the length of + * the returned string. Requires that every element of <b>sl</b> is + * NUL-terminated string. + */ +char * +smartlist_join_strings(smartlist_t *sl, const char *join, + int terminate, size_t *len_out) +{ + return smartlist_join_strings2(sl,join,strlen(join),terminate,len_out); +} + +/** As smartlist_join_strings, but instead of separating/terminated with a + * NUL-terminated string <b>join</b>, uses the <b>join_len</b>-byte sequence + * at <b>join</b>. (Useful for generating a sequence of NUL-terminated + * strings.) + */ +char * +smartlist_join_strings2(smartlist_t *sl, const char *join, + size_t join_len, int terminate, size_t *len_out) +{ + int i; + size_t n = 0; + char *r = NULL, *dst, *src; + + obfs_assert(sl); + obfs_assert(join); + + if (terminate) + n = join_len; + + for (i = 0; i < sl->num_used; ++i) { + n += strlen(sl->list[i]); + if (i+1 < sl->num_used) /* avoid double-counting the last one */ + n += join_len; + } + dst = r = xmalloc(n+1); + for (i = 0; i < sl->num_used; ) { + for (src = sl->list[i]; *src; ) + *dst++ = *src++; + if (++i < sl->num_used) { + memcpy(dst, join, join_len); + dst += join_len; + } + } + if (terminate) { + memcpy(dst, join, join_len); + dst += join_len; + } + *dst = '\0'; + + if (len_out) + *len_out = dst-r; + return r; +} + +/** Sort the members of <b>sl</b> into an order defined by + * the ordering function <b>compare</b>, which returns less then 0 if a + * precedes b, greater than 0 if b precedes a, and 0 if a 'equals' b. + */ +void +smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b)) +{ + if (!sl->num_used) + return; + qsort(sl->list, sl->num_used, sizeof(void*), + (int (*)(const void *,const void*))compare); +} + +/** Given a smartlist <b>sl</b> sorted with the function <b>compare</b>, + * return the most frequent member in the list. Break ties in favor of + * later elements. If the list is empty, return NULL. + */ +void * +smartlist_get_most_frequent(const smartlist_t *sl, + int (*compare)(const void **a, const void **b)) +{ + const void *most_frequent = NULL; + int most_frequent_count = 0; + + const void *cur = NULL; + int i, count=0; + + if (!sl->num_used) + return NULL; + for (i = 0; i < sl->num_used; ++i) { + const void *item = sl->list[i]; + if (cur && 0 == compare(&cur, &item)) { + ++count; + } else { + if (cur && count >= most_frequent_count) { + most_frequent = cur; + most_frequent_count = count; + } + cur = item; + count = 1; + } + } + if (cur && count >= most_frequent_count) { + most_frequent = cur; + most_frequent_count = count; + } + return (void*)most_frequent; +} + +/** Given a sorted smartlist <b>sl</b> and the comparison function used to + * sort it, remove all duplicate members. If free_fn is provided, calls + * free_fn on each duplicate. Otherwise, just removes them. Preserves order. + */ +void +smartlist_uniq(smartlist_t *sl, + int (*compare)(const void **a, const void **b), + void (*free_fn)(void *a)) +{ + int i; + for (i=1; i < sl->num_used; ++i) { + if (compare((const void **)&(sl->list[i-1]), + (const void **)&(sl->list[i])) == 0) { + if (free_fn) + free_fn(sl->list[i]); + smartlist_del_keeporder(sl, i--); + } + } +} + +/** Return a randomly chosen element of <b>sl</b>; or NULL if <b>sl</b> + * is empty. */ + +void * +smartlist_choose(const smartlist_t *sl) +{ + int len = smartlist_len(sl); + if (len) + return smartlist_get(sl, random_int(len)); + return NULL; /* no elements to choose from */ +} + +/** Scramble the elements of <b>sl</b> into a random order. */ +void +smartlist_shuffle(smartlist_t *sl) +{ + int i; + + /* From the end of the list to the front, choose at random from the + positions we haven't looked at yet, and swap that position into the + current position. Remember to give "no swap" the same probability as + any other swap. */ + for (i = smartlist_len(sl)-1; i > 0; --i) { + int j = random_int(i+1); + smartlist_swap(sl, i, j); + } +} + + +/** Assuming the members of <b>sl</b> are in order, return a pointer to the + * member that matches <b>key</b>. Ordering and matching are defined by a + * <b>compare</b> function that returns 0 on a match; less than 0 if key is + * less than member, and greater than 0 if key is greater then member. + */ +void * +smartlist_bsearch(smartlist_t *sl, const void *key, + int (*compare)(const void *key, const void **member)) +{ + int found, idx; + idx = smartlist_bsearch_idx(sl, key, compare, &found); + return found ? smartlist_get(sl, idx) : NULL; +} + +/** Assuming the members of <b>sl</b> are in order, return the index of the + * member that matches <b>key</b>. If no member matches, return the index of + * the first member greater than <b>key</b>, or smartlist_len(sl) if no member + * is greater than <b>key</b>. Set <b>found_out</b> to true on a match, to + * false otherwise. Ordering and matching are defined by a <b>compare</b> + * function that returns 0 on a match; less than 0 if key is less than member, + * and greater than 0 if key is greater then member. + */ +int +smartlist_bsearch_idx(const smartlist_t *sl, const void *key, + int (*compare)(const void *key, const void **member), + int *found_out) +{ + int hi = smartlist_len(sl) - 1, lo = 0, cmp, mid; + + while (lo <= hi) { + mid = (lo + hi) / 2; + cmp = compare(key, (const void**) &(sl->list[mid])); + if (cmp>0) { /* key > sl[mid] */ + lo = mid+1; + } else if (cmp<0) { /* key < sl[mid] */ + hi = mid-1; + } else { /* key == sl[mid] */ + *found_out = 1; + return mid; + } + } + /* lo > hi. */ + { + obfs_assert(lo >= 0); + if (lo < smartlist_len(sl)) { + cmp = compare(key, (const void**) &(sl->list[lo])); + obfs_assert(cmp < 0); + } else if (smartlist_len(sl)) { + cmp = compare(key, (const void**) &(sl->list[smartlist_len(sl)-1])); + obfs_assert(cmp > 0); + } + } + *found_out = 0; + return lo; +} + +/** Helper: compare two const char **s. */ +static int +_compare_string_ptrs(const void **_a, const void **_b) +{ + return strcmp((const char*)*_a, (const char*)*_b); +} + +/** Sort a smartlist <b>sl</b> containing strings into lexically ascending + * order. */ +void +smartlist_sort_strings(smartlist_t *sl) +{ + smartlist_sort(sl, _compare_string_ptrs); +} + +/** Return the most frequent string in the sorted list <b>sl</b> */ +char * +smartlist_get_most_frequent_string(smartlist_t *sl) +{ + return smartlist_get_most_frequent(sl, _compare_string_ptrs); +} + +/** Remove duplicate strings from a sorted list, and free them with free(). + */ +void +smartlist_uniq_strings(smartlist_t *sl) +{ + smartlist_uniq(sl, _compare_string_ptrs, free); +} + +/* Heap-based priority queue implementation for O(lg N) insert and remove. + * Recall that the heap property is that, for every index I, h[I] < + * H[LEFT_CHILD[I]] and h[I] < H[RIGHT_CHILD[I]]. + * + * For us to remove items other than the topmost item, each item must store + * its own index within the heap. When calling the pqueue functions, tell + * them about the offset of the field that stores the index within the item. + * + * Example: + * + * typedef struct timer_t { + * struct timeval tv; + * int heap_index; + * } timer_t; + * + * static int compare(const void *p1, const void *p2) { + * const timer_t *t1 = p1, *t2 = p2; + * if (t1->tv.tv_sec < t2->tv.tv_sec) { + * return -1; + * } else if (t1->tv.tv_sec > t2->tv.tv_sec) { + * return 1; + * } else { + * return t1->tv.tv_usec - t2->tv_usec; + * } + * } + * + * void timer_heap_insert(smartlist_t *heap, timer_t *timer) { + * smartlist_pqueue_add(heap, compare, STRUCT_OFFSET(timer_t, heap_index), + * timer); + * } + * + * void timer_heap_pop(smartlist_t *heap) { + * return smartlist_pqueue_pop(heap, compare, + * STRUCT_OFFSET(timer_t, heap_index)); + * } + */ + +/** @{ */ +/** Functions to manipulate heap indices to find a node's parent and children. + * + * For a 1-indexed array, we would use LEFT_CHILD[x] = 2*x and RIGHT_CHILD[x] + * = 2*x + 1. But this is C, so we have to adjust a little. */ +//#define LEFT_CHILD(i) ( ((i)+1)*2 - 1) +//#define RIGHT_CHILD(i) ( ((i)+1)*2 ) +//#define PARENT(i) ( ((i)+1)/2 - 1) +#define LEFT_CHILD(i) ( 2*(i) + 1 ) +#define RIGHT_CHILD(i) ( 2*(i) + 2 ) +#define PARENT(i) ( ((i)-1) / 2 ) +/** }@ */ + +/** @{ */ +/** Helper macros for heaps: Given a local variable <b>idx_field_offset</b> + * set to the offset of an integer index within the heap element structure, + * IDX_OF_ITEM(p) gives you the index of p, and IDXP(p) gives you a pointer to + * where p's index is stored. Given additionally a local smartlist <b>sl</b>, + * UPDATE_IDX(i) sets the index of the element at <b>i</b> to the correct + * value (that is, to <b>i</b>). + */ +#define IDXP(p) ((int*)( ((char*)(p)) + idx_field_offset )) + +#define UPDATE_IDX(i) do { \ + void *updated = sl->list[i]; \ + *IDXP(updated) = i; \ + } while (0) + +#define IDX_OF_ITEM(p) (*IDXP(p)) +/** @} */ + +/** Helper. <b>sl</b> may have at most one violation of the heap property: + * the item at <b>idx</b> may be greater than one or both of its children. + * Restore the heap property. */ +static inline void +smartlist_heapify(smartlist_t *sl, + int (*compare)(const void *a, const void *b), + int idx_field_offset, + int idx) +{ + while (1) { + int left_idx = LEFT_CHILD(idx); + int best_idx; + + if (left_idx >= sl->num_used) + return; + if (compare(sl->list[idx],sl->list[left_idx]) < 0) + best_idx = idx; + else + best_idx = left_idx; + if (left_idx+1 < sl->num_used && + compare(sl->list[left_idx+1],sl->list[best_idx]) < 0) + best_idx = left_idx + 1; + + if (best_idx == idx) { + return; + } else { + void *tmp = sl->list[idx]; + sl->list[idx] = sl->list[best_idx]; + sl->list[best_idx] = tmp; + UPDATE_IDX(idx); + UPDATE_IDX(best_idx); + + idx = best_idx; + } + } +} + +/** Insert <b>item</b> into the heap stored in <b>sl</b>, where order is + * determined by <b>compare</b> and the offset of the item in the heap is + * stored in an int-typed field at position <b>idx_field_offset</b> within + * item. + */ +void +smartlist_pqueue_add(smartlist_t *sl, + int (*compare)(const void *a, const void *b), + int idx_field_offset, + void *item) +{ + int idx; + smartlist_add(sl,item); + UPDATE_IDX(sl->num_used-1); + + for (idx = sl->num_used - 1; idx; ) { + int parent = PARENT(idx); + if (compare(sl->list[idx], sl->list[parent]) < 0) { + void *tmp = sl->list[parent]; + sl->list[parent] = sl->list[idx]; + sl->list[idx] = tmp; + UPDATE_IDX(parent); + UPDATE_IDX(idx); + idx = parent; + } else { + return; + } + } +} + +/** Remove and return the top-priority item from the heap stored in <b>sl</b>, + * where order is determined by <b>compare</b> and the item's position is + * stored at position <b>idx_field_offset</b> within the item. <b>sl</b> must + * not be empty. */ +void * +smartlist_pqueue_pop(smartlist_t *sl, + int (*compare)(const void *a, const void *b), + int idx_field_offset) +{ + void *top; + obfs_assert(sl->num_used); + + top = sl->list[0]; + *IDXP(top)=-1; + if (--sl->num_used) { + sl->list[0] = sl->list[sl->num_used]; + UPDATE_IDX(0); + smartlist_heapify(sl, compare, idx_field_offset, 0); + } + return top; +} + +/** Remove the item <b>item</b> from the heap stored in <b>sl</b>, + * where order is determined by <b>compare</b> and the item's position is + * stored at position <b>idx_field_offset</b> within the item. <b>sl</b> must + * not be empty. */ +void +smartlist_pqueue_remove(smartlist_t *sl, + int (*compare)(const void *a, const void *b), + int idx_field_offset, + void *item) +{ + int idx = IDX_OF_ITEM(item); + obfs_assert(idx >= 0); + obfs_assert(sl->list[idx] == item); + --sl->num_used; + *IDXP(item) = -1; + if (idx == sl->num_used) { + return; + } else { + sl->list[idx] = sl->list[sl->num_used]; + UPDATE_IDX(idx); + smartlist_heapify(sl, compare, idx_field_offset, idx); + } +} + +/** Assert that the heap property is correctly maintained by the heap stored + * in <b>sl</b>, where order is determined by <b>compare</b>. */ +void +smartlist_pqueue_assert_ok(smartlist_t *sl, + int (*compare)(const void *a, const void *b), + int idx_field_offset) +{ + int i; + for (i = sl->num_used - 1; i >= 0; --i) { + if (i>0) + obfs_assert(compare(sl->list[PARENT(i)], sl->list[i]) <= 0); + obfs_assert(IDX_OF_ITEM(sl->list[i]) == i); + } +} + +/** Helper: compare two SHA256_LENGTH digests. */ +static int +_compare_digests(const void **_a, const void **_b) +{ + return memcmp((const char*)*_a, (const char*)*_b, SHA256_LENGTH); +} + +/** Sort the list of SHA256_LENGTH-byte digests into ascending order. */ +void +smartlist_sort_digests(smartlist_t *sl) +{ + smartlist_sort(sl, _compare_digests); +} + +/** Remove duplicate digests from a sorted list, and free them with free(). + */ +void +smartlist_uniq_digests(smartlist_t *sl) +{ + smartlist_uniq(sl, _compare_digests, 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[SHA256_LENGTH], digestmap_); + +/** 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 ht_string_hash(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 !memcmp(a->key, b->key, SHA256_LENGTH); +} + +/** Helper: return a hash value for a digest_map_t. */ +static inline unsigned int +digestmap_entry_hash(const digestmap_entry_t *a) +{ +#if SIZEOF_INT != 8 + const uint32_t *p = (const uint32_t*)a->key; + return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; +#else + const uint64_t *p = (const uint64_t*)a->key; + return p[0] ^ p[1]; +#endif +} + +HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, + strmap_entries_eq) +HT_GENERATE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, + strmap_entries_eq, 0.6, malloc, realloc, free) + +HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, + digestmap_entries_eq) +HT_GENERATE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, + digestmap_entries_eq, 0.6, malloc, realloc, free) + +/** Constructor to create a new empty map from strings to void*'s. + */ +strmap_t * +strmap_new(void) +{ + strmap_t *result; + result = xmalloc(sizeof(strmap_t)); + HT_INIT(strmap_impl, &result->head); + return result; +} + +/** Constructor to create a new empty map from digests to void*'s. + */ +digestmap_t * +digestmap_new(void) +{ + digestmap_t *result; + result = xmalloc(sizeof(digestmap_t)); + HT_INIT(digestmap_impl, &result->head); + return result; +} + +/** Set the current value for <b>key</b> to <b>val</b>. Returns the previous + * value for <b>key</b> if one was set, or NULL if one was not. + * + * This function makes a copy of <b>key</b> if necessary, but not of + * <b>val</b>. + */ +void * +strmap_set(strmap_t *map, const char *key, void *val) +{ + strmap_entry_t *resolve; + strmap_entry_t search; + void *oldval; + obfs_assert(map); + obfs_assert(key); + obfs_assert(val); + search.key = (char*)key; + resolve = HT_FIND(strmap_impl, &map->head, &search); + if (resolve) { + oldval = resolve->val; + resolve->val = val; + return oldval; + } else { + resolve = xzalloc(sizeof(strmap_entry_t)); + resolve->key = xstrdup(key); + resolve->val = val; + obfs_assert(!HT_FIND(strmap_impl, &map->head, resolve)); + HT_INSERT(strmap_impl, &map->head, resolve); + return NULL; + } +} + +#define OPTIMIZED_DIGESTMAP_SET + +/** Like strmap_set() above but for digestmaps. */ +void * +digestmap_set(digestmap_t *map, const char *key, void *val) +{ +#ifndef OPTIMIZED_DIGESTMAP_SET + digestmap_entry_t *resolve; +#endif + digestmap_entry_t search; + void *oldval; + obfs_assert(map); + obfs_assert(key); + obfs_assert(val); + memcpy(&search.key, key, SHA256_LENGTH); +#ifndef OPTIMIZED_DIGESTMAP_SET + resolve = HT_FIND(digestmap_impl, &map->head, &search); + if (resolve) { + oldval = resolve->val; + resolve->val = val; + return oldval; + } else { + resolve = xzalloc(sizeof(digestmap_entry_t)); + memcpy(resolve->key, key, SHA256_LENGTH); + resolve->val = val; + HT_INSERT(digestmap_impl, &map->head, resolve); + return NULL; + } +#else + /* We spend up to 5% 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 do in the unoptimized code above. (Each of + * HT_INSERT and HT_FIND calls HT_SET_HASH and HT_FIND_P.) + */ + _HT_FIND_OR_INSERT(digestmap_impl, node, digestmap_entry_hash, &(map->head), + digestmap_entry_t, &search, ptr, + { + /* we found an entry. */ + oldval = (*ptr)->val; + (*ptr)->val = val; + return oldval; + }, + { + /* We didn't find the entry. */ + digestmap_entry_t *newent = + xzalloc(sizeof(digestmap_entry_t)); + memcpy(newent->key, key, SHA256_LENGTH); + newent->val = val; + _HT_FOI_INSERT(node, &(map->head), &search, newent, ptr); + return NULL; + }); +#endif +} + +/** Return the current value associated with <b>key</b>, or NULL if no + * value is set. + */ +void * +strmap_get(const strmap_t *map, const char *key) +{ + strmap_entry_t *resolve; + strmap_entry_t search; + obfs_assert(map); + obfs_assert(key); + search.key = (char*)key; + resolve = HT_FIND(strmap_impl, &map->head, &search); + if (resolve) { + return resolve->val; + } else { + return NULL; + } +} + +/** Like strmap_get() above but for digestmaps. */ +void * +digestmap_get(const digestmap_t *map, const char *key) +{ + digestmap_entry_t *resolve; + digestmap_entry_t search; + obfs_assert(map); + obfs_assert(key); + memcpy(&search.key, key, SHA256_LENGTH); + resolve = HT_FIND(digestmap_impl, &map->head, &search); + if (resolve) { + return resolve->val; + } else { + 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 * +strmap_remove(strmap_t *map, const char *key) +{ + strmap_entry_t *resolve; + strmap_entry_t search; + void *oldval; + obfs_assert(map); + obfs_assert(key); + search.key = (char*)key; + resolve = HT_REMOVE(strmap_impl, &map->head, &search); + if (resolve) { + oldval = resolve->val; + free(resolve->key); + free(resolve); + return oldval; + } else { + return NULL; + } +} + +/** Like strmap_remove() above but for digestmaps. */ +void * +digestmap_remove(digestmap_t *map, const char *key) +{ + digestmap_entry_t *resolve; + digestmap_entry_t search; + void *oldval; + obfs_assert(map); + obfs_assert(key); + memcpy(&search.key, key, SHA256_LENGTH); + resolve = HT_REMOVE(digestmap_impl, &map->head, &search); + if (resolve) { + oldval = resolve->val; + free(resolve); + return oldval; + } else { + return NULL; + } +} + +/** 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 = xstrdup(key); + ascii_strlower(lc_key); + v = strmap_set(map,lc_key,val); + 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 = xstrdup(key); + ascii_strlower(lc_key); + v = strmap_get(map,lc_key); + 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 = xstrdup(key); + ascii_strlower(lc_key); + v = strmap_remove(map,lc_key); + free(lc_key); + return v; +} + +/** 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); + * iter = strmap_iter_next(map,iter); + * } + * } + * \endcode + * + */ +strmap_iter_t * +strmap_iter_init(strmap_t *map) +{ + obfs_assert(map); + return HT_START(strmap_impl, &map->head); +} + +/** Start iterating through <b>map</b>. See strmap_iter_init() for example. */ +digestmap_iter_t * +digestmap_iter_init(digestmap_t *map) +{ + obfs_assert(map); + return HT_START(digestmap_impl, &map->head); +} + +/** Advance the iterator <b>iter</b> for <b>map</b> a single step to the next + * entry, and return its new value. */ +strmap_iter_t * +strmap_iter_next(strmap_t *map, strmap_iter_t *iter) +{ + obfs_assert(map); + obfs_assert(iter); + return HT_NEXT(strmap_impl, &map->head, iter); +} + +/** Advance the iterator <b>iter</b> for map a single step to the next entry, + * and return its new value. */ +digestmap_iter_t * +digestmap_iter_next(digestmap_t *map, digestmap_iter_t *iter) +{ + obfs_assert(map); + obfs_assert(iter); + return HT_NEXT(digestmap_impl, &map->head, iter); +} + +/** Advance the iterator <b>iter</b> a single step to the next entry, removing + * the current entry, and return its new value. + */ +strmap_iter_t * +strmap_iter_next_rmv(strmap_t *map, strmap_iter_t *iter) +{ + strmap_entry_t *rmv; + obfs_assert(map); + obfs_assert(iter); + obfs_assert(*iter); + rmv = *iter; + iter = HT_NEXT_RMV(strmap_impl, &map->head, iter); + free(rmv->key); + free(rmv); + return iter; +} + +/** Advance the iterator <b>iter</b> a single step to the next entry, removing + * the current entry, and return its new value. + */ +digestmap_iter_t * +digestmap_iter_next_rmv(digestmap_t *map, digestmap_iter_t *iter) +{ + digestmap_entry_t *rmv; + obfs_assert(map); + obfs_assert(iter); + obfs_assert(*iter); + rmv = *iter; + iter = HT_NEXT_RMV(digestmap_impl, &map->head, iter); + free(rmv); + return iter; +} + +/** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed to by + * iter. */ +void +strmap_iter_get(strmap_iter_t *iter, const char **keyp, void **valp) +{ + obfs_assert(iter); + obfs_assert(*iter); + obfs_assert(keyp); + obfs_assert(valp); + *keyp = (*iter)->key; + *valp = (*iter)->val; +} + +/** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed to by + * iter. */ +void +digestmap_iter_get(digestmap_iter_t *iter, const char **keyp, void **valp) +{ + obfs_assert(iter); + obfs_assert(*iter); + obfs_assert(keyp); + obfs_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 +strmap_iter_done(strmap_iter_t *iter) +{ + return iter == NULL; +} + +/** Return true iff <b>iter</b> has advanced past the last entry of + * <b>map</b>. */ +int +digestmap_iter_done(digestmap_iter_t *iter) +{ + return iter == NULL; +} + +/** Remove all entries from <b>map</b>, and deallocate storage for those + * entries. If free_val is provided, it is invoked on every value in + * <b>map</b>. + */ +void +strmap_free(strmap_t *map, void (*free_val)(void*)) +{ + strmap_entry_t **ent, **next, *this; + if (!map) + return; + + for (ent = HT_START(strmap_impl, &map->head); ent != NULL; ent = next) { + this = *ent; + next = HT_NEXT_RMV(strmap_impl, &map->head, ent); + free(this->key); + if (free_val) + free_val(this->val); + free(this); + } + obfs_assert(HT_EMPTY(&map->head)); + HT_CLEAR(strmap_impl, &map->head); + free(map); +} + +/** Remove all entries from <b>map</b>, and deallocate storage for those + * entries. If free_val is provided, it is invoked on every value in + * <b>map</b>. + */ +void +digestmap_free(digestmap_t *map, void (*free_val)(void*)) +{ + digestmap_entry_t **ent, **next, *this; + if (!map) + return; + for (ent = HT_START(digestmap_impl, &map->head); ent != NULL; ent = next) { + this = *ent; + next = HT_NEXT_RMV(digestmap_impl, &map->head, ent); + if (free_val) + free_val(this->val); + free(this); + } + obfs_assert(HT_EMPTY(&map->head)); + HT_CLEAR(digestmap_impl, &map->head); + free(map); +} + +/** Fail with an assertion error if anything has gone wrong with the internal + * representation of <b>map</b>. */ +void +strmap_assert_ok(const strmap_t *map) +{ + obfs_assert(!_strmap_impl_HT_REP_IS_BAD(&map->head)); +} +/** Fail with an assertion error if anything has gone wrong with the internal + * representation of <b>map</b>. */ +void +digestmap_assert_ok(const digestmap_t *map) +{ + obfs_assert(!_digestmap_impl_HT_REP_IS_BAD(&map->head)); +} + +/** Return true iff <b>map</b> has no entries. */ +int +strmap_isempty(const strmap_t *map) +{ + return HT_EMPTY(&map->head); +} + +/** Return true iff <b>map</b> has no entries. */ +int +digestmap_isempty(const digestmap_t *map) +{ + return HT_EMPTY(&map->head); +} + +/** Return the number of items in <b>map</b>. */ +int +strmap_size(const strmap_t *map) +{ + return HT_SIZE(&map->head); +} + +/** Return the number of items in <b>map</b>. */ +int +digestmap_size(const digestmap_t *map) +{ + return HT_SIZE(&map->head); +} + +/** 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) \ + { \ + obfs_assert(nth >= 0); \ + obfs_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 << (ui64_log2(max_elements)+5); + digestset_t *r = xmalloc(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); + free(set); +} diff --git a/src/container.h b/src/container.h new file mode 100644 index 0000000..e1c1a07 --- /dev/null +++ b/src/container.h @@ -0,0 +1,685 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2011, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef CONTAINER_H +#define CONTAINER_H + +#include <stdlib.h> +#include <string.h> +#include <time.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; + +smartlist_t *smartlist_create(void); +void smartlist_free(smartlist_t *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_remove(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_isin(const smartlist_t *sl, const void *element) ATTR_PURE; +int smartlist_string_isin(const smartlist_t *sl, const char *element) + ATTR_PURE; +int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE; +int smartlist_string_isin_case(const smartlist_t *sl, const char *element) + ATTR_PURE; +int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; +int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) + ATTR_PURE; +int smartlist_digest_isin(const smartlist_t *sl, const char *element) + ATTR_PURE; +int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) + ATTR_PURE; +void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); +void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); + +#ifdef DEBUG_SMARTLIST +/** Return the number of items in sl. + */ +static inline int smartlist_len(const smartlist_t *sl) ATTR_PURE; +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) ATTR_PURE; +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 +#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 + +/** 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)); +void smartlist_uniq(smartlist_t *sl, + int (*compare)(const void **a, const void **b), + void (*free_fn)(void *elt)); + +void smartlist_shuffle(smartlist_t *sl); +void *smartlist_choose(const smartlist_t *sl); + +void smartlist_sort_strings(smartlist_t *sl); +void smartlist_sort_digests(smartlist_t *sl); + +char *smartlist_get_most_frequent_string(smartlist_t *sl); + +void smartlist_uniq_strings(smartlist_t *sl); +void smartlist_uniq_digests(smartlist_t *sl); +void *smartlist_bsearch(smartlist_t *sl, const void *key, + int (*compare)(const void *key, const void **member)) + ATTR_PURE; +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 statement <b>cmd</b>. 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(list, char *, cp, + * { + * printf("%d: %s\n", cp_sl_idx, cp); + * free(cp); + * }); + * smartlist_free(list); + * </pre> + * + * Example use (advanced): + * <pre> + * SMARTLIST_FOREACH(list, char *, cp, + * { + * if (!strcmp(cp, "junk")) { + * free(cp); + * SMARTLIST_DEL_CURRENT(list, 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); + * 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")) { + * free(cp); + * smartlist_del(list, cp_sl_idx); + * --cp_sl_idx; + * --cp_sl_len; + * } + * } + * } + * </pre> + */ +#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \ + do { \ + 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; \ + } } while (0) + +#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) \ + do { \ + smartlist_del(sl, var ## _sl_idx); \ + --var ## _sl_idx; \ + --var ## _sl_len; \ + } while (0) + +/** 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) \ + do { \ + smartlist_set(sl, var ## _sl_idx, val); \ + } while (0) + +/* 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, + * 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 = 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) \ + do { \ + 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) \ + } \ + } while (0) + +#define DECLARE_MAP_FNS(maptype, keytype, prefix) \ + typedef struct maptype maptype; \ + typedef struct prefix##entry_t *prefix##iter_t; \ + 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); \ + 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_); + +#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) \ + do { \ + 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) \ + do { \ + 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) \ + do { \ + keyvar##_del = 1; \ + } while (0) + +/** Used to end a MAP_FOREACH() block. */ +#define MAP_FOREACH_END } while (0) + +/** 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 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; \ + static inline maptype* prefix##new(void) \ + { \ + return (maptype*)digestmap_new(); \ + } \ + static inline digestmap_t* prefix##to_digestmap(maptype *map) \ + { \ + return (digestmap_t*)map; \ + } \ + static inline valtype* prefix##get(maptype *map, const char *key) \ + { \ + return (valtype*)digestmap_get((digestmap_t*)map, key); \ + } \ + static inline valtype* prefix##set(maptype *map, const char *key, \ + valtype *val) \ + { \ + return (valtype*)digestmap_set((digestmap_t*)map, key, val); \ + } \ + static inline valtype* prefix##remove(maptype *map, const char *key) \ + { \ + return (valtype*)digestmap_remove((digestmap_t*)map, key); \ + } \ + static inline void prefix##free(maptype *map, void (*free_val)(void*)) \ + { \ + digestmap_free((digestmap_t*)map, free_val); \ + } \ + static inline int prefix##isempty(maptype *map) \ + { \ + return digestmap_isempty((digestmap_t*)map); \ + } \ + static inline int prefix##size(maptype *map) \ + { \ + return digestmap_size((digestmap_t*)map); \ + } \ + static inline prefix##iter_t *prefix##iter_init(maptype *map) \ + { \ + return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \ + } \ + 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); \ + } \ + 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); \ + } \ + 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; \ + } \ + 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 +#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 xzalloc(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 = xrealloc(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) +{ + 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 uint32_t *p = (const uint32_t *)digest; + const uint32_t d1 = p[0] + (p[1]>>16); + const uint32_t d2 = p[1] + (p[2]>>16); + const uint32_t d3 = p[2] + (p[3]>>16); + const uint32_t d4 = p[3] + (p[0]>>16); + 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_isin(const digestset_t *set, const char *digest) +{ + const uint32_t *p = (const uint32_t *)digest; + const uint32_t d1 = p[0] + (p[1]>>16); + const uint32_t d2 = p[1] + (p[2]>>16); + const uint32_t d3 = p[2] + (p[3]>>16); + const uint32_t d4 = p[3] + (p[0]>>16); + 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); + +/* 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 long +median_long(long *array, int n_elements) +{ + return find_nth_long(array, n_elements, (n_elements-1)/2); +} + +#endif diff --git a/src/crypt.c b/src/crypt.c index e28e061..98516e6 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -211,3 +211,27 @@ random_bytes(uchar *buf, size_t buflen) { return RAND_bytes(buf, buflen) == 1 ? 0 : -1; } + + +/** Return a pseudorandom integer, chosen uniformly from the values + * between 0 and <b>max</b>-1 inclusive. <b>max</b> must be between 1 and + * INT_MAX+1, inclusive. */ +int +random_int(unsigned int max) +{ + unsigned int val; + unsigned int cutoff; + obfs_assert(max <= ((unsigned int)INT_MAX)+1); + obfs_assert(max > 0); /* don't div by 0 */ + + /* We ignore any values that are >= 'cutoff,' to avoid biasing the + * distribution with clipping at the upper end of unsigned int's + * range. + */ + cutoff = UINT_MAX - (UINT_MAX%max); + while (1) { + random_bytes((uchar*)&val, sizeof(val)); + if (val < cutoff) + return val % max; + } +} diff --git a/src/crypt.h b/src/crypt.h index 3f1e4df..beccda6 100644 --- a/src/crypt.h +++ b/src/crypt.h @@ -45,6 +45,11 @@ void crypt_free(crypt_t *); /** Set b to contain n random bytes. */ int random_bytes(uchar *b, size_t n);
+/** Return a random integer in the range [0, max). + * 'max' must be between 1 and INT_MAX+1, inclusive. + */ +int random_int(unsigned int max); + #ifdef CRYPT_PRIVATE
#include <openssl/aes.h> diff --git a/src/ht.h b/src/ht.h new file mode 100644 index 0000000..8c45db1 --- /dev/null +++ b/src/ht.h @@ -0,0 +1,471 @@ +/* Copyright (c) 2002, Christopher Clark. + * Copyright (c) 2005-2006, Nick Mathewson. + * Copyright (c) 2007-2011, The Tor Project, Inc. */ +/* See license at end. */ + +/* Based on ideas by Christopher Clark and interfaces from Niels Provos. */ + +#ifndef HT_H +#define HT_H + +#define HT_HEAD(name, type) \ + struct name { \ + /* The hash table itself. */ \ + struct type **hth_table; \ + /* How long is the hash table? */ \ + unsigned hth_table_length; \ + /* How many elements does the table contain? */ \ + unsigned hth_n_entries; \ + /* How many elements will we allow in the table before resizing it? */ \ + unsigned hth_load_limit; \ + /* Position of hth_table_length in the primes table. */ \ + int hth_prime_idx; \ + } + +#define HT_INITIALIZER() \ + { NULL, 0, 0, 0, -1 } + +#define HT_ENTRY(type) \ + struct { \ + struct type *hte_next; \ + unsigned hte_hash; \ + } + +#define HT_EMPTY(head) \ + ((head)->hth_n_entries == 0) + +/* Helper: alias for the bucket containing 'elm'. */ +#define _HT_BUCKET(head, field, elm) \ + ((head)->hth_table[elm->field.hte_hash % head->hth_table_length]) + +/* How many elements in 'head'? */ +#define HT_SIZE(head) \ + ((head)->hth_n_entries) + +/* Return memory usage for a hashtable (not counting the entries themselves) */ +#define HT_MEM_USAGE(head) \ + (sizeof(*head) + (head)->hth_table_length * sizeof(void*)) + +#define HT_FIND(name, head, elm) name##_HT_FIND((head), (elm)) +#define HT_INSERT(name, head, elm) name##_HT_INSERT((head), (elm)) +#define HT_REPLACE(name, head, elm) name##_HT_REPLACE((head), (elm)) +#define HT_REMOVE(name, head, elm) name##_HT_REMOVE((head), (elm)) +#define HT_START(name, head) name##_HT_START(head) +#define HT_NEXT(name, head, elm) name##_HT_NEXT((head), (elm)) +#define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm)) +#define HT_CLEAR(name, head) name##_HT_CLEAR(head) +#define HT_INIT(name, head) name##_HT_INIT(head) +/* Helper: */ +static inline unsigned +ht_improve_hash(unsigned h) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + h += ~(h << 9); + h ^= ((h >> 14) | (h << 18)); /* >>> */ + h += (h << 4); + h ^= ((h >> 10) | (h << 22)); /* >>> */ + return h; +} + +#if 0 +/** Basic string hash function, from Java standard String.hashCode(). */ +static inline unsigned +ht_string_hash(const char *s) +{ + unsigned h = 0; + int m = 1; + while (*s) { + h += ((signed char)*s++)*m; + m = (m<<5)-1; /* m *= 31 */ + } + return h; +} +#endif + +/** Basic string hash function, from Python's str.__hash__() */ +static inline unsigned +ht_string_hash(const char *s) +{ + unsigned h; + const unsigned char *cp = (const unsigned char *)s; + h = *cp << 7; + while (*cp) { + h = (1000003*h) ^ *cp++; + } + /* This conversion truncates the length of the string, but that's ok. */ + h ^= (unsigned)(cp-(const unsigned char*)s); + return h; +} + +#define _HT_SET_HASH(elm, field, hashfn) \ + (elm)->field.hte_hash = hashfn(elm) + +#define HT_FOREACH(x, name, head) \ + for ((x) = HT_START(name, head); \ + (x) != NULL; \ + (x) = HT_NEXT(name, head, x)) + +#define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \ + int name##_HT_GROW(struct name *ht, unsigned min_capacity); \ + void name##_HT_CLEAR(struct name *ht); \ + int _##name##_HT_REP_IS_BAD(const struct name *ht); \ + static inline void \ + name##_HT_INIT(struct name *head) { \ + head->hth_table_length = 0; \ + head->hth_table = NULL; \ + head->hth_n_entries = 0; \ + head->hth_load_limit = 0; \ + head->hth_prime_idx = -1; \ + } \ + /* Helper: returns a pointer to the right location in the table \ + * 'head' to find or insert the element 'elm'. */ \ + static inline struct type ** \ + _##name##_HT_FIND_P(struct name *head, struct type *elm) \ + { \ + struct type **p; \ + if (!head->hth_table) \ + return NULL; \ + p = &_HT_BUCKET(head, field, elm); \ + while (*p) { \ + if (eqfn(*p, elm)) \ + return p; \ + p = &(*p)->field.hte_next; \ + } \ + return p; \ + } \ + /* Return a pointer to the element in the table 'head' matching 'elm', \ + * or NULL if no such element exists */ \ + static inline struct type * \ + name##_HT_FIND(const struct name *head, struct type *elm) \ + { \ + struct type **p; \ + struct name *h = (struct name *) head; \ + _HT_SET_HASH(elm, field, hashfn); \ + p = _##name##_HT_FIND_P(h, elm); \ + return p ? *p : NULL; \ + } \ + /* Insert the element 'elm' into the table 'head'. Do not call this \ + * function if the table might already contain a matching element. */ \ + static inline void \ + name##_HT_INSERT(struct name *head, struct type *elm) \ + { \ + struct type **p; \ + if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ + name##_HT_GROW(head, head->hth_n_entries+1); \ + ++head->hth_n_entries; \ + _HT_SET_HASH(elm, field, hashfn); \ + p = &_HT_BUCKET(head, field, elm); \ + elm->field.hte_next = *p; \ + *p = elm; \ + } \ + /* Insert the element 'elm' into the table 'head'. If there already \ + * a matching element in the table, replace that element and return \ + * it. */ \ + static inline struct type * \ + name##_HT_REPLACE(struct name *head, struct type *elm) \ + { \ + struct type **p, *r; \ + if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ + name##_HT_GROW(head, head->hth_n_entries+1); \ + _HT_SET_HASH(elm, field, hashfn); \ + p = _##name##_HT_FIND_P(head, elm); \ + r = *p; \ + *p = elm; \ + if (r && (r!=elm)) { \ + elm->field.hte_next = r->field.hte_next; \ + r->field.hte_next = NULL; \ + return r; \ + } else { \ + ++head->hth_n_entries; \ + return NULL; \ + } \ + } \ + /* Remove any element matching 'elm' from the table 'head'. If such \ + * an element is found, return it; otherwise return NULL. */ \ + static inline struct type * \ + name##_HT_REMOVE(struct name *head, struct type *elm) \ + { \ + struct type **p, *r; \ + _HT_SET_HASH(elm, field, hashfn); \ + p = _##name##_HT_FIND_P(head,elm); \ + if (!p || !*p) \ + return NULL; \ + r = *p; \ + *p = r->field.hte_next; \ + r->field.hte_next = NULL; \ + --head->hth_n_entries; \ + return r; \ + } \ + /* Invoke the function 'fn' on every element of the table 'head', \ + * using 'data' as its second argument. If the function returns \ + * nonzero, remove the most recently examined element before invoking \ + * the function again. */ \ + static inline void \ + name##_HT_FOREACH_FN(struct name *head, \ + int (*fn)(struct type *, void *), \ + void *data) \ + { \ + unsigned idx; \ + int remove; \ + struct type **p, **nextp, *next; \ + if (!head->hth_table) \ + return; \ + for (idx=0; idx < head->hth_table_length; ++idx) { \ + p = &head->hth_table[idx]; \ + while (*p) { \ + nextp = &(*p)->field.hte_next; \ + next = *nextp; \ + remove = fn(*p, data); \ + if (remove) { \ + --head->hth_n_entries; \ + *p = next; \ + } else { \ + p = nextp; \ + } \ + } \ + } \ + } \ + /* Return a pointer to the first element in the table 'head', under \ + * an arbitrary order. This order is stable under remove operations, \ + * but not under others. If the table is empty, return NULL. */ \ + static inline struct type ** \ + name##_HT_START(struct name *head) \ + { \ + unsigned b = 0; \ + while (b < head->hth_table_length) { \ + if (head->hth_table[b]) \ + return &head->hth_table[b]; \ + ++b; \ + } \ + return NULL; \ + } \ + /* Return the next element in 'head' after 'elm', under the arbitrary \ + * order used by HT_START. If there are no more elements, return \ + * NULL. If 'elm' is to be removed from the table, you must call \ + * this function for the next value before you remove it. \ + */ \ + static inline struct type ** \ + name##_HT_NEXT(struct name *head, struct type **elm) \ + { \ + if ((*elm)->field.hte_next) { \ + return &(*elm)->field.hte_next; \ + } else { \ + unsigned b = ((*elm)->field.hte_hash % head->hth_table_length)+1; \ + while (b < head->hth_table_length) { \ + if (head->hth_table[b]) \ + return &head->hth_table[b]; \ + ++b; \ + } \ + return NULL; \ + } \ + } \ + static inline struct type ** \ + name##_HT_NEXT_RMV(struct name *head, struct type **elm) \ + { \ + unsigned h = (*elm)->field.hte_hash; \ + *elm = (*elm)->field.hte_next; \ + --head->hth_n_entries; \ + if (*elm) { \ + return elm; \ + } else { \ + unsigned b = (h % head->hth_table_length)+1; \ + while (b < head->hth_table_length) { \ + if (head->hth_table[b]) \ + return &head->hth_table[b]; \ + ++b; \ + } \ + return NULL; \ + } \ + } + +#define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \ + reallocfn, freefn) \ + static unsigned name##_PRIMES[] = { \ + 53, 97, 193, 389, \ + 769, 1543, 3079, 6151, \ + 12289, 24593, 49157, 98317, \ + 196613, 393241, 786433, 1572869, \ + 3145739, 6291469, 12582917, 25165843, \ + 50331653, 100663319, 201326611, 402653189, \ + 805306457, 1610612741 \ + }; \ + static unsigned name##_N_PRIMES = \ + (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \ + /* Expand the internal table of 'head' until it is large enough to \ + * hold 'size' elements. Return 0 on success, -1 on allocation \ + * failure. */ \ + int \ + name##_HT_GROW(struct name *head, unsigned size) \ + { \ + unsigned new_len, new_load_limit; \ + int prime_idx; \ + struct type **new_table; \ + if (head->hth_prime_idx == (int)name##_N_PRIMES - 1) \ + return 0; \ + if (head->hth_load_limit > size) \ + return 0; \ + prime_idx = head->hth_prime_idx; \ + do { \ + new_len = name##_PRIMES[++prime_idx]; \ + new_load_limit = (unsigned)(load*new_len); \ + } while (new_load_limit <= size && \ + prime_idx < (int)name##_N_PRIMES); \ + if ((new_table = mallocfn(new_len*sizeof(struct type*)))) { \ + unsigned b; \ + memset(new_table, 0, new_len*sizeof(struct type*)); \ + for (b = 0; b < head->hth_table_length; ++b) { \ + struct type *elm, *next; \ + unsigned b2; \ + elm = head->hth_table[b]; \ + while (elm) { \ + next = elm->field.hte_next; \ + b2 = elm->field.hte_hash % new_len; \ + elm->field.hte_next = new_table[b2]; \ + new_table[b2] = elm; \ + elm = next; \ + } \ + } \ + if (head->hth_table) \ + freefn(head->hth_table); \ + head->hth_table = new_table; \ + } else { \ + unsigned b, b2; \ + new_table = reallocfn(head->hth_table, new_len*sizeof(struct type*)); \ + if (!new_table) return -1; \ + memset(new_table + head->hth_table_length, 0, \ + (new_len - head->hth_table_length)*sizeof(struct type*)); \ + for (b=0; b < head->hth_table_length; ++b) { \ + struct type *e, **pE; \ + for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \ + b2 = e->field.hte_hash % new_len; \ + if (b2 == b) { \ + pE = &e->field.hte_next; \ + } else { \ + *pE = e->field.hte_next; \ + e->field.hte_next = new_table[b2]; \ + new_table[b2] = e; \ + } \ + } \ + } \ + head->hth_table = new_table; \ + } \ + head->hth_table_length = new_len; \ + head->hth_prime_idx = prime_idx; \ + head->hth_load_limit = new_load_limit; \ + return 0; \ + } \ + /* Free all storage held by 'head'. Does not free 'head' itself, or \ + * individual elements. */ \ + void \ + name##_HT_CLEAR(struct name *head) \ + { \ + if (head->hth_table) \ + freefn(head->hth_table); \ + head->hth_table_length = 0; \ + name##_HT_INIT(head); \ + } \ + /* Debugging helper: return false iff the representation of 'head' is \ + * internally consistent. */ \ + int \ + _##name##_HT_REP_IS_BAD(const struct name *head) \ + { \ + unsigned n, i; \ + struct type *elm; \ + if (!head->hth_table_length) { \ + if (!head->hth_table && !head->hth_n_entries && \ + !head->hth_load_limit && head->hth_prime_idx == -1) \ + return 0; \ + else \ + return 1; \ + } \ + if (!head->hth_table || head->hth_prime_idx < 0 || \ + !head->hth_load_limit) \ + return 2; \ + if (head->hth_n_entries > head->hth_load_limit) \ + return 3; \ + if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \ + return 4; \ + if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \ + return 5; \ + for (n = i = 0; i < head->hth_table_length; ++i) { \ + for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \ + if (elm->field.hte_hash != hashfn(elm)) \ + return 1000 + i; \ + if ((elm->field.hte_hash % head->hth_table_length) != i) \ + return 10000 + i; \ + ++n; \ + } \ + } \ + if (n != head->hth_n_entries) \ + return 6; \ + return 0; \ + } + +/** Implements an over-optimized "find and insert if absent" block; + * not meant for direct usage by typical code, or usage outside the critical + * path.*/ +#define _HT_FIND_OR_INSERT(name, field, hashfn, head, eltype, elm, var, y, n) \ + { \ + struct name *_##var##_head = head; \ + eltype **var; \ + if (!_##var##_head->hth_table || \ + _##var##_head->hth_n_entries >= _##var##_head->hth_load_limit) \ + name##_HT_GROW(_##var##_head, _##var##_head->hth_n_entries+1); \ + _HT_SET_HASH((elm), field, hashfn); \ + var = _##name##_HT_FIND_P(_##var##_head, (elm)); \ + if (*var) { \ + y; \ + } else { \ + n; \ + } \ + } +#define _HT_FOI_INSERT(field, head, elm, newent, var) \ + { \ + newent->field.hte_hash = (elm)->field.hte_hash; \ + newent->field.hte_next = NULL; \ + *var = newent; \ + ++((head)->hth_n_entries); \ + } + +/* + * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code + * by Christopher Clark, retrofit to allow drop-in memory management, and to + * use the same interface as Niels Provos's HT_H. I'm not sure whether this + * is a derived work any more, but whether it is or not, the license below + * applies. + * + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endif + diff --git a/src/test/unittest.c b/src/test/unittest.c index 45757e9..8974c87 100644 --- a/src/test/unittest.c +++ b/src/test/unittest.c @@ -5,11 +5,13 @@ #include "tinytest.h" #include "../crypt.h"
+extern struct testcase_t container_tests[]; extern struct testcase_t crypt_tests[]; extern struct testcase_t obfs2_tests[]; extern struct testcase_t socks_tests[];
struct testgroup_t groups[] = { + { "container/", container_tests }, { "crypt/", crypt_tests }, { "obfs2/", obfs2_tests }, { "socks/", socks_tests }, diff --git a/src/test/unittest_container.c b/src/test/unittest_container.c new file mode 100644 index 0000000..4731139 --- /dev/null +++ b/src/test/unittest_container.c @@ -0,0 +1,772 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2011, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "../util.h" +#include "tinytest_macros.h" + +#include "../container.h" +#include "../crypt.h" + +/** Helper: return a tristate based on comparing the strings in *<b>a</b> and + * *<b>b</b>. */ +static int +_compare_strs(const void **a, const void **b) +{ + const char *s1 = *a, *s2 = *b; + return strcmp(s1, s2); +} + +/** Helper: return a tristate based on comparing the strings in *<b>a</b> and + * *<b>b</b>, excluding a's first character, and ignoring case. */ +static int +_compare_without_first_ch(const void *a, const void **b) +{ + const char *s1 = a, *s2 = *b; + return strcasecmp(s1+1, s2); +} + +/** Run unit tests for basic dynamic-sized array functionality. */ +static void +test_container_smartlist_basic(void *unused) +{ + smartlist_t *sl; + + /* XXXX test sort_digests, uniq_strings, uniq_digests */ + + /* Test smartlist add, del_keeporder, insert, get. */ + sl = smartlist_create(); + smartlist_add(sl, (void*)1); + smartlist_add(sl, (void*)2); + smartlist_add(sl, (void*)3); + smartlist_add(sl, (void*)4); + smartlist_del_keeporder(sl, 1); + smartlist_insert(sl, 1, (void*)22); + smartlist_insert(sl, 0, (void*)0); + smartlist_insert(sl, 5, (void*)555); + tt_ptr_op(smartlist_get(sl,0), ==, (void*)0); + tt_ptr_op(smartlist_get(sl,1), ==, (void*)1); + tt_ptr_op(smartlist_get(sl,2), ==, (void*)22); + tt_ptr_op(smartlist_get(sl,3), ==, (void*)3); + tt_ptr_op(smartlist_get(sl,4), ==, (void*)4); + tt_ptr_op(smartlist_get(sl,5), ==, (void*)555); + /* Try deleting in the middle. */ + smartlist_del(sl, 1); + tt_ptr_op(smartlist_get(sl, 1), ==, (void*)555); + /* Try deleting at the end. */ + smartlist_del(sl, 4); + tt_int_op(smartlist_len(sl), ==, 4); + + /* test isin. */ + tt_assert(smartlist_isin(sl, (void*)3)); + tt_assert(!smartlist_isin(sl, (void*)99)); + + end: + smartlist_free(sl); +} + +/** Run unit tests for smartlist-of-strings functionality. */ +static void +test_container_smartlist_strings(void *unused) +{ + smartlist_t *sl = smartlist_create(); + char *cp=NULL, *cp_alloc=NULL; + size_t sz; + + /* Test split and join */ + tt_int_op(smartlist_len(sl), ==, 0); + smartlist_split_string(sl, "abc", ":", 0, 0); + tt_int_op(smartlist_len(sl), ==, 1); + tt_str_op(smartlist_get(sl, 0), ==, "abc"); + smartlist_split_string(sl, "a::bc::", "::", 0, 0); + tt_int_op(smartlist_len(sl), ==, 4); + tt_str_op(smartlist_get(sl, 1), ==, "a"); + tt_str_op(smartlist_get(sl, 2), ==, "bc"); + tt_str_op(smartlist_get(sl, 3), ==, ""); + cp_alloc = smartlist_join_strings(sl, "", 0, NULL); + tt_str_op(cp_alloc, ==, "abcabc"); + free(cp_alloc); + cp_alloc = smartlist_join_strings(sl, "!", 0, NULL); + tt_str_op(cp_alloc, ==, "abc!a!bc!"); + free(cp_alloc); + cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); + tt_str_op(cp_alloc, ==, "abcXYaXYbcXY"); + free(cp_alloc); + cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); + tt_str_op(cp_alloc, ==, "abcXYaXYbcXYXY"); + free(cp_alloc); + cp_alloc = smartlist_join_strings(sl, "", 1, NULL); + tt_str_op(cp_alloc, ==, "abcabc"); + free(cp_alloc); + + smartlist_split_string(sl, "/def/ /ghijk", "/", 0, 0); + tt_int_op(smartlist_len(sl), ==, 8); + tt_str_op(smartlist_get(sl, 4), ==, ""); + tt_str_op(smartlist_get(sl, 5), ==, "def"); + tt_str_op(smartlist_get(sl, 6), ==, " "); + tt_str_op(smartlist_get(sl, 7), ==, "ghijk"); + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0); + tt_int_op(smartlist_len(sl), ==, 3); + tt_str_op(smartlist_get(sl,0), ==, "a"); + tt_str_op(smartlist_get(sl,1), ==, "bbd"); + tt_str_op(smartlist_get(sl,2), ==, "cdef"); + smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", + SPLIT_SKIP_SPACE, 0); + tt_int_op(smartlist_len(sl), ==, 8); + tt_str_op(smartlist_get(sl,3), ==, "z"); + tt_str_op(smartlist_get(sl,4), ==, "zhasd"); + tt_str_op(smartlist_get(sl,5), ==, ""); + tt_str_op(smartlist_get(sl,6), ==, "bnud"); + tt_str_op(smartlist_get(sl,7), ==, ""); + + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + smartlist_split_string(sl, " ab\tc \td ef ", NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tt_int_op(smartlist_len(sl), ==, 4); + tt_str_op(smartlist_get(sl,0), ==, "ab"); + tt_str_op(smartlist_get(sl,1), ==, "c"); + tt_str_op(smartlist_get(sl,2), ==, "d"); + tt_str_op(smartlist_get(sl,3), ==, "ef"); + smartlist_split_string(sl, "ghi\tj", NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tt_int_op(smartlist_len(sl), ==, 6); + tt_str_op(smartlist_get(sl,4), ==, "ghi"); + tt_str_op(smartlist_get(sl,5), ==, "j"); + + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); + tt_str_op(cp_alloc, ==, ""); + free(cp_alloc); + cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); + tt_str_op(cp_alloc, ==, "XY"); + free(cp_alloc); + + smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tt_int_op(smartlist_len(sl), ==, 3); + tt_str_op(smartlist_get(sl, 0), ==, "z"); + tt_str_op(smartlist_get(sl, 1), ==, "zhasd"); + tt_str_op(smartlist_get(sl, 2), ==, "bnud"); + smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); + tt_int_op(smartlist_len(sl), ==, 5); + tt_str_op(smartlist_get(sl, 3), ==, "z"); + tt_str_op(smartlist_get(sl, 4), ==, "zhasd <> <> bnud<>"); + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + smartlist_split_string(sl, "abcd\n", "\n", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tt_int_op(smartlist_len(sl), ==, 1); + tt_str_op(smartlist_get(sl, 0), ==, "abcd"); + smartlist_split_string(sl, "efgh", "\n", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tt_int_op(smartlist_len(sl), ==, 2); + tt_str_op(smartlist_get(sl, 1), ==, "efgh"); + + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + /* Test swapping, shuffling, and sorting. */ + smartlist_split_string(sl, "the,onion,router,by,arma,and,nickm", ",", 0, 0); + tt_int_op(smartlist_len(sl), ==, 7); + smartlist_sort(sl, _compare_strs); + cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); + tt_str_op(cp_alloc, ==, "and,arma,by,nickm,onion,router,the"); + free(cp_alloc); + smartlist_swap(sl, 1, 5); + cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); + tt_str_op(cp_alloc, ==, "and,router,by,nickm,onion,arma,the"); + free(cp_alloc); + smartlist_shuffle(sl); + tt_int_op(smartlist_len(sl), ==, 7); + tt_assert(smartlist_string_isin(sl, "and")); + tt_assert(smartlist_string_isin(sl, "router")); + tt_assert(smartlist_string_isin(sl, "by")); + tt_assert(smartlist_string_isin(sl, "nickm")); + tt_assert(smartlist_string_isin(sl, "onion")); + tt_assert(smartlist_string_isin(sl, "arma")); + tt_assert(smartlist_string_isin(sl, "the")); + + /* Test bsearch. */ + smartlist_sort(sl, _compare_strs); + tt_str_op(smartlist_bsearch(sl, "zNicKM", + _compare_without_first_ch), ==, "nickm"); + tt_str_op(smartlist_bsearch(sl, " AND", _compare_without_first_ch), ==, "and"); + tt_ptr_op(smartlist_bsearch(sl, " ANz", _compare_without_first_ch), ==, NULL); + + /* Test bsearch_idx */ + { + int f; + tt_int_op(smartlist_bsearch_idx(sl," aaa",_compare_without_first_ch,&f), + ==, 0); + tt_int_op(f, ==, 0); + tt_int_op(smartlist_bsearch_idx(sl," and",_compare_without_first_ch,&f), + ==, 0); + tt_int_op(f, ==, 1); + tt_int_op(smartlist_bsearch_idx(sl," arm",_compare_without_first_ch,&f), + ==, 1); + tt_int_op(f, ==, 0); + tt_int_op(smartlist_bsearch_idx(sl," arma",_compare_without_first_ch,&f), + ==, 1); + tt_int_op(f, ==, 1); + tt_int_op(smartlist_bsearch_idx(sl," armb",_compare_without_first_ch,&f), + ==, 2); + tt_int_op(f, ==, 0); + tt_int_op(smartlist_bsearch_idx(sl," zzzz",_compare_without_first_ch,&f), + ==, 7); + tt_int_op(f, ==, 0); + } + + /* Test reverse() and pop_last() */ + smartlist_reverse(sl); + cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); + tt_str_op(cp_alloc, ==, "the,router,onion,nickm,by,arma,and"); + free(cp_alloc); + cp_alloc = smartlist_pop_last(sl); + tt_str_op(cp_alloc, ==, "and"); + free(cp_alloc); + tt_int_op(smartlist_len(sl), ==, 6); + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + cp_alloc = smartlist_pop_last(sl); + tt_ptr_op(cp_alloc, ==, NULL); + + /* Test uniq() */ + smartlist_split_string(sl, + "50,noon,radar,a,man,a,plan,a,canal,panama,radar,noon,50", + ",", 0, 0); + smartlist_sort(sl, _compare_strs); + smartlist_uniq(sl, _compare_strs, free); + cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); + tt_str_op(cp_alloc, ==, "50,a,canal,man,noon,panama,plan,radar"); + free(cp_alloc); + + /* Test string_isin and isin_case and num_isin */ + tt_assert(smartlist_string_isin(sl, "noon")); + tt_assert(!smartlist_string_isin(sl, "noonoon")); + tt_assert(smartlist_string_isin_case(sl, "nOOn")); + tt_assert(!smartlist_string_isin_case(sl, "nooNooN")); + tt_assert(smartlist_string_num_isin(sl, 50)); + tt_assert(!smartlist_string_num_isin(sl, 60)); + + /* Test smartlist_choose */ + { + int i; + int allsame = 1; + int allin = 1; + void *first = smartlist_choose(sl); + tt_assert(smartlist_isin(sl, first)); + for (i = 0; i < 100; ++i) { + void *second = smartlist_choose(sl); + if (second != first) + allsame = 0; + if (!smartlist_isin(sl, second)) + allin = 0; + } + tt_assert(!allsame); + tt_assert(allin); + } + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_clear(sl); + + /* Test string_remove and remove and join_strings2 */ + smartlist_split_string(sl, + "Some say the Earth will end in ice and some in fire", + " ", 0, 0); + cp = smartlist_get(sl, 4); + tt_str_op(cp, ==, "will"); + smartlist_add(sl, cp); + smartlist_remove(sl, cp); + free(cp); + cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); + tt_str_op(cp_alloc, ==, "Some,say,the,Earth,fire,end,in,ice,and,some,in"); + free(cp_alloc); + smartlist_string_remove(sl, "in"); + cp_alloc = smartlist_join_strings2(sl, "+XX", 1, 0, &sz); + tt_str_op(cp_alloc, ==, "Some+say+the+Earth+fire+end+some+ice+and"); + tt_int_op((int)sz, ==, 40); + + end: + + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_free(sl); + free(cp_alloc); +} + +/** Run unit tests for smartlist set manipulation functions. */ +static void +test_container_smartlist_overlap(void *unused) +{ + smartlist_t *sl = smartlist_create(); + smartlist_t *ints = smartlist_create(); + smartlist_t *odds = smartlist_create(); + smartlist_t *evens = smartlist_create(); + smartlist_t *primes = smartlist_create(); + int i; + for (i=1; i < 10; i += 2) + smartlist_add(odds, (void*)(uintptr_t)i); + for (i=0; i < 10; i += 2) + smartlist_add(evens, (void*)(uintptr_t)i); + + /* add_all */ + smartlist_add_all(ints, odds); + smartlist_add_all(ints, evens); + tt_int_op(smartlist_len(ints), ==, 10); + + smartlist_add(primes, (void*)2); + smartlist_add(primes, (void*)3); + smartlist_add(primes, (void*)5); + smartlist_add(primes, (void*)7); + + /* overlap */ + tt_assert(smartlist_overlap(ints, odds)); + tt_assert(smartlist_overlap(odds, primes)); + tt_assert(smartlist_overlap(evens, primes)); + tt_assert(!smartlist_overlap(odds, evens)); + + /* intersect */ + smartlist_add_all(sl, odds); + smartlist_intersect(sl, primes); + tt_int_op(smartlist_len(sl), ==, 3); + tt_assert(smartlist_isin(sl, (void*)3)); + tt_assert(smartlist_isin(sl, (void*)5)); + tt_assert(smartlist_isin(sl, (void*)7)); + + /* subtract */ + smartlist_add_all(sl, primes); + smartlist_subtract(sl, odds); + tt_int_op(smartlist_len(sl), ==, 1); + tt_assert(smartlist_isin(sl, (void*)2)); + + end: + smartlist_free(odds); + smartlist_free(evens); + smartlist_free(ints); + smartlist_free(primes); + smartlist_free(sl); +} + +/** Run unit tests for smartlist-of-digests functions. */ +static void +test_container_smartlist_digests(void *unused) +{ + smartlist_t *sl = smartlist_create(); + + /* digest_isin. */ + smartlist_add(sl, xmemdup("AAAAAAAAAAAAAAAAAAAA", SHA256_LENGTH)); + smartlist_add(sl, xmemdup("\00090AAB2AAAAaasdAAAAA", SHA256_LENGTH)); + smartlist_add(sl, xmemdup("\00090AAB2AAAAaasdAAAAA", SHA256_LENGTH)); + tt_int_op(smartlist_digest_isin(NULL, "AAAAAAAAAAAAAAAAAAAA"), ==, 0); + tt_assert(smartlist_digest_isin(sl, "AAAAAAAAAAAAAAAAAAAA")); + tt_assert(smartlist_digest_isin(sl, "\00090AAB2AAAAaasdAAAAA")); + tt_int_op(smartlist_digest_isin(sl, "\00090AAB2AAABaasdAAAAA"), ==, 0); + + /* sort digests */ + smartlist_sort_digests(sl); + tt_mem_op(smartlist_get(sl, 0), ==, "\00090AAB2AAAAaasdAAAAA", SHA256_LENGTH); + tt_mem_op(smartlist_get(sl, 1), ==, "\00090AAB2AAAAaasdAAAAA", SHA256_LENGTH); + tt_mem_op(smartlist_get(sl, 2), ==, "AAAAAAAAAAAAAAAAAAAA", SHA256_LENGTH); + tt_int_op(smartlist_len(sl), ==, 3); + + /* uniq_digests */ + smartlist_uniq_digests(sl); + tt_int_op(smartlist_len(sl), ==, 2); + tt_mem_op(smartlist_get(sl, 0), ==, "\00090AAB2AAAAaasdAAAAA", SHA256_LENGTH); + tt_mem_op(smartlist_get(sl, 1), ==, "AAAAAAAAAAAAAAAAAAAA", SHA256_LENGTH); + + end: + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_free(sl); +} + +/** Run unit tests for concatenate-a-smartlist-of-strings functions. */ +static void +test_container_smartlist_join(void *unused) +{ + smartlist_t *sl = smartlist_create(); + smartlist_t *sl2 = smartlist_create(), *sl3 = smartlist_create(), + *sl4 = smartlist_create(); + char *joined=NULL; + /* unique, sorted. */ + smartlist_split_string(sl, + "Abashments Ambush Anchorman Bacon Banks Borscht " + "Bunks Inhumane Insurance Knish Know Manners " + "Maraschinos Stamina Sunbonnets Unicorns Wombats", + " ", 0, 0); + /* non-unique, sorted. */ + smartlist_split_string(sl2, + "Ambush Anchorman Anchorman Anemias Anemias Bacon " + "Crossbowmen Inhumane Insurance Knish Know Manners " + "Manners Maraschinos Wombats Wombats Work", + " ", 0, 0); + SMARTLIST_FOREACH_JOIN(sl, char *, cp1, + sl2, char *, cp2, + strcmp(cp1,cp2), + smartlist_add(sl3, cp2)) { + tt_str_op(cp1, ==, cp2); + smartlist_add(sl4, cp1); + } SMARTLIST_FOREACH_JOIN_END(cp1, cp2); + + SMARTLIST_FOREACH(sl3, const char *, cp, + tt_assert(smartlist_isin(sl2, cp) && + !smartlist_string_isin(sl, cp))); + SMARTLIST_FOREACH(sl4, const char *, cp, + tt_assert(smartlist_isin(sl, cp) && + smartlist_string_isin(sl2, cp))); + joined = smartlist_join_strings(sl3, ",", 0, NULL); + tt_str_op(joined, ==, "Anemias,Anemias,Crossbowmen,Work"); + free(joined); + joined = smartlist_join_strings(sl4, ",", 0, NULL); + tt_str_op(joined, ==, + "Ambush,Anchorman,Anchorman,Bacon,Inhumane,Insurance," + "Knish,Know,Manners,Manners,Maraschinos,Wombats,Wombats"); + + end: + smartlist_free(sl4); + smartlist_free(sl3); + SMARTLIST_FOREACH(sl2, char *, cp, free(cp)); + smartlist_free(sl2); + SMARTLIST_FOREACH(sl, char *, cp, free(cp)); + smartlist_free(sl); + free(joined); +} + +/** Run unit tests for bitarray code */ +static void +test_container_bitarray(void *unused) +{ + bitarray_t *ba = NULL; + int i, j; + + ba = bitarray_init_zero(1); + tt_assert(ba); + tt_assert(! bitarray_is_set(ba, 0)); + bitarray_set(ba, 0); + tt_assert(bitarray_is_set(ba, 0)); + bitarray_clear(ba, 0); + tt_assert(! bitarray_is_set(ba, 0)); + bitarray_free(ba); + + ba = bitarray_init_zero(1023); + for (i = 1; i < 64; ) { + for (j = 0; j < 1023; ++j) { + if (j % i) + bitarray_set(ba, j); + else + bitarray_clear(ba, j); + } + for (j = 0; j < 1023; ++j) { + tt_bool_op(bitarray_is_set(ba, j), ==, j%i); + } + + if (i < 7) + ++i; + else if (i == 28) + i = 32; + else + i += 7; + } + + end: + if (ba) + bitarray_free(ba); +} + +/** Run unit tests for digest set code (implemented as a hashtable or as a + * bloom filter) */ +static void +test_container_digestset(void *unused) +{ + smartlist_t *included = smartlist_create(); + char d[SHA256_LENGTH]; + int i; + int ok = 1; + int false_positives = 0; + digestset_t *set = NULL; + + for (i = 0; i < 1000; ++i) { + random_bytes((uchar *)d, SHA256_LENGTH); + smartlist_add(included, xmemdup(d, SHA256_LENGTH)); + } + set = digestset_new(1000); + SMARTLIST_FOREACH(included, const char *, cp, + if (digestset_isin(set, cp)) + ok = 0); + tt_assert(ok); + SMARTLIST_FOREACH(included, const char *, cp, + digestset_add(set, cp)); + SMARTLIST_FOREACH(included, const char *, cp, + if (!digestset_isin(set, cp)) + ok = 0); + tt_assert(ok); + for (i = 0; i < 1000; ++i) { + random_bytes((uchar *)d, SHA256_LENGTH); + if (digestset_isin(set, d)) + ++false_positives; + } + tt_assert(false_positives < 50); /* Should be far lower. */ + + end: + if (set) + digestset_free(set); + SMARTLIST_FOREACH(included, char *, cp, free(cp)); + smartlist_free(included); +} + +typedef struct pq_entry_t { + const char *val; + int idx; +} pq_entry_t; + +/** Helper: return a tristate based on comparing two pq_entry_t values. */ +static int +_compare_strings_for_pqueue(const void *p1, const void *p2) +{ + const pq_entry_t *e1=p1, *e2=p2; + return strcmp(e1->val, e2->val); +} + +/** Run unit tests for heap-based priority queue functions. */ +static void +test_container_pqueue(void *unused) +{ + smartlist_t *sl = smartlist_create(); + int (*cmp)(const void *, const void*); + const int offset = offsetof(pq_entry_t, idx); +#define ENTRY(s) pq_entry_t s = { #s, -1 } + ENTRY(cows); + ENTRY(zebras); + ENTRY(fish); + ENTRY(frogs); + ENTRY(apples); + ENTRY(squid); + ENTRY(daschunds); + ENTRY(eggplants); + ENTRY(weissbier); + ENTRY(lobsters); + ENTRY(roquefort); + ENTRY(chinchillas); + ENTRY(fireflies); + +#define OK() smartlist_pqueue_assert_ok(sl, cmp, offset) + + cmp = _compare_strings_for_pqueue; + smartlist_pqueue_add(sl, cmp, offset, &cows); + smartlist_pqueue_add(sl, cmp, offset, &zebras); + smartlist_pqueue_add(sl, cmp, offset, &fish); + smartlist_pqueue_add(sl, cmp, offset, &frogs); + smartlist_pqueue_add(sl, cmp, offset, &apples); + smartlist_pqueue_add(sl, cmp, offset, &squid); + smartlist_pqueue_add(sl, cmp, offset, &daschunds); + smartlist_pqueue_add(sl, cmp, offset, &eggplants); + smartlist_pqueue_add(sl, cmp, offset, &weissbier); + smartlist_pqueue_add(sl, cmp, offset, &lobsters); + smartlist_pqueue_add(sl, cmp, offset, &roquefort); + + OK(); + + tt_int_op(smartlist_len(sl), ==, 11); + tt_ptr_op(smartlist_get(sl, 0), ==, &apples); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &apples); + tt_int_op(smartlist_len(sl), ==, 10); + OK(); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &cows); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &daschunds); + smartlist_pqueue_add(sl, cmp, offset, &chinchillas); + OK(); + smartlist_pqueue_add(sl, cmp, offset, &fireflies); + OK(); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &chinchillas); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &eggplants); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &fireflies); + OK(); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &fish); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &frogs); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &lobsters); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &roquefort); + OK(); + tt_int_op(smartlist_len(sl), ==, 3); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &squid); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &weissbier); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &zebras); + tt_int_op(smartlist_len(sl), ==, 0); + OK(); + + /* Now test remove. */ + smartlist_pqueue_add(sl, cmp, offset, &cows); + smartlist_pqueue_add(sl, cmp, offset, &fish); + smartlist_pqueue_add(sl, cmp, offset, &frogs); + smartlist_pqueue_add(sl, cmp, offset, &apples); + smartlist_pqueue_add(sl, cmp, offset, &squid); + smartlist_pqueue_add(sl, cmp, offset, &zebras); + tt_int_op(smartlist_len(sl), ==, 6); + OK(); + smartlist_pqueue_remove(sl, cmp, offset, &zebras); + tt_int_op(smartlist_len(sl), ==, 5); + OK(); + smartlist_pqueue_remove(sl, cmp, offset, &cows); + tt_int_op(smartlist_len(sl), ==, 4); + OK(); + smartlist_pqueue_remove(sl, cmp, offset, &apples); + tt_int_op(smartlist_len(sl), ==, 3); + OK(); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &fish); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &frogs); + tt_ptr_op(smartlist_pqueue_pop(sl, cmp, offset), ==, &squid); + tt_int_op(smartlist_len(sl), ==, 0); + OK(); + +#undef OK + + end: + + smartlist_free(sl); +} + +/** Run unit tests for string-to-void* map functions */ +static void +test_container_strmap(void *unused) +{ + strmap_t *map; + strmap_iter_t *iter; + const char *k; + void *v; + char *visited = NULL; + smartlist_t *found_keys = NULL; + + map = strmap_new(); + tt_assert(map); + tt_int_op(strmap_size(map), ==, 0); + tt_assert(strmap_isempty(map)); + v = strmap_set(map, "K1", (void*)99); + tt_ptr_op(v, ==, NULL); + tt_assert(!strmap_isempty(map)); + v = strmap_set(map, "K2", (void*)101); + tt_ptr_op(v, ==, NULL); + v = strmap_set(map, "K1", (void*)100); + tt_int_op((void*)99, ==, v); + tt_ptr_op(strmap_get(map, "K1"), ==, (void*)100); + tt_ptr_op(strmap_get(map, "K2"), ==, (void*)101); + tt_ptr_op(strmap_get(map, "K-not-there"), ==, NULL); + strmap_assert_ok(map); + + v = strmap_remove(map,"K2"); + strmap_assert_ok(map); + tt_ptr_op(v, ==, (void*)101); + tt_ptr_op(strmap_get(map, "K2"), ==, NULL); + tt_ptr_op(strmap_remove(map, "K2"), ==, NULL); + + strmap_set(map, "K2", (void*)101); + strmap_set(map, "K3", (void*)102); + strmap_set(map, "K4", (void*)103); + tt_int_op(strmap_size(map), ==, 4); + strmap_assert_ok(map); + strmap_set(map, "K5", (void*)104); + strmap_set(map, "K6", (void*)105); + strmap_assert_ok(map); + + /* Test iterator. */ + iter = strmap_iter_init(map); + found_keys = smartlist_create(); + while (!strmap_iter_done(iter)) { + strmap_iter_get(iter,&k,&v); + smartlist_add(found_keys, xstrdup(k)); + tt_ptr_op(v, ==, strmap_get(map, k)); + + if (!strcmp(k, "K2")) { + iter = strmap_iter_next_rmv(map,iter); + } else { + iter = strmap_iter_next(map,iter); + } + } + + /* Make sure we removed K2, but not the others. */ + tt_ptr_op(strmap_get(map, "K2"), ==, NULL); + tt_ptr_op(strmap_get(map, "K5"), ==, (void*)104); + /* Make sure we visited everyone once */ + smartlist_sort_strings(found_keys); + visited = smartlist_join_strings(found_keys, ":", 0, NULL); + tt_str_op(visited, ==, "K1:K2:K3:K4:K5:K6"); + + strmap_assert_ok(map); + /* Clean up after ourselves. */ + strmap_free(map, NULL); + map = NULL; + + /* Now try some lc functions. */ + map = strmap_new(); + strmap_set_lc(map,"Ab.C", (void*)1); + tt_ptr_op(strmap_get(map, "ab.c"), ==, (void*)1); + strmap_assert_ok(map); + tt_ptr_op(strmap_get_lc(map, "AB.C"), ==, (void*)1); + tt_ptr_op(strmap_get(map, "AB.C"), ==, NULL); + tt_ptr_op(strmap_remove_lc(map, "aB.C"), ==, (void*)1); + strmap_assert_ok(map); + tt_ptr_op(strmap_get_lc(map, "AB.C"), ==, NULL); + + end: + if (map) + strmap_free(map,NULL); + if (found_keys) { + SMARTLIST_FOREACH(found_keys, char *, cp, free(cp)); + smartlist_free(found_keys); + } + free(visited); +} + +/** Run unit tests for getting the median of a list. */ +static void +test_container_order_functions(void *unused) +{ + int lst[25], n = 0; + // int a=12,b=24,c=25,d=60,e=77; + +#define median() median_int(lst, n) + + lst[n++] = 12; + tt_int_op(median(), ==, 12); /* 12 */ + lst[n++] = 77; + //smartlist_shuffle(sl); + tt_int_op(median(), ==, 12); /* 12, 77 */ + lst[n++] = 77; + //smartlist_shuffle(sl); + tt_int_op(median(), ==, 77); /* 12, 77, 77 */ + lst[n++] = 24; + tt_int_op(median(), ==, 24); /* 12,24,77,77 */ + lst[n++] = 60; + lst[n++] = 12; + lst[n++] = 25; + //smartlist_shuffle(sl); + tt_int_op(median(), ==, 25); /* 12,12,24,25,60,77,77 */ +#undef median + + end: + ; +} + +#define T(name) \ + { #name, test_container_##name, 0, NULL, NULL } + +struct testcase_t container_tests[] = { + T(smartlist_basic), + T(smartlist_strings), + T(smartlist_overlap), + T(smartlist_digests), + T(smartlist_join), + T(bitarray), + T(digestset), + T(strmap), + T(pqueue), + T(order_functions), + END_OF_TESTCASES +}; + diff --git a/src/util.c b/src/util.c index 231c3a8..9b9fa48 100644 --- a/src/util.c +++ b/src/util.c @@ -88,6 +88,55 @@ xstrdup(const char *s) return xmemdup(s, strlen(s) + 1); }
+char * +xstrndup(const char *s, size_t maxsize) +{ + char *copy; + size_t size; + /* strnlen is not in any standard :-( */ + for (size = 0; size < maxsize; size++) + if (s[size] == '\0') + break; + + copy = xmalloc(size + 1); + memcpy(copy, s, size); + copy[size] = '\0'; + return copy; +} + +/******************************** Mathematics ********************************/ + +unsigned int +ui64_log2(uint64_t u64) +{ + unsigned int r = 0; + if (u64 >= (((uint64_t)1)<<32)) { + u64 >>= 32; + r = 32; + } + if (u64 >= (((uint64_t)1)<<16)) { + u64 >>= 16; + r += 16; + } + if (u64 >= (((uint64_t)1)<<8)) { + u64 >>= 8; + r += 8; + } + if (u64 >= (((uint64_t)1)<<4)) { + u64 >>= 4; + r += 4; + } + if (u64 >= (((uint64_t)1)<<2)) { + u64 >>= 2; + r += 2; + } + if (u64 >= (((uint64_t)1)<<1)) { + u64 >>= 1; + r += 1; + } + return r; +} + /************************ Obfsproxy Network Routines *************************/
/** @@ -206,6 +255,31 @@ obfs_vsnprintf(char *str, size_t size, const char *format, va_list args) return -1; return r; } +/** Remove from the string <b>s</b> every character which appears in + * <b>strip</b>. */ +void +ascii_strstrip(char *s, const char *strip) +{ + char *read = s; + while (*read) { + if (strchr(strip, *read)) { + ++read; + } else { + *s++ = *read++; + } + } + *s = '\0'; +} + +void +ascii_strlower(char *s) +{ + while (*s) { + if (*s >= 'A' && *s <= 'Z') + *s = *s - 'A' + 'a'; + ++s; + } +}
/************************ Doubly Linked List (DLL) ******************/
diff --git a/src/util.h b/src/util.h index d365cbc..e44a711 100644 --- a/src/util.h +++ b/src/util.h @@ -8,6 +8,7 @@ #include "config.h" #include <stdarg.h> /* for va_list */ #include <stddef.h> /* size_t, offsetof, NULL, etc */ +#include <stdint.h> /* intN_t, uintN_t */
#ifndef __GNUC__ #define __attribute__(x) /* nothing */ @@ -34,6 +35,11 @@ void *xzalloc(size_t size) ATTR_MALLOC; /* clears memory */ void *xrealloc(void *ptr, size_t size); void *xmemdup(const void *ptr, size_t size) ATTR_MALLOC; char *xstrdup(const char *s) ATTR_MALLOC; +char *xstrndup(const char *s, size_t maxsize) ATTR_MALLOC; + +/***** Math. *****/ + +unsigned int ui64_log2(uint64_t u64);
/***** Network functions stuff. *****/
@@ -48,6 +54,18 @@ int init_evdns_base(struct event_base *base);
/***** String functions stuff. *****/
+static inline int ascii_isspace(unsigned char c) +{ + return (c == ' ' || + c == '\t' || + c == '\r' || + c == '\n' || + c == '\v' || + c == '\f'); +} +void ascii_strstrip(char *s, const char *kill); +void ascii_strlower(char *s); + int obfs_vsnprintf(char *str, size_t size, const char *format, va_list args); int obfs_snprintf(char *str, size_t size, @@ -133,5 +151,4 @@ void log_debug(const char *format, ...) log_error("aborted at %s:%d", __FILE__, __LINE__); \ } while (0)
- #endif
tor-commits@lists.torproject.org