Extract core part of smartlist code into its own library.

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

The "containers" library still uses the logging code, and has the
higher-level smartlist functions.
This commit is contained in:
Nick Mathewson 2018-06-26 12:13:23 -04:00
parent 860b9a9918
commit b1de1e7a77
16 changed files with 610 additions and 500 deletions

2
.gitignore vendored
View File

@ -185,6 +185,8 @@ uptime-*.json
/src/lib/libtor-malloc-testing.a
/src/lib/libtor-string.a
/src/lib/libtor-string-testing.a
/src/lib/libtor-smartlist-core.a
/src/lib/libtor-smartlist-core-testing.a
/src/lib/libtor-tls.a
/src/lib/libtor-tls-testing.a
/src/lib/libtor-trace.a

View File

@ -40,11 +40,12 @@ endif
# "Common" libraries used to link tor's utility code.
TOR_UTIL_LIBS = \
src/common/libor.a \
src/lib/libtor-container.a \
src/lib/libtor-log.a \
src/lib/libtor-lock.a \
src/lib/libtor-fdio.a \
src/lib/libtor-container.a \
src/lib/libtor-string.a \
src/lib/libtor-smartlist-core.a \
src/lib/libtor-malloc.a \
src/lib/libtor-wallclock.a \
src/lib/libtor-err.a \
@ -55,11 +56,12 @@ TOR_UTIL_LIBS = \
# and tests)
TOR_UTIL_TESTING_LIBS = \
src/common/libor-testing.a \
src/lib/libtor-container-testing.a \
src/lib/libtor-log-testing.a \
src/lib/libtor-lock-testing.a \
src/lib/libtor-fdio-testing.a \
src/lib/libtor-container-testing.a \
src/lib/libtor-string-testing.a \
src/lib/libtor-smartlist-core-testing.a \
src/lib/libtor-malloc-testing.a \
src/lib/libtor-wallclock-testing.a \
src/lib/libtor-err-testing.a \

View File

@ -13,6 +13,7 @@ include src/lib/lock/include.am
include src/lib/log/include.am
include src/lib/malloc/include.am
include src/lib/string/include.am
include src/lib/smartlist_core/include.am
include src/lib/testsupport/include.am
include src/lib/tls/include.am
include src/lib/trace/include.am

View File

@ -5,6 +5,7 @@ lib/ctime/*.h
lib/defs/*.h
lib/malloc/*.h
lib/err/*.h
lib/smartlist_core/*.h
lib/string/*.h
lib/testsupport/testsupport.h
lib/intmath/*.h

View File

@ -9,7 +9,6 @@
* with helper functions to use smartlists.
**/
#include "lib/malloc/util_malloc.h"
#include "lib/container/smartlist.h"
#include "lib/err/torerr.h"
#include "lib/malloc/util_malloc.h"
@ -24,108 +23,6 @@
#include <stdlib.h>
#include <string.h>
/** All newly allocated smartlists have this capacity. */
#define SMARTLIST_DEFAULT_CAPACITY 16
/** Allocate and return an empty smartlist.
*/
MOCK_IMPL(smartlist_t *,
smartlist_new,(void))
{
smartlist_t *sl = tor_malloc(sizeof(smartlist_t));
sl->num_used = 0;
sl->capacity = SMARTLIST_DEFAULT_CAPACITY;
sl->list = tor_calloc(sizeof(void *), sl->capacity);
return sl;
}
/** Deallocate a smartlist. Does not release storage associated with the
* list's elements.
*/
MOCK_IMPL(void,
smartlist_free_,(smartlist_t *sl))
{
if (!sl)
return;
tor_free(sl->list);
tor_free(sl);
}
/** Remove all elements from the list.
*/
void
smartlist_clear(smartlist_t *sl)
{
memset(sl->list, 0, sizeof(void *) * sl->num_used);
sl->num_used = 0;
}
#if SIZE_MAX < INT_MAX
#error "We don't support systems where size_t is smaller than int."
#endif
/** Make sure that <b>sl</b> can hold at least <b>size</b> entries. */
static inline void
smartlist_ensure_capacity(smartlist_t *sl, size_t size)
{
/* Set MAX_CAPACITY to MIN(INT_MAX, SIZE_MAX / sizeof(void*)) */
#if (SIZE_MAX/SIZEOF_VOID_P) > INT_MAX
#define MAX_CAPACITY (INT_MAX)
#else
#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*))))
#endif
raw_assert(size <= MAX_CAPACITY);
if (size > (size_t) sl->capacity) {
size_t higher = (size_t) sl->capacity;
if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) {
higher = MAX_CAPACITY;
} else {
while (size > higher)
higher *= 2;
}
sl->list = tor_reallocarray(sl->list, sizeof(void *),
((size_t)higher));
memset(sl->list + sl->capacity, 0,
sizeof(void *) * (higher - sl->capacity));
sl->capacity = (int) higher;
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
}
/** Append element to the end of the list. */
void
smartlist_add(smartlist_t *sl, void *element)
{
smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1);
sl->list[sl->num_used++] = element;
}
/** Append each element from S2 to the end of S1. */
void
smartlist_add_all(smartlist_t *s1, const smartlist_t *s2)
{
size_t new_size = (size_t)s1->num_used + (size_t)s2->num_used;
tor_assert(new_size >= (size_t) s1->num_used); /* check for overflow. */
smartlist_ensure_capacity(s1, new_size);
memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*));
tor_assert(new_size <= INT_MAX); /* redundant. */
s1->num_used = (int) new_size;
}
/** Append a copy of string to sl */
void
smartlist_add_strdup(struct smartlist_t *sl, const char *string)
{
char *copy;
copy = tor_strdup(string);
smartlist_add(sl, copy);
}
/** Append the string produced by tor_asprintf(<b>pattern</b>, <b>...</b>)
* to <b>sl</b>. */
void
@ -150,56 +47,6 @@ smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern,
smartlist_add(sl, str);
}
/** Remove all elements E from sl such that E==element. Preserve
* the order of any elements before E, but elements after E can be
* rearranged.
*/
void
smartlist_remove(smartlist_t *sl, const void *element)
{
int i;
if (element == NULL)
return;
for (i=0; i < sl->num_used; i++)
if (sl->list[i] == element) {
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
sl->list[sl->num_used] = NULL;
}
}
/** As <b>smartlist_remove</b>, but do not change the order of
* any elements not removed */
void
smartlist_remove_keeporder(smartlist_t *sl, const void *element)
{
int i, j, num_used_orig = sl->num_used;
if (element == NULL)
return;
for (i=j=0; j < num_used_orig; ++j) {
if (sl->list[j] == element) {
--sl->num_used;
} else {
sl->list[i++] = sl->list[j];
}
}
}
/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise,
* return NULL. */
void *
smartlist_pop_last(smartlist_t *sl)
{
tor_assert(sl);
if (sl->num_used) {
void *tmp = sl->list[--sl->num_used];
sl->list[sl->num_used] = NULL;
return tmp;
} else
return NULL;
}
/** Reverse the order of the items in <b>sl</b>. */
void
smartlist_reverse(smartlist_t *sl)
@ -232,18 +79,6 @@ smartlist_string_remove(smartlist_t *sl, const char *element)
}
}
/** Return true iff some element E of sl has E==element.
*/
int
smartlist_contains(const smartlist_t *sl, const void *element)
{
int i;
for (i=0; i < sl->num_used; i++)
if (sl->list[i] == element)
return 1;
return 0;
}
/** Return true iff <b>sl</b> has some element E such that
* !strcmp(E,<b>element</b>)
*/
@ -399,131 +234,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2)
smartlist_remove(sl1, sl2->list[i]);
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last
* element, swap the last element of sl into the <b>idx</b>th space.
*/
void
smartlist_del(smartlist_t *sl, int idx)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
sl->list[idx] = sl->list[--sl->num_used];
sl->list[sl->num_used] = NULL;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last element,
* moving all subsequent elements back one space. Return the old value
* of the <b>idx</b>th element.
*/
void
smartlist_del_keeporder(smartlist_t *sl, int idx)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
--sl->num_used;
if (idx < sl->num_used)
memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx));
sl->list[sl->num_used] = NULL;
}
/** Insert the value <b>val</b> as the new <b>idx</b>th element of
* <b>sl</b>, moving all items previously at <b>idx</b> or later
* forward one space.
*/
void
smartlist_insert(smartlist_t *sl, int idx, void *val)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx <= sl->num_used);
if (idx == sl->num_used) {
smartlist_add(sl, val);
} else {
smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1);
/* Move other elements away */
if (idx < sl->num_used)
memmove(sl->list + idx + 1, sl->list + idx,
sizeof(void*)*(sl->num_used-idx));
sl->num_used++;
sl->list[idx] = val;
}
}
/**
* Split a string <b>str</b> along all occurrences of <b>sep</b>,
* appending the (newly allocated) split strings, in order, to
* <b>sl</b>. Return the number of strings added to <b>sl</b>.
*
* If <b>flags</b>&amp;SPLIT_SKIP_SPACE is true, remove initial and
* trailing space from each entry.
* If <b>flags</b>&amp;SPLIT_IGNORE_BLANK is true, remove any entries
* of length 0.
* If <b>flags</b>&amp;SPLIT_STRIP_SPACE is true, strip spaces from each
* split string.
*
* If <b>max</b>\>0, divide the string into no more than <b>max</b> pieces. If
* <b>sep</b> is NULL, split on any sequence of horizontal space.
*/
int
smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max)
{
const char *cp, *end, *next;
int n = 0;
tor_assert(sl);
tor_assert(str);
cp = str;
while (1) {
if (flags&SPLIT_SKIP_SPACE) {
while (TOR_ISSPACE(*cp)) ++cp;
}
if (max>0 && n == max-1) {
end = strchr(cp,'\0');
} else if (sep) {
end = strstr(cp,sep);
if (!end)
end = strchr(cp,'\0');
} else {
for (end = cp; *end && *end != '\t' && *end != ' '; ++end)
;
}
tor_assert(end);
if (!*end) {
next = NULL;
} else if (sep) {
next = end+strlen(sep);
} else {
next = end+1;
while (*next == '\t' || *next == ' ')
++next;
}
if (flags&SPLIT_SKIP_SPACE) {
while (end > cp && TOR_ISSPACE(*(end-1)))
--end;
}
if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) {
char *string = tor_strndup(cp, end-cp);
if (flags&SPLIT_STRIP_SPACE)
tor_strstrip(string, " ");
smartlist_add(sl, string);
++n;
}
if (!next)
break;
cp = next;
}
return n;
}
/** Allocate and return a new string containing the concatenation of
* the elements of <b>sl</b>, in order, separated by <b>join</b>. If
* <b>terminate</b> is true, also terminate the string with <b>join</b>.

View File

@ -6,50 +6,19 @@
#ifndef TOR_SMARTLIST_H
#define TOR_SMARTLIST_H
#include <stddef.h>
#include <stdarg.h>
#include "lib/cc/compat_compiler.h"
#include "lib/cc/torint.h"
#include "lib/testsupport/testsupport.h"
#include "lib/smartlist_core/smartlist_core.h"
#include "lib/smartlist_core/smartlist_foreach.h"
#include "lib/smartlist_core/smartlist_split.h"
/** A resizeable list of pointers, with associated helpful functionality.
*
* The members of this struct are exposed only so that macros and inlines can
* use them; all access to smartlist internals should go through the functions
* and macros defined here.
**/
typedef struct smartlist_t {
/** @{ */
/** <b>list</b> has enough capacity to store exactly <b>capacity</b> elements
* before it needs to be resized. Only the first <b>num_used</b> (\<=
* capacity) elements point to valid data.
*/
void **list;
int num_used;
int capacity;
/** @} */
} smartlist_t;
MOCK_DECL(smartlist_t *, smartlist_new, (void));
MOCK_DECL(void, smartlist_free_, (smartlist_t *sl));
#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl))
void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
void smartlist_add_strdup(struct smartlist_t *sl, const char *string);
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...)
CHECK_PRINTF(2, 3);
void smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern,
va_list args)
CHECK_PRINTF(2, 0);
void smartlist_remove(smartlist_t *sl, const void *element);
void smartlist_remove_keeporder(smartlist_t *sl, const void *element);
void *smartlist_pop_last(smartlist_t *sl);
void smartlist_reverse(smartlist_t *sl);
void smartlist_string_remove(smartlist_t *sl, const char *element);
int smartlist_contains(const smartlist_t *sl, const void *element);
int smartlist_contains_string(const smartlist_t *sl, const char *element);
int smartlist_pos(const smartlist_t *sl, const void *element);
int smartlist_string_pos(const smartlist_t *, const char *elt);
@ -62,52 +31,6 @@ int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
/* smartlist_choose() is defined in crypto.[ch] */
#ifdef DEBUG_SMARTLIST
#include "lib/err/torerr.h"
#include <stdlib.h>
/** Return the number of items in sl.
*/
static inline int smartlist_len(const smartlist_t *sl);
static inline int smartlist_len(const smartlist_t *sl) {
raw_assert(sl);
return (sl)->num_used;
}
/** Return the <b>idx</b>th element of sl.
*/
static inline void *smartlist_get(const smartlist_t *sl, int idx);
static inline void *smartlist_get(const smartlist_t *sl, int idx) {
raw_assert(sl);
raw_assert(idx>=0);
raw_assert(sl->num_used > idx);
return sl->list[idx];
}
static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
raw_assert(sl);
raw_assert(idx>=0);
raw_assert(sl->num_used > idx);
sl->list[idx] = val;
}
#else /* !(defined(DEBUG_SMARTLIST)) */
#define smartlist_len(sl) ((sl)->num_used)
#define smartlist_get(sl, idx) ((sl)->list[idx])
#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val))
#endif /* defined(DEBUG_SMARTLIST) */
/** Exchange the elements at indices <b>idx1</b> and <b>idx2</b> of the
* smartlist <b>sl</b>. */
static inline void smartlist_swap(smartlist_t *sl, int idx1, int idx2)
{
if (idx1 != idx2) {
void *elt = smartlist_get(sl, idx1);
smartlist_set(sl, idx1, smartlist_get(sl, idx2));
smartlist_set(sl, idx2, elt);
}
}
void smartlist_del(smartlist_t *sl, int idx);
void smartlist_del_keeporder(smartlist_t *sl, int idx);
void smartlist_insert(smartlist_t *sl, int idx, void *val);
void smartlist_sort(smartlist_t *sl,
int (*compare)(const void **a, const void **b));
void *smartlist_get_most_frequent_(const smartlist_t *sl,
@ -153,136 +76,12 @@ void smartlist_pqueue_assert_ok(smartlist_t *sl,
int (*compare)(const void *a, const void *b),
int idx_field_offset);
#define SPLIT_SKIP_SPACE 0x01
#define SPLIT_IGNORE_BLANK 0x02
#define SPLIT_STRIP_SPACE 0x04
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max);
char *smartlist_join_strings(smartlist_t *sl, const char *join, int terminate,
size_t *len_out) ATTR_MALLOC;
char *smartlist_join_strings2(smartlist_t *sl, const char *join,
size_t join_len, int terminate, size_t *len_out)
ATTR_MALLOC;
/** Iterate over the items in a smartlist <b>sl</b>, in order. For each item,
* assign it to a new local variable of type <b>type</b> named <b>var</b>, and
* execute the statements inside the loop body. Inside the loop, the loop
* index can be accessed as <b>var</b>_sl_idx and the length of the list can
* be accessed as <b>var</b>_sl_len.
*
* NOTE: Do not change the length of the list while the loop is in progress,
* unless you adjust the _sl_len variable correspondingly. See second example
* below.
*
* Example use:
* <pre>
* smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
* SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
* printf("%d: %s\n", cp_sl_idx, cp);
* tor_free(cp);
* } SMARTLIST_FOREACH_END(cp);
* smartlist_free(list);
* </pre>
*
* Example use (advanced):
* <pre>
* SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
* if (!strcmp(cp, "junk")) {
* tor_free(cp);
* SMARTLIST_DEL_CURRENT(list, cp);
* }
* } SMARTLIST_FOREACH_END(cp);
* </pre>
*/
/* Note: these macros use token pasting, and reach into smartlist internals.
* This can make them a little daunting. Here's the approximate unpacking of
* the above examples, for entertainment value:
*
* <pre>
* smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
* {
* int cp_sl_idx, cp_sl_len = smartlist_len(list);
* char *cp;
* for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
* cp = smartlist_get(list, cp_sl_idx);
* printf("%d: %s\n", cp_sl_idx, cp);
* tor_free(cp);
* }
* }
* smartlist_free(list);
* </pre>
*
* <pre>
* {
* int cp_sl_idx, cp_sl_len = smartlist_len(list);
* char *cp;
* for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
* cp = smartlist_get(list, cp_sl_idx);
* if (!strcmp(cp, "junk")) {
* tor_free(cp);
* smartlist_del(list, cp_sl_idx);
* --cp_sl_idx;
* --cp_sl_len;
* }
* }
* }
* </pre>
*/
#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \
STMT_BEGIN \
int var ## _sl_idx, var ## _sl_len=(sl)->num_used; \
type var; \
for (var ## _sl_idx = 0; var ## _sl_idx < var ## _sl_len; \
++var ## _sl_idx) { \
var = (sl)->list[var ## _sl_idx];
#define SMARTLIST_FOREACH_END(var) \
var = NULL; \
(void) var ## _sl_idx; \
} STMT_END
/**
* An alias for SMARTLIST_FOREACH_BEGIN and SMARTLIST_FOREACH_END, using
* <b>cmd</b> as the loop body. This wrapper is here for convenience with
* very short loops.
*
* By convention, we do not use this for loops which nest, or for loops over
* 10 lines or so. Use SMARTLIST_FOREACH_{BEGIN,END} for those.
*/
#define SMARTLIST_FOREACH(sl, type, var, cmd) \
SMARTLIST_FOREACH_BEGIN(sl,type,var) { \
cmd; \
} SMARTLIST_FOREACH_END(var)
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, remove the current element in a way that
* won't confuse the loop. */
#define SMARTLIST_DEL_CURRENT(sl, var) \
STMT_BEGIN \
smartlist_del(sl, var ## _sl_idx); \
--var ## _sl_idx; \
--var ## _sl_len; \
STMT_END
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, remove the current element in a way that
* won't confuse the loop. */
#define SMARTLIST_DEL_CURRENT_KEEPORDER(sl, var) \
STMT_BEGIN \
smartlist_del_keeporder(sl, var ## _sl_idx); \
--var ## _sl_idx; \
--var ## _sl_len; \
STMT_END
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, replace the current element with <b>val</b>.
* Does not deallocate the current value of <b>var</b>.
*/
#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \
STMT_BEGIN \
smartlist_set(sl, var ## _sl_idx, val); \
STMT_END
/* Helper: Given two lists of items, possibly of different types, such that
* both lists are sorted on some common field (as determined by a comparison
* expression <b>cmpexpr</b>), and such that one list (<b>sl1</b>) has no

View File

@ -1,7 +1,7 @@
orconfig.h
lib/cc/*.h
lib/container/smartlist.h
lib/smartlist_core/*.h
lib/err/*.h
lib/fdio/*.h
lib/intmath/*.h

View File

@ -5,7 +5,7 @@
/* See LICENSE for licensing information */
/**
* \file log.c
* \file torlog.c
* \brief Functions to send messages to log files or the console.
**/
@ -34,7 +34,9 @@
#include "lib/log/torlog.h"
#include "lib/log/ratelim.h"
#include "lib/lock/compat_mutex.h"
#include "lib/container/smartlist.h"
#include "lib/smartlist_core/smartlist_core.h"
#include "lib/smartlist_core/smartlist_foreach.h"
#include "lib/smartlist_core/smartlist_split.h"
#include "lib/err/torerr.h"
#include "lib/intmath/bits.h"
#include "lib/string/compat_string.h"

View File

@ -12,7 +12,8 @@
#include "lib/log/torlog.h"
#include "lib/err/backtrace.h"
#ifdef TOR_UNIT_TESTS
#include "lib/container/smartlist.h"
#include "lib/smartlist_core/smartlist_core.h"
#include "lib/smartlist_core/smartlist_foreach.h"
#endif
#include "lib/malloc/util_malloc.h"
#include "lib/string/printf.h"

View File

@ -0,0 +1,7 @@
orconfig.h
lib/cc/*.h
lib/malloc/*.h
lib/err/*.h
lib/string/*.h
lib/smartlist_core/*.h
lib/testsupport/testsupport.h

View File

@ -0,0 +1,21 @@
noinst_LIBRARIES += src/lib/libtor-smartlist-core.a
if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-smartlist-core-testing.a
endif
src_lib_libtor_smartlist_core_a_SOURCES = \
src/lib/smartlist_core/smartlist_core.c \
src/lib/smartlist_core/smartlist_split.c
src_lib_libtor_smartlist_core_testing_a_SOURCES = \
$(src_lib_libtor_smartlist_core_a_SOURCES)
src_lib_libtor_smartlist_core_testing_a_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_smartlist_core_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS += \
src/lib/smartlist_core/smartlist_core.h \
src/lib/smartlist_core/smartlist_foreach.h \
src/lib/smartlist_core/smartlist_split.h

View File

@ -0,0 +1,237 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file smartlist_core.c
* \brief Implements the core functionality of a smartlist (a resizeable
* dynamic array). For more functionality and helper functions, see the
* container library.
**/
#include "lib/err/torerr.h"
#include "lib/malloc/util_malloc.h"
#include "lib/smartlist_core/smartlist_core.h"
#define tor_assert raw_assert
#include <stdlib.h>
#include <string.h>
/** All newly allocated smartlists have this capacity. */
#define SMARTLIST_DEFAULT_CAPACITY 16
/** Allocate and return an empty smartlist.
*/
MOCK_IMPL(smartlist_t *,
smartlist_new,(void))
{
smartlist_t *sl = tor_malloc(sizeof(smartlist_t));
sl->num_used = 0;
sl->capacity = SMARTLIST_DEFAULT_CAPACITY;
sl->list = tor_calloc(sizeof(void *), sl->capacity);
return sl;
}
/** Deallocate a smartlist. Does not release storage associated with the
* list's elements.
*/
MOCK_IMPL(void,
smartlist_free_,(smartlist_t *sl))
{
if (!sl)
return;
tor_free(sl->list);
tor_free(sl);
}
/** Remove all elements from the list.
*/
void
smartlist_clear(smartlist_t *sl)
{
memset(sl->list, 0, sizeof(void *) * sl->num_used);
sl->num_used = 0;
}
#if SIZE_MAX < INT_MAX
#error "We don't support systems where size_t is smaller than int."
#endif
/** Make sure that <b>sl</b> can hold at least <b>size</b> entries. */
static inline void
smartlist_ensure_capacity(smartlist_t *sl, size_t size)
{
/* Set MAX_CAPACITY to MIN(INT_MAX, SIZE_MAX / sizeof(void*)) */
#if (SIZE_MAX/SIZEOF_VOID_P) > INT_MAX
#define MAX_CAPACITY (INT_MAX)
#else
#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*))))
#endif
raw_assert(size <= MAX_CAPACITY);
if (size > (size_t) sl->capacity) {
size_t higher = (size_t) sl->capacity;
if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) {
higher = MAX_CAPACITY;
} else {
while (size > higher)
higher *= 2;
}
sl->list = tor_reallocarray(sl->list, sizeof(void *),
((size_t)higher));
memset(sl->list + sl->capacity, 0,
sizeof(void *) * (higher - sl->capacity));
sl->capacity = (int) higher;
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
}
/** Append element to the end of the list. */
void
smartlist_add(smartlist_t *sl, void *element)
{
smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1);
sl->list[sl->num_used++] = element;
}
/** Append each element from S2 to the end of S1. */
void
smartlist_add_all(smartlist_t *s1, const smartlist_t *s2)
{
size_t new_size = (size_t)s1->num_used + (size_t)s2->num_used;
tor_assert(new_size >= (size_t) s1->num_used); /* check for overflow. */
smartlist_ensure_capacity(s1, new_size);
memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*));
tor_assert(new_size <= INT_MAX); /* redundant. */
s1->num_used = (int) new_size;
}
/** Append a copy of string to sl */
void
smartlist_add_strdup(struct smartlist_t *sl, const char *string)
{
char *copy;
copy = tor_strdup(string);
smartlist_add(sl, copy);
}
/** Remove all elements E from sl such that E==element. Preserve
* the order of any elements before E, but elements after E can be
* rearranged.
*/
void
smartlist_remove(smartlist_t *sl, const void *element)
{
int i;
if (element == NULL)
return;
for (i=0; i < sl->num_used; i++)
if (sl->list[i] == element) {
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
sl->list[sl->num_used] = NULL;
}
}
/** As <b>smartlist_remove</b>, but do not change the order of
* any elements not removed */
void
smartlist_remove_keeporder(smartlist_t *sl, const void *element)
{
int i, j, num_used_orig = sl->num_used;
if (element == NULL)
return;
for (i=j=0; j < num_used_orig; ++j) {
if (sl->list[j] == element) {
--sl->num_used;
} else {
sl->list[i++] = sl->list[j];
}
}
}
/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise,
* return NULL. */
void *
smartlist_pop_last(smartlist_t *sl)
{
tor_assert(sl);
if (sl->num_used) {
void *tmp = sl->list[--sl->num_used];
sl->list[sl->num_used] = NULL;
return tmp;
} else
return NULL;
}
/** Return true iff some element E of sl has E==element.
*/
int
smartlist_contains(const smartlist_t *sl, const void *element)
{
int i;
for (i=0; i < sl->num_used; i++)
if (sl->list[i] == element)
return 1;
return 0;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last
* element, swap the last element of sl into the <b>idx</b>th space.
*/
void
smartlist_del(smartlist_t *sl, int idx)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
sl->list[idx] = sl->list[--sl->num_used];
sl->list[sl->num_used] = NULL;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last element,
* moving all subsequent elements back one space. Return the old value
* of the <b>idx</b>th element.
*/
void
smartlist_del_keeporder(smartlist_t *sl, int idx)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
--sl->num_used;
if (idx < sl->num_used)
memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx));
sl->list[sl->num_used] = NULL;
}
/** Insert the value <b>val</b> as the new <b>idx</b>th element of
* <b>sl</b>, moving all items previously at <b>idx</b> or later
* forward one space.
*/
void
smartlist_insert(smartlist_t *sl, int idx, void *val)
{
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(idx <= sl->num_used);
if (idx == sl->num_used) {
smartlist_add(sl, val);
} else {
smartlist_ensure_capacity(sl, ((size_t) sl->num_used)+1);
/* Move other elements away */
if (idx < sl->num_used)
memmove(sl->list + idx + 1, sl->list + idx,
sizeof(void*)*(sl->num_used-idx));
sl->num_used++;
sl->list[idx] = val;
}
}

View File

@ -0,0 +1,95 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_SMARTLIST_CORE_H
#define TOR_SMARTLIST_CORE_H
#include <stddef.h>
#include "lib/cc/compat_compiler.h"
#include "lib/cc/torint.h"
#include "lib/testsupport/testsupport.h"
/** A resizeable list of pointers, with associated helpful functionality.
*
* The members of this struct are exposed only so that macros and inlines can
* use them; all access to smartlist internals should go through the functions
* and macros defined here.
**/
typedef struct smartlist_t {
/** @{ */
/** <b>list</b> has enough capacity to store exactly <b>capacity</b> elements
* before it needs to be resized. Only the first <b>num_used</b> (\<=
* capacity) elements point to valid data.
*/
void **list;
int num_used;
int capacity;
/** @} */
} smartlist_t;
MOCK_DECL(smartlist_t *, smartlist_new, (void));
MOCK_DECL(void, smartlist_free_, (smartlist_t *sl));
#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl))
void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
void smartlist_add_strdup(struct smartlist_t *sl, const char *string);
void smartlist_remove(smartlist_t *sl, const void *element);
void smartlist_remove_keeporder(smartlist_t *sl, const void *element);
void *smartlist_pop_last(smartlist_t *sl);
int smartlist_contains(const smartlist_t *sl, const void *element);
/* smartlist_choose() is defined in crypto.[ch] */
#ifdef DEBUG_SMARTLIST
#include "lib/err/torerr.h"
#include <stdlib.h>
/** Return the number of items in sl.
*/
static inline int smartlist_len(const smartlist_t *sl);
static inline int smartlist_len(const smartlist_t *sl) {
raw_assert(sl);
return (sl)->num_used;
}
/** Return the <b>idx</b>th element of sl.
*/
static inline void *smartlist_get(const smartlist_t *sl, int idx);
static inline void *smartlist_get(const smartlist_t *sl, int idx) {
raw_assert(sl);
raw_assert(idx>=0);
raw_assert(sl->num_used > idx);
return sl->list[idx];
}
static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
raw_assert(sl);
raw_assert(idx>=0);
raw_assert(sl->num_used > idx);
sl->list[idx] = val;
}
#else /* !(defined(DEBUG_SMARTLIST)) */
#define smartlist_len(sl) ((sl)->num_used)
#define smartlist_get(sl, idx) ((sl)->list[idx])
#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val))
#endif /* defined(DEBUG_SMARTLIST) */
/** Exchange the elements at indices <b>idx1</b> and <b>idx2</b> of the
* smartlist <b>sl</b>. */
static inline void smartlist_swap(smartlist_t *sl, int idx1, int idx2)
{
if (idx1 != idx2) {
void *elt = smartlist_get(sl, idx1);
smartlist_set(sl, idx1, smartlist_get(sl, idx2));
smartlist_set(sl, idx2, elt);
}
}
void smartlist_del(smartlist_t *sl, int idx);
void smartlist_del_keeporder(smartlist_t *sl, int idx);
void smartlist_insert(smartlist_t *sl, int idx, void *val);
#endif /* !defined(TOR_CONTAINER_H) */

View File

@ -0,0 +1,128 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_SMARTLIST_FOREACH_H
#define TOR_SMARTLIST_FOREACH_H
/** Iterate over the items in a smartlist <b>sl</b>, in order. For each item,
* assign it to a new local variable of type <b>type</b> named <b>var</b>, and
* execute the statements inside the loop body. Inside the loop, the loop
* index can be accessed as <b>var</b>_sl_idx and the length of the list can
* be accessed as <b>var</b>_sl_len.
*
* NOTE: Do not change the length of the list while the loop is in progress,
* unless you adjust the _sl_len variable correspondingly. See second example
* below.
*
* Example use:
* <pre>
* smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
* SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
* printf("%d: %s\n", cp_sl_idx, cp);
* tor_free(cp);
* } SMARTLIST_FOREACH_END(cp);
* smartlist_free(list);
* </pre>
*
* Example use (advanced):
* <pre>
* SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
* if (!strcmp(cp, "junk")) {
* tor_free(cp);
* SMARTLIST_DEL_CURRENT(list, cp);
* }
* } SMARTLIST_FOREACH_END(cp);
* </pre>
*/
/* Note: these macros use token pasting, and reach into smartlist internals.
* This can make them a little daunting. Here's the approximate unpacking of
* the above examples, for entertainment value:
*
* <pre>
* smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
* {
* int cp_sl_idx, cp_sl_len = smartlist_len(list);
* char *cp;
* for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
* cp = smartlist_get(list, cp_sl_idx);
* printf("%d: %s\n", cp_sl_idx, cp);
* tor_free(cp);
* }
* }
* smartlist_free(list);
* </pre>
*
* <pre>
* {
* int cp_sl_idx, cp_sl_len = smartlist_len(list);
* char *cp;
* for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
* cp = smartlist_get(list, cp_sl_idx);
* if (!strcmp(cp, "junk")) {
* tor_free(cp);
* smartlist_del(list, cp_sl_idx);
* --cp_sl_idx;
* --cp_sl_len;
* }
* }
* }
* </pre>
*/
#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \
STMT_BEGIN \
int var ## _sl_idx, var ## _sl_len=(sl)->num_used; \
type var; \
for (var ## _sl_idx = 0; var ## _sl_idx < var ## _sl_len; \
++var ## _sl_idx) { \
var = (sl)->list[var ## _sl_idx];
#define SMARTLIST_FOREACH_END(var) \
var = NULL; \
(void) var ## _sl_idx; \
} STMT_END
/**
* An alias for SMARTLIST_FOREACH_BEGIN and SMARTLIST_FOREACH_END, using
* <b>cmd</b> as the loop body. This wrapper is here for convenience with
* very short loops.
*
* By convention, we do not use this for loops which nest, or for loops over
* 10 lines or so. Use SMARTLIST_FOREACH_{BEGIN,END} for those.
*/
#define SMARTLIST_FOREACH(sl, type, var, cmd) \
SMARTLIST_FOREACH_BEGIN(sl,type,var) { \
cmd; \
} SMARTLIST_FOREACH_END(var)
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, remove the current element in a way that
* won't confuse the loop. */
#define SMARTLIST_DEL_CURRENT(sl, var) \
STMT_BEGIN \
smartlist_del(sl, var ## _sl_idx); \
--var ## _sl_idx; \
--var ## _sl_len; \
STMT_END
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, remove the current element in a way that
* won't confuse the loop. */
#define SMARTLIST_DEL_CURRENT_KEEPORDER(sl, var) \
STMT_BEGIN \
smartlist_del_keeporder(sl, var ## _sl_idx); \
--var ## _sl_idx; \
--var ## _sl_len; \
STMT_END
/** Helper: While in a SMARTLIST_FOREACH loop over the list <b>sl</b> indexed
* with the variable <b>var</b>, replace the current element with <b>val</b>.
* Does not deallocate the current value of <b>var</b>.
*/
#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \
STMT_BEGIN \
smartlist_set(sl, var ## _sl_idx, val); \
STMT_END
#endif /* !defined(TOR_SMARTLIST_FOREACH_H) */

View File

@ -0,0 +1,89 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "lib/smartlist_core/smartlist_core.h"
#include "lib/smartlist_core/smartlist_split.h"
#include "lib/err/torerr.h"
#include "lib/string/util_string.h"
#include "lib/string/compat_ctype.h"
#include "lib/malloc/util_malloc.h"
#include <string.h>
#define tor_assert raw_assert
/**
* Split a string <b>str</b> along all occurrences of <b>sep</b>,
* appending the (newly allocated) split strings, in order, to
* <b>sl</b>. Return the number of strings added to <b>sl</b>.
*
* If <b>flags</b>&amp;SPLIT_SKIP_SPACE is true, remove initial and
* trailing space from each entry.
* If <b>flags</b>&amp;SPLIT_IGNORE_BLANK is true, remove any entries
* of length 0.
* If <b>flags</b>&amp;SPLIT_STRIP_SPACE is true, strip spaces from each
* split string.
*
* If <b>max</b>\>0, divide the string into no more than <b>max</b> pieces. If
* <b>sep</b> is NULL, split on any sequence of horizontal space.
*/
int
smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max)
{
const char *cp, *end, *next;
int n = 0;
tor_assert(sl);
tor_assert(str);
cp = str;
while (1) {
if (flags&SPLIT_SKIP_SPACE) {
while (TOR_ISSPACE(*cp)) ++cp;
}
if (max>0 && n == max-1) {
end = strchr(cp,'\0');
} else if (sep) {
end = strstr(cp,sep);
if (!end)
end = strchr(cp,'\0');
} else {
for (end = cp; *end && *end != '\t' && *end != ' '; ++end)
;
}
tor_assert(end);
if (!*end) {
next = NULL;
} else if (sep) {
next = end+strlen(sep);
} else {
next = end+1;
while (*next == '\t' || *next == ' ')
++next;
}
if (flags&SPLIT_SKIP_SPACE) {
while (end > cp && TOR_ISSPACE(*(end-1)))
--end;
}
if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) {
char *string = tor_strndup(cp, end-cp);
if (flags&SPLIT_STRIP_SPACE)
tor_strstrip(string, " ");
smartlist_add(sl, string);
++n;
}
if (!next)
break;
cp = next;
}
return n;
}

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_SMARTLIST_SPLIT_H
#define TOR_SMARTLIST_SPLIT_H
#define SPLIT_SKIP_SPACE 0x01
#define SPLIT_IGNORE_BLANK 0x02
#define SPLIT_STRIP_SPACE 0x04
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max);
#endif