Skip to content
Snippets Groups Projects
Commit dfd7a7f5 authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

Add a type to map names to short identifiers

We'll be using this for four kinds of identifier in dispatch.c
parent 9a61d3f5
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,7 @@ endif
src_lib_libtor_container_a_SOURCES = \
src/lib/container/bloomfilt.c \
src/lib/container/map.c \
src/lib/container/namemap.c \
src/lib/container/order.c \
src/lib/container/smartlist.c
......@@ -21,5 +22,7 @@ noinst_HEADERS += \
src/lib/container/bloomfilt.h \
src/lib/container/handles.h \
src/lib/container/map.h \
src/lib/container/namemap.h \
src/lib/container/namemap_st.h \
src/lib/container/order.h \
src/lib/container/smartlist.h
/* 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 "orconfig.h"
#include "lib/container/smartlist.h"
#include "lib/container/namemap.h"
#include "lib/container/namemap_st.h"
#include "lib/log/util_bug.h"
#include "lib/malloc/malloc.h"
#include "lib/string/printf.h"
#include "ext/siphash.h"
#include <string.h>
/** Helper for namemap hashtable implementation: compare two entries. */
static inline int
mapped_name_eq(const mapped_name_t *a, const mapped_name_t *b)
{
return !strcmp(a->name, b->name);
}
/** Helper for namemap hashtable implementation: hash an entry. */
static inline unsigned
mapped_name_hash(const mapped_name_t *a)
{
return (unsigned) siphash24g(a->name, strlen(a->name));
}
HT_PROTOTYPE(namemap_ht, mapped_name_t, node, mapped_name_hash,
mapped_name_eq)
HT_GENERATE2(namemap_ht, mapped_name_t, node, mapped_name_hash,
mapped_name_eq, 0.6, tor_reallocarray_, tor_free_)
/** Set up an uninitialized <b>map</b>. */
void
namemap_init(namemap_t *map)
{
memset(map, 0, sizeof(*map));
HT_INIT(namemap_ht, &map->ht);
map->names = smartlist_new();
}
/** Return the name that <b>map</b> associates with a given <b>id</b>, or
* NULL if there is no such name. */
const char *
namemap_get_name(const namemap_t *map, unsigned id)
{
if (map->names && id < (unsigned)smartlist_len(map->names)) {
mapped_name_t *name = smartlist_get(map->names, (int)id);
return name->name;
} else {
return NULL;
}
}
/**
* Return the name that <b>map</b> associates with a given <b>id</b>, or a
* pointer to a statically allocated string describing the value of <b>id</b>
* if no such name exists.
**/
const char *
namemap_fmt_name(const namemap_t *map, unsigned id)
{
static char buf[32];
const char *name = namemap_get_name(map, id);
if (name)
return name;
tor_snprintf(buf, sizeof(buf), "{%u}", id);
return buf;
}
/**
* Helper: As namemap_get_id(), but requires that <b>name</b> is
* <b>namelen</b> charaters long, and that <b>namelen</b> is no more than
* MAX_NAMEMAP_NAME_LEN.
*/
static unsigned
namemap_get_id_unchecked(const namemap_t *map,
const char *name,
size_t namelen)
{
union {
mapped_name_t n;
char storage[MAX_NAMEMAP_NAME_LEN + sizeof(mapped_name_t) + 1];
} u;
memcpy(u.n.name, name, namelen);
u.n.name[namelen] = 0;
const mapped_name_t *found = HT_FIND(namemap_ht, &map->ht, &u.n);
if (found) {
tor_assert(map->names);
tor_assert(smartlist_get(map->names, found->intval) == found);
return found->intval;
}
return NAMEMAP_ERR;
}
/**
* Return the identifier currently associated by <b>map</b> with the name
* <b>name</b>, or NAMEMAP_ERR if no such identifier exists.
**/
unsigned
namemap_get_id(const namemap_t *map,
const char *name)
{
size_t namelen = strlen(name);
if (namelen > MAX_NAMEMAP_NAME_LEN) {
return NAMEMAP_ERR;
}
return namemap_get_id_unchecked(map, name, namelen);
}
/**
* Return the identifier associated by <b>map</b> with the name
* <b>name</b>, allocating a new identifier in <b>map</b> if none exists.
*
* Return NAMEMAP_ERR if <b>name</b> is too long, or if there are no more
* identifiers we can allocate.
**/
unsigned
namemap_get_or_create_id(namemap_t *map,
const char *name)
{
size_t namelen = strlen(name);
if (namelen > MAX_NAMEMAP_NAME_LEN) {
return NAMEMAP_ERR;
}
if (PREDICT_UNLIKELY(map->names == NULL))
map->names = smartlist_new();
unsigned found = namemap_get_id_unchecked(map, name, namelen);
if (found != NAMEMAP_ERR)
return found;
unsigned new_id = (unsigned)smartlist_len(map->names);
if (new_id == NAMEMAP_ERR)
return NAMEMAP_ERR; /* Can't allocate any more. */
mapped_name_t *insert = tor_malloc_zero(
offsetof(mapped_name_t, name) + namelen + 1);
memcpy(insert->name, name, namelen+1);
insert->intval = new_id;
HT_INSERT(namemap_ht, &map->ht, insert);
smartlist_add(map->names, insert);
return new_id;
}
/** Return the number of entries in 'names' */
size_t
namemap_get_size(const namemap_t *map)
{
if (PREDICT_UNLIKELY(map->names == NULL))
return 0;
return smartlist_len(map->names);
}
/**
* Release all storage held in <b>map</b>.
*/
void
namemap_clear(namemap_t *map)
{
if (!map)
return;
HT_CLEAR(namemap_ht, &map->ht);
if (map->names) {
SMARTLIST_FOREACH(map->names, mapped_name_t *, n,
tor_free(n));
smartlist_free(map->names);
}
memset(map, 0, sizeof(*map));
}
/* 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_NAMEMAP_H
#define TOR_NAMEMAP_H
/**
* \file namemap.h
*
* \brief Header for namemap.c
**/
#include "lib/cc/compat_compiler.h"
#include "ext/ht.h"
#include <stddef.h>
typedef struct namemap_t namemap_t;
/** Returned in place of an identifier when an error occurs. */
#define NAMEMAP_ERR UINT_MAX
void namemap_init(namemap_t *map);
const char *namemap_get_name(const namemap_t *map, unsigned id);
const char *namemap_fmt_name(const namemap_t *map, unsigned id);
unsigned namemap_get_id(const namemap_t *map,
const char *name);
unsigned namemap_get_or_create_id(namemap_t *map,
const char *name);
size_t namemap_get_size(const namemap_t *map);
void namemap_clear(namemap_t *map);
#endif
/* 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 NAMEMAP_ST_H
#define NAMEMAP_ST_H
#include "lib/cc/compat_compiler.h"
#include "ext/ht.h"
struct smartlist_t;
/** Longest allowed name that's allowed in a namemap_t. */
#define MAX_NAMEMAP_NAME_LEN 128
/** An entry inside a namemap_t. Maps a string to a numeric identifier. */
typedef struct mapped_name_t {
HT_ENTRY(mapped_name_t) node;
unsigned intval;
char name[FLEXIBLE_ARRAY_MEMBER];
} mapped_name_t;
/** A structure that allocates small numeric identifiers for names and maps
* back and forth between them. */
struct namemap_t {
HT_HEAD(namemap_ht, mapped_name_t) ht;
struct smartlist_t *names;
};
/** Macro to initialize a namemap. */
#define NAMEMAP_INIT() { HT_INITIALIZER(), NULL }
#endif
......@@ -148,6 +148,7 @@ src_test_test_SOURCES += \
src/test/test_logging.c \
src/test/test_mainloop.c \
src/test/test_microdesc.c \
src/test/test_namemap.c \
src/test/test_netinfo.c \
src/test/test_nodelist.c \
src/test/test_oom.c \
......
......@@ -857,6 +857,7 @@ struct testgroup_t testgroups[] = {
{ "consdiff/", consdiff_tests },
{ "consdiffmgr/", consdiffmgr_tests },
{ "container/", container_tests },
{ "container/namemap/", namemap_tests },
{ "control/", controller_tests },
{ "control/btrack/", btrack_tests },
{ "control/event/", controller_event_tests },
......
......@@ -234,6 +234,7 @@ extern struct testcase_t link_handshake_tests[];
extern struct testcase_t logging_tests[];
extern struct testcase_t mainloop_tests[];
extern struct testcase_t microdesc_tests[];
extern struct testcase_t namemap_tests[];
extern struct testcase_t netinfo_tests[];
extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
......
/* Copyright (c) 2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "test/test.h"
#include "lib/cc/torint.h"
#include "lib/container/namemap.h"
#include "lib/container/namemap_st.h"
#include "lib/malloc/malloc.h"
#include <stdio.h>
#include <string.h>
static void
test_namemap_empty(void *arg)
{
(void)arg;
namemap_t m;
namemap_init(&m);
namemap_t m2 = NAMEMAP_INIT();
tt_uint_op(0, OP_EQ, namemap_get_size(&m));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello128"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ""));
tt_uint_op(0, OP_EQ, namemap_get_size(&m));
tt_uint_op(0, OP_EQ, namemap_get_size(&m2));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello128"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, ""));
tt_uint_op(0, OP_EQ, namemap_get_size(&m));
done:
namemap_clear(&m);
namemap_clear(&m2);
}
static void
test_namemap_toolong(void *arg)
{
(void)arg;
namemap_t m;
char *ok = NULL;
char *toolong = NULL;
namemap_init(&m);
ok = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+1);
memset(ok, 'x', MAX_NAMEMAP_NAME_LEN);
toolong = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+2);
memset(toolong, 'x', MAX_NAMEMAP_NAME_LEN+1);
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ok));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
unsigned u1 = namemap_get_or_create_id(&m, toolong);
unsigned u2 = namemap_get_or_create_id(&m, ok);
tt_uint_op(u1, OP_EQ, NAMEMAP_ERR);
tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
tt_uint_op(u2, OP_EQ, namemap_get_id(&m, ok));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
tt_str_op(ok, OP_EQ, namemap_get_name(&m, u2));
tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m, u1));
done:
tor_free(ok);
tor_free(toolong);
namemap_clear(&m);
}
static void
test_namemap_blackbox(void *arg)
{
(void)arg;
namemap_t m1, m2;
namemap_init(&m1);
namemap_init(&m2);
unsigned u1 = namemap_get_or_create_id(&m1, "hello");
unsigned u2 = namemap_get_or_create_id(&m1, "world");
tt_uint_op(u1, OP_NE, NAMEMAP_ERR);
tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
tt_uint_op(u1, OP_NE, u2);
tt_uint_op(u1, OP_EQ, namemap_get_id(&m1, "hello"));
tt_uint_op(u1, OP_EQ, namemap_get_or_create_id(&m1, "hello"));
tt_uint_op(u2, OP_EQ, namemap_get_id(&m1, "world"));
tt_uint_op(u2, OP_EQ, namemap_get_or_create_id(&m1, "world"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "HELLO"));
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
unsigned u3 = namemap_get_or_create_id(&m2, "hola");
tt_uint_op(u3, OP_NE, NAMEMAP_ERR);
tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "hola"));
tt_uint_op(u3, OP_EQ, namemap_get_or_create_id(&m2, "hola"));
tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
unsigned int u4 = namemap_get_or_create_id(&m1, "hola");
tt_uint_op(u4, OP_NE, NAMEMAP_ERR);
tt_uint_op(u4, OP_EQ, namemap_get_id(&m1, "hola"));
tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
tt_str_op("hello", OP_EQ, namemap_get_name(&m1, u1));
tt_str_op("world", OP_EQ, namemap_get_name(&m1, u2));
tt_str_op("hola", OP_EQ, namemap_get_name(&m2, u3));
tt_str_op("hola", OP_EQ, namemap_get_name(&m1, u4));
tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m2, u3 + 10));
done:
namemap_clear(&m1);
namemap_clear(&m2);
}
static void
test_namemap_internals(void *arg)
{
(void)arg;
// This test actually assumes know something about the identity layout.
namemap_t m;
namemap_init(&m);
tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
tt_uint_op(1, OP_EQ, namemap_get_id(&m, "is"));
tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
done:
namemap_clear(&m);
}
#define T(name) \
{ #name, test_namemap_ ## name , 0, NULL, NULL }
struct testcase_t namemap_tests[] = {
T(empty),
T(toolong),
T(blackbox),
T(internals),
END_OF_TESTCASES
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment