Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / lib / container / namemap.c
blob1676209e14ec60641e1376dc140bcb18cf56e6df
1 /* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
6 /**
7 * @file namemap.c
8 * @brief Mappings between identifiers and 16-bit ints.
9 **/
11 #include "orconfig.h"
12 #include "lib/container/smartlist.h"
13 #include "lib/container/namemap.h"
14 #include "lib/container/namemap_st.h"
15 #include "lib/log/util_bug.h"
16 #include "lib/malloc/malloc.h"
17 #include "lib/string/printf.h"
19 #include "ext/siphash.h"
21 #include <string.h>
23 /** Helper for namemap hashtable implementation: compare two entries. */
24 static inline int
25 mapped_name_eq(const mapped_name_t *a, const mapped_name_t *b)
27 return !strcmp(a->name, b->name);
30 /** Helper for namemap hashtable implementation: hash an entry. */
31 static inline unsigned
32 mapped_name_hash(const mapped_name_t *a)
34 return (unsigned) siphash24g(a->name, strlen(a->name));
37 HT_PROTOTYPE(namemap_ht, mapped_name_t, node, mapped_name_hash,
38 mapped_name_eq);
39 HT_GENERATE2(namemap_ht, mapped_name_t, node, mapped_name_hash,
40 mapped_name_eq, 0.6, tor_reallocarray_, tor_free_);
42 /** Set up an uninitialized <b>map</b>. */
43 void
44 namemap_init(namemap_t *map)
46 memset(map, 0, sizeof(*map));
47 HT_INIT(namemap_ht, &map->ht);
48 map->names = smartlist_new();
51 /** Return the name that <b>map</b> associates with a given <b>id</b>, or
52 * NULL if there is no such name. */
53 const char *
54 namemap_get_name(const namemap_t *map, unsigned id)
56 if (map->names && id < (unsigned)smartlist_len(map->names)) {
57 mapped_name_t *name = smartlist_get(map->names, (int)id);
58 return name->name;
59 } else {
60 return NULL;
64 /**
65 * Return the name that <b>map</b> associates with a given <b>id</b>, or a
66 * pointer to a statically allocated string describing the value of <b>id</b>
67 * if no such name exists.
68 **/
69 const char *
70 namemap_fmt_name(const namemap_t *map, unsigned id)
72 static char buf[32];
74 const char *name = namemap_get_name(map, id);
75 if (name)
76 return name;
78 tor_snprintf(buf, sizeof(buf), "{%u}", id);
80 return buf;
83 /**
84 * Helper: As namemap_get_id(), but requires that <b>name</b> is
85 * <b>namelen</b> characters long, and that <b>namelen</b> is no more than
86 * MAX_NAMEMAP_NAME_LEN.
88 static unsigned
89 namemap_get_id_unchecked(const namemap_t *map,
90 const char *name,
91 size_t namelen)
93 union {
94 mapped_name_t n;
95 char storage[MAX_NAMEMAP_NAME_LEN + sizeof(mapped_name_t) + 1];
96 } u;
97 memcpy(u.n.name, name, namelen);
98 u.n.name[namelen] = 0;
99 const mapped_name_t *found = HT_FIND(namemap_ht, &map->ht, &u.n);
100 if (found) {
101 tor_assert(map->names);
102 tor_assert(smartlist_get(map->names, found->intval) == found);
103 return found->intval;
106 return NAMEMAP_ERR;
110 * Return the identifier currently associated by <b>map</b> with the name
111 * <b>name</b>, or NAMEMAP_ERR if no such identifier exists.
113 unsigned
114 namemap_get_id(const namemap_t *map,
115 const char *name)
117 size_t namelen = strlen(name);
118 if (namelen > MAX_NAMEMAP_NAME_LEN) {
119 return NAMEMAP_ERR;
122 return namemap_get_id_unchecked(map, name, namelen);
126 * Return the identifier associated by <b>map</b> with the name
127 * <b>name</b>, allocating a new identifier in <b>map</b> if none exists.
129 * Return NAMEMAP_ERR if <b>name</b> is too long, or if there are no more
130 * identifiers we can allocate.
132 unsigned
133 namemap_get_or_create_id(namemap_t *map,
134 const char *name)
136 size_t namelen = strlen(name);
137 if (namelen > MAX_NAMEMAP_NAME_LEN) {
138 return NAMEMAP_ERR;
141 if (PREDICT_UNLIKELY(map->names == NULL))
142 map->names = smartlist_new();
144 unsigned found = namemap_get_id_unchecked(map, name, namelen);
145 if (found != NAMEMAP_ERR)
146 return found;
148 unsigned new_id = (unsigned)smartlist_len(map->names);
149 if (new_id == NAMEMAP_ERR)
150 return NAMEMAP_ERR; /* Can't allocate any more. */
152 mapped_name_t *insert = tor_malloc_zero(
153 offsetof(mapped_name_t, name) + namelen + 1);
154 memcpy(insert->name, name, namelen+1);
155 insert->intval = new_id;
157 HT_INSERT(namemap_ht, &map->ht, insert);
158 smartlist_add(map->names, insert);
160 return new_id;
163 /** Return the number of entries in 'names' */
164 size_t
165 namemap_get_size(const namemap_t *map)
167 if (PREDICT_UNLIKELY(map->names == NULL))
168 return 0;
170 return smartlist_len(map->names);
174 * Release all storage held in <b>map</b>.
176 void
177 namemap_clear(namemap_t *map)
179 if (!map)
180 return;
182 HT_CLEAR(namemap_ht, &map->ht);
183 if (map->names) {
184 SMARTLIST_FOREACH(map->names, mapped_name_t *, n,
185 tor_free(n));
186 smartlist_free(map->names);
188 memset(map, 0, sizeof(*map));