blob: d031efc2264868e49895385620020ed56458bba6 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/transport/metadata.h"
35
Craig Tiller9fa41b92015-04-10 15:08:03 -070036#include <assert.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080037#include <stddef.h>
38#include <string.h>
39
Craig Tillerebdef9d2015-11-19 17:09:49 -080040#include <grpc/compression.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080041#include <grpc/support/alloc.h>
Craig Tiller9fa41b92015-04-10 15:08:03 -070042#include <grpc/support/atm.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080043#include <grpc/support/log.h>
Craig Tillerebdef9d2015-11-19 17:09:49 -080044#include <grpc/support/string_util.h>
Craig Tiller9d35a1f2015-11-02 14:16:12 -080045#include <grpc/support/time.h>
46#include "src/core/profiling/timers.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080047#include "src/core/support/murmur_hash.h"
Craig Tillerebdef9d2015-11-19 17:09:49 -080048#include "src/core/support/string.h"
ctiller430c4992014-12-11 09:15:41 -080049#include "src/core/transport/chttp2/bin_encoder.h"
Craig Tiller0e72ede2015-11-19 07:48:53 -080050#include "src/core/transport/static_metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080051
Craig Tiller70b080d2015-11-19 08:04:48 -080052/* There are two kinds of mdelem and mdstr instances.
53 * Static instances are declared in static_metadata.{h,c} and
54 * are initialized by grpc_mdctx_global_init().
55 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
56 * by internal_string and internal_element structures.
57 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
58 * used to determine which kind of element a pointer refers to.
59 */
60
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080061#define INITIAL_STRTAB_CAPACITY 4
62#define INITIAL_MDTAB_CAPACITY 4
63
Craig Tiller1a65a232015-07-06 10:22:32 -070064#ifdef GRPC_METADATA_REFCOUNT_DEBUG
65#define DEBUG_ARGS , const char *file, int line
66#define FWD_DEBUG_ARGS , file, line
Craig Tillerb2b42612015-11-20 12:02:17 -080067#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070068#else
69#define DEBUG_ARGS
70#define FWD_DEBUG_ARGS
Craig Tillerb2b42612015-11-20 12:02:17 -080071#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
Craig Tiller1a65a232015-07-06 10:22:32 -070072#endif
73
Craig Tillerb2b42612015-11-20 12:02:17 -080074#define TABLE_IDX(hash, log2_shards, capacity) \
75 (((hash) >> (log2_shards)) % (capacity))
76#define SHARD_IDX(hash, log2_shards) ((hash) & ((1 << (log2_shards)) - 1))
77
Craig Tiller8344daa2015-10-09 18:10:57 -070078typedef void (*destroy_user_data_func)(void *user_data);
79
Craig Tiller70b080d2015-11-19 08:04:48 -080080/* Shadow structure for grpc_mdstr for non-static values */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080081typedef struct internal_string {
82 /* must be byte compatible with grpc_mdstr */
83 gpr_slice slice;
84 gpr_uint32 hash;
85
86 /* private only data */
Craig Tillerb2b42612015-11-20 12:02:17 -080087 gpr_atm refcnt;
88
ctiller430c4992014-12-11 09:15:41 -080089 gpr_uint8 has_base64_and_huffman_encoded;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080090 gpr_slice_refcount refcount;
91
ctiller430c4992014-12-11 09:15:41 -080092 gpr_slice base64_and_huffman;
93
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080094 struct internal_string *bucket_next;
95} internal_string;
96
Craig Tiller70b080d2015-11-19 08:04:48 -080097/* Shadow structure for grpc_mdelem for non-static elements */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080098typedef struct internal_metadata {
99 /* must be byte compatible with grpc_mdelem */
100 internal_string *key;
101 internal_string *value;
102
Craig Tillerb2b42612015-11-20 12:02:17 -0800103 /* private only data */
Craig Tiller9fa41b92015-04-10 15:08:03 -0700104 gpr_atm refcnt;
105
Craig Tiller83901532015-07-10 14:02:45 -0700106 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -0700107 gpr_atm destroy_user_data;
108 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800109
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800110 struct internal_metadata *bucket_next;
111} internal_metadata;
112
Craig Tillerb2b42612015-11-20 12:02:17 -0800113typedef struct strtab_shard {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800114 gpr_mu mu;
Craig Tillerb2b42612015-11-20 12:02:17 -0800115 internal_string **strs;
116 size_t count;
117 size_t capacity;
118} strtab_shard;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800119
Craig Tillerb2b42612015-11-20 12:02:17 -0800120typedef struct mdtab_shard {
121 gpr_mu mu;
122 internal_metadata **elems;
123 size_t count;
124 size_t capacity;
125 size_t free;
126} mdtab_shard;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800127
Craig Tillerb2b42612015-11-20 12:02:17 -0800128#define LOG2_STRTAB_SHARD_COUNT 5
129#define LOG2_MDTAB_SHARD_COUNT 4
130#define STRTAB_SHARD_COUNT ((size_t)(1 << LOG2_STRTAB_SHARD_COUNT))
131#define MDTAB_SHARD_COUNT ((size_t)(1 << LOG2_MDTAB_SHARD_COUNT))
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800132
Craig Tillerb2b42612015-11-20 12:02:17 -0800133/* hash seed: decided at initialization time */
134static gpr_uint32 g_hash_seed;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800135
Craig Tillerb2b42612015-11-20 12:02:17 -0800136/* linearly probed hash tables for static element lookup */
137static grpc_mdstr *g_static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
138static grpc_mdelem *g_static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
139static size_t g_static_strtab_maxprobe;
140static size_t g_static_mdtab_maxprobe;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800141
Craig Tillerb2b42612015-11-20 12:02:17 -0800142static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
143static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
144
145static void discard_metadata(mdtab_shard *shard);
146static void gc_mdtab(mdtab_shard *shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800147
Craig Tiller0e72ede2015-11-19 07:48:53 -0800148void grpc_mdctx_global_init(void) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800149 size_t i, j;
150 g_hash_seed = (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
151 g_static_strtab_maxprobe = 0;
152 g_static_mdtab_maxprobe = 0;
153 /* build static tables */
154 memset(g_static_mdtab, 0, sizeof(g_static_mdtab));
155 memset(g_static_strtab, 0, sizeof(g_static_strtab));
Craig Tiller0e72ede2015-11-19 07:48:53 -0800156 for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
157 grpc_mdstr *elem = &grpc_static_mdstr_table[i];
158 const char *str = grpc_static_metadata_strings[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800159 gpr_uint32 hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed);
Craig Tillerb774be42015-11-19 07:56:13 -0800160 *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str);
Craig Tillerb2b42612015-11-20 12:02:17 -0800161 *(gpr_uint32 *)&elem->hash = hash;
162 for (j = 0;; j++) {
163 size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_strtab);
164 if (g_static_strtab[idx] == NULL) {
165 g_static_strtab[idx] = &grpc_static_mdstr_table[i];
166 break;
167 }
168 }
169 if (j > g_static_strtab_maxprobe) {
170 g_static_strtab_maxprobe = j;
171 }
Craig Tiller0e72ede2015-11-19 07:48:53 -0800172 }
173 for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
174 grpc_mdelem *elem = &grpc_static_mdelem_table[i];
Craig Tillerebdef9d2015-11-19 17:09:49 -0800175 grpc_mdstr *key =
176 &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]];
177 grpc_mdstr *value =
178 &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]];
Craig Tillerb2b42612015-11-20 12:02:17 -0800179 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(key->hash, value->hash);
Craig Tillerb774be42015-11-19 07:56:13 -0800180 *(grpc_mdstr **)&elem->key = key;
181 *(grpc_mdstr **)&elem->value = value;
Craig Tillerb2b42612015-11-20 12:02:17 -0800182 for (j = 0;; j++) {
183 size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_mdtab);
184 if (g_static_mdtab[idx] == NULL) {
185 g_static_mdtab[idx] = elem;
186 break;
187 }
188 }
189 if (j > g_static_mdtab_maxprobe) {
190 g_static_mdtab_maxprobe = j;
191 }
192 }
193 /* initialize shards */
194 for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
195 strtab_shard *shard = &g_strtab_shard[i];
196 gpr_mu_init(&shard->mu);
197 shard->count = 0;
198 shard->capacity = INITIAL_STRTAB_CAPACITY;
199 shard->strs = gpr_malloc(sizeof(*shard->strs) * shard->capacity);
200 memset(shard->strs, 0, sizeof(*shard->strs) * shard->capacity);
201 }
202 for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
203 mdtab_shard *shard = &g_mdtab_shard[i];
204 gpr_mu_init(&shard->mu);
205 shard->count = 0;
206 shard->free = 0;
207 shard->capacity = INITIAL_MDTAB_CAPACITY;
208 shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
209 memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800210 }
211}
212
Craig Tillerb2b42612015-11-20 12:02:17 -0800213void grpc_mdctx_global_shutdown(void) {
214 size_t i;
215 for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
216 mdtab_shard *shard = &g_mdtab_shard[i];
217 gpr_mu_destroy(&shard->mu);
218 discard_metadata(shard);
219 GPR_ASSERT(shard->count == 0);
220 gpr_free(shard->elems);
221 }
222 for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
223 strtab_shard *shard = &g_strtab_shard[i];
224 gpr_mu_destroy(&shard->mu);
225 GPR_ASSERT(shard->count == 0);
226 gpr_free(shard->strs);
227 }
228}
Craig Tiller0e72ede2015-11-19 07:48:53 -0800229
230static int is_mdstr_static(grpc_mdstr *s) {
Craig Tillerb774be42015-11-19 07:56:13 -0800231 return s >= &grpc_static_mdstr_table[0] &&
232 s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800233}
234
235static int is_mdelem_static(grpc_mdelem *e) {
Craig Tillerb774be42015-11-19 07:56:13 -0800236 return e >= &grpc_static_mdelem_table[0] &&
237 e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800238}
239
Craig Tillerb2b42612015-11-20 12:02:17 -0800240static void ref_md_locked(mdtab_shard *shard,
241 internal_metadata *md DEBUG_ARGS) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700242#ifdef GRPC_METADATA_REFCOUNT_DEBUG
243 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
244 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
245 gpr_atm_no_barrier_load(&md->refcnt),
246 gpr_atm_no_barrier_load(&md->refcnt) + 1,
247 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
248 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
249#endif
Craig Tillerb8e82672015-10-10 13:52:47 -0700250 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800251 shard->free--;
Craig Tillerb8e82672015-10-10 13:52:47 -0700252 } else {
253 GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800254 }
255}
256
Craig Tillerb2b42612015-11-20 12:02:17 -0800257#if 0
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800258grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
259 grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
Craig Tiller0e72ede2015-11-19 07:48:53 -0800260 size_t i, j;
261
262 memset(ctx, 0, sizeof(*ctx));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800263
Craig Tillerb2b42612015-11-20 12:02:17 -0800264 g_refs = 1;
265 g_hash_seed = seed;
266 gpr_mu_init(&g_mu);
267 g_strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
268 memset(g_strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
269 g_strtab_count = 0;
270 g_strtab_capacity = INITIAL_STRTAB_CAPACITY;
271 g_mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
272 memset(g_mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
273 g_mdtab_count = 0;
274 g_mdtab_capacity = INITIAL_MDTAB_CAPACITY;
275 g_mdtab_free = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800276
Craig Tiller0e72ede2015-11-19 07:48:53 -0800277
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800278 return ctx;
279}
280
Craig Tiller32946d32015-01-15 11:37:30 -0800281grpc_mdctx *grpc_mdctx_create(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800282 /* This seed is used to prevent remote connections from controlling hash table
283 * collisions. It needs to be somewhat unpredictable to a remote connection.
284 */
Craig Tiller32ca48c2015-09-10 11:47:15 -0700285 return grpc_mdctx_create_with_seed(
286 (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800287}
Craig Tillerb2b42612015-11-20 12:02:17 -0800288#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800289
Craig Tillerb2b42612015-11-20 12:02:17 -0800290static void discard_metadata(mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800291 size_t i;
292 internal_metadata *next, *cur;
293
Craig Tillerb2b42612015-11-20 12:02:17 -0800294 for (i = 0; i < shard->capacity; i++) {
295 cur = shard->elems[i];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800296 while (cur) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700297 void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700298 GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800299 next = cur->bucket_next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800300 GRPC_MDSTR_UNREF((grpc_mdstr *)cur->key);
301 GRPC_MDSTR_UNREF((grpc_mdstr *)cur->value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700302 if (user_data != NULL) {
303 ((destroy_user_data_func)gpr_atm_no_barrier_load(
304 &cur->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800305 }
Craig Tiller83901532015-07-10 14:02:45 -0700306 gpr_mu_destroy(&cur->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800307 gpr_free(cur);
308 cur = next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800309 shard->free--;
310 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800311 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800312 shard->elems[i] = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800313 }
314}
315
Craig Tillerb2b42612015-11-20 12:02:17 -0800316#if 0
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700317static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800318 GPR_ASSERT(g_strtab_count == 0);
319 GPR_ASSERT(g_mdtab_count == 0);
320 GPR_ASSERT(g_mdtab_free == 0);
321 gpr_free(g_strtab);
322 gpr_free(g_mdtab);
323 gpr_mu_unlock(&g_mu);
324 gpr_mu_destroy(&g_mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800325 gpr_free(ctx);
326}
327
Craig Tiller9be83ee2015-02-18 14:16:15 -0800328void grpc_mdctx_ref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800329 GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800330 lock(ctx);
Craig Tillerb2b42612015-11-20 12:02:17 -0800331 GPR_ASSERT(g_refs > 0);
332 g_refs++;
Craig Tiller9be83ee2015-02-18 14:16:15 -0800333 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800334 GPR_TIMER_END("grpc_mdctx_ref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800335}
336
337void grpc_mdctx_unref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800338 GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800339 lock(ctx);
Craig Tillerb2b42612015-11-20 12:02:17 -0800340 GPR_ASSERT(g_refs > 0);
341 g_refs--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800342 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800343 GPR_TIMER_END("grpc_mdctx_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800344}
Craig Tillerb2b42612015-11-20 12:02:17 -0800345#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800346
Craig Tillerb2b42612015-11-20 12:02:17 -0800347static void grow_strtab(strtab_shard *shard) {
348 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800349 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800350 internal_string **strtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800351 internal_string *s, *next;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800352
353 GPR_TIMER_BEGIN("grow_strtab", 0);
354
355 strtab = gpr_malloc(sizeof(internal_string *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800356 memset(strtab, 0, sizeof(internal_string *) * capacity);
357
Craig Tillerb2b42612015-11-20 12:02:17 -0800358 for (i = 0; i < shard->capacity; i++) {
359 for (s = shard->strs[i]; s; s = next) {
360 size_t idx = TABLE_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT, capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800361 next = s->bucket_next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800362 s->bucket_next = strtab[idx];
363 strtab[idx] = s;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800364 }
365 }
366
Craig Tillerb2b42612015-11-20 12:02:17 -0800367 gpr_free(shard->strs);
368 shard->strs = strtab;
369 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800370
371 GPR_TIMER_END("grow_strtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800372}
373
Craig Tillerb2b42612015-11-20 12:02:17 -0800374static void internal_destroy_string(strtab_shard *shard, internal_string *is) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800375 internal_string **prev_next;
376 internal_string *cur;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800377 GPR_TIMER_BEGIN("internal_destroy_string", 0);
ctiller430c4992014-12-11 09:15:41 -0800378 if (is->has_base64_and_huffman_encoded) {
379 gpr_slice_unref(is->base64_and_huffman);
380 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800381 for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT,
382 shard->capacity)],
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800383 cur = *prev_next;
384 cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
385 ;
386 *prev_next = cur->bucket_next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800387 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800388 gpr_free(is);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800389 GPR_TIMER_END("internal_destroy_string", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800390}
391
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800392static void slice_ref(void *p) {
393 internal_string *is =
394 (internal_string *)((char *)p - offsetof(internal_string, refcount));
Craig Tillerb2b42612015-11-20 12:02:17 -0800395 GRPC_MDSTR_REF((grpc_mdstr *)(is));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800396}
397
398static void slice_unref(void *p) {
399 internal_string *is =
400 (internal_string *)((char *)p - offsetof(internal_string, refcount));
Craig Tillerb2b42612015-11-20 12:02:17 -0800401 GRPC_MDSTR_UNREF((grpc_mdstr *)(is));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800402}
403
Craig Tillerb2b42612015-11-20 12:02:17 -0800404grpc_mdstr *grpc_mdstr_from_string(const char *str) {
405 return grpc_mdstr_from_buffer((const gpr_uint8 *)str, strlen(str));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800406}
407
Craig Tillerb2b42612015-11-20 12:02:17 -0800408grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice) {
409 grpc_mdstr *result = grpc_mdstr_from_buffer(GPR_SLICE_START_PTR(slice),
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800410 GPR_SLICE_LENGTH(slice));
411 gpr_slice_unref(slice);
412 return result;
413}
414
Craig Tillerb2b42612015-11-20 12:02:17 -0800415grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *buf, size_t length) {
416 gpr_uint32 hash = gpr_murmur_hash3(buf, length, g_hash_seed);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800417 internal_string *s;
Craig Tillerb2b42612015-11-20 12:02:17 -0800418 strtab_shard *shard =
419 &g_strtab_shard[SHARD_IDX(hash, LOG2_STRTAB_SHARD_COUNT)];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800420 size_t i;
Craig Tillerb2b42612015-11-20 12:02:17 -0800421 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800422
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800423 GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800424
425 /* search for a static string */
Craig Tillerb2b42612015-11-20 12:02:17 -0800426 for (i = 0; i <= g_static_strtab_maxprobe; i++) {
427 grpc_mdstr *ss;
428 idx = (hash + i) % GPR_ARRAY_SIZE(g_static_strtab);
429 ss = g_static_strtab[idx];
430 if (ss == NULL) break;
431 if (ss->hash == hash && GPR_SLICE_LENGTH(ss->slice) == length &&
432 0 == memcmp(buf, GPR_SLICE_START_PTR(ss->slice), length)) {
433 return ss;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800434 }
435 }
436
Craig Tillerb2b42612015-11-20 12:02:17 -0800437 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800438
439 /* search for an existing string */
Craig Tillerb2b42612015-11-20 12:02:17 -0800440 idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity);
441 for (s = shard->strs[idx]; s; s = s->bucket_next) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800442 if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
443 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800444 GRPC_MDSTR_REF((grpc_mdstr *)s);
445 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800446 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800447 return (grpc_mdstr *)s;
448 }
449 }
450
451 /* not found: create a new string */
452 if (length + 1 < GPR_SLICE_INLINED_SIZE) {
453 /* string data goes directly into the slice */
454 s = gpr_malloc(sizeof(internal_string));
Craig Tillerb2b42612015-11-20 12:02:17 -0800455 gpr_atm_rel_store(&s->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800456 s->slice.refcount = NULL;
457 memcpy(s->slice.data.inlined.bytes, buf, length);
458 s->slice.data.inlined.bytes[length] = 0;
Craig Tiller6a6b36c2015-09-10 16:00:22 -0700459 s->slice.data.inlined.length = (gpr_uint8)length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800460 } else {
461 /* string data goes after the internal_string header, and we +1 for null
462 terminator */
463 s = gpr_malloc(sizeof(internal_string) + length + 1);
Craig Tillerb2b42612015-11-20 12:02:17 -0800464 gpr_atm_rel_store(&s->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800465 s->refcount.ref = slice_ref;
466 s->refcount.unref = slice_unref;
467 s->slice.refcount = &s->refcount;
468 s->slice.data.refcounted.bytes = (gpr_uint8 *)(s + 1);
469 s->slice.data.refcounted.length = length;
470 memcpy(s->slice.data.refcounted.bytes, buf, length);
471 /* add a null terminator for cheap c string conversion when desired */
472 s->slice.data.refcounted.bytes[length] = 0;
473 }
ctiller430c4992014-12-11 09:15:41 -0800474 s->has_base64_and_huffman_encoded = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800475 s->hash = hash;
Craig Tillerb2b42612015-11-20 12:02:17 -0800476 s->bucket_next = shard->strs[idx];
477 shard->strs[idx] = s;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800478
Craig Tillerb2b42612015-11-20 12:02:17 -0800479 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800480
Craig Tillerb2b42612015-11-20 12:02:17 -0800481 if (shard->count > shard->capacity * 2) {
482 grow_strtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800483 }
484
Craig Tillerb2b42612015-11-20 12:02:17 -0800485 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800486 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800487
488 return (grpc_mdstr *)s;
489}
490
Craig Tillerb2b42612015-11-20 12:02:17 -0800491static void gc_mdtab(mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800492 size_t i;
493 internal_metadata **prev_next;
494 internal_metadata *md, *next;
495
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800496 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800497 for (i = 0; i < shard->capacity; i++) {
498 prev_next = &shard->elems[i];
499 for (md = shard->elems[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700500 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800501 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700502 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800503 GRPC_MDSTR_UNREF((grpc_mdstr *)md->key);
504 GRPC_MDSTR_UNREF((grpc_mdstr *)md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800505 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700506 ((destroy_user_data_func)gpr_atm_no_barrier_load(
507 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800508 }
509 gpr_free(md);
510 *prev_next = next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800511 shard->free--;
512 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800513 } else {
514 prev_next = &md->bucket_next;
515 }
516 }
517 }
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800518 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800519}
520
Craig Tillerb2b42612015-11-20 12:02:17 -0800521static void grow_mdtab(mdtab_shard *shard) {
522 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800523 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800524 internal_metadata **mdtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800525 internal_metadata *md, *next;
526 gpr_uint32 hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800527
528 GPR_TIMER_BEGIN("grow_mdtab", 0);
529
530 mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800531 memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
532
Craig Tillerb2b42612015-11-20 12:02:17 -0800533 for (i = 0; i < shard->capacity; i++) {
534 for (md = shard->elems[i]; md; md = next) {
535 size_t idx;
ctillerfb93d192014-12-15 10:40:05 -0800536 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800537 next = md->bucket_next;
Craig Tillerb2b42612015-11-20 12:02:17 -0800538 idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity);
539 md->bucket_next = mdtab[idx];
540 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800541 }
542 }
543
Craig Tillerb2b42612015-11-20 12:02:17 -0800544 gpr_free(shard->elems);
545 shard->elems = mdtab;
546 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800547
548 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800549}
550
Craig Tillerb2b42612015-11-20 12:02:17 -0800551static void rehash_mdtab(mdtab_shard *shard) {
552 if (shard->free > shard->capacity / 4) {
553 gc_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800554 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800555 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800556 }
557}
558
Craig Tillerb2b42612015-11-20 12:02:17 -0800559grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800560 grpc_mdstr *mvalue) {
561 internal_string *key = (internal_string *)mkey;
562 internal_string *value = (internal_string *)mvalue;
ctillerfb93d192014-12-15 10:40:05 -0800563 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800564 internal_metadata *md;
Craig Tillerb2b42612015-11-20 12:02:17 -0800565 mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800566 size_t i;
Craig Tillerb2b42612015-11-20 12:02:17 -0800567 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800568
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800569 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
570
Craig Tiller0e72ede2015-11-19 07:48:53 -0800571 if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800572 for (i = 0; i <= g_static_mdtab_maxprobe; i++) {
573 grpc_mdelem *smd;
574 idx = (hash + i) % GPR_ARRAY_SIZE(g_static_mdtab);
575 smd = g_static_mdtab[idx];
576 if (smd == NULL) break;
577 if (smd->key == mkey && smd->value == mvalue) {
578 return smd;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800579 }
580 }
581 }
582
Craig Tillerb2b42612015-11-20 12:02:17 -0800583 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800584
Craig Tillerb2b42612015-11-20 12:02:17 -0800585 idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800586 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800587 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800588 if (md->key == key && md->value == value) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800589 REF_MD_LOCKED(shard, md);
590 GRPC_MDSTR_UNREF((grpc_mdstr *)key);
591 GRPC_MDSTR_UNREF((grpc_mdstr *)value);
592 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800593 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800594 return (grpc_mdelem *)md;
595 }
596 }
597
598 /* not found: create a new pair */
599 md = gpr_malloc(sizeof(internal_metadata));
Craig Tiller63bda562015-10-09 17:40:19 -0700600 gpr_atm_rel_store(&md->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800601 md->key = key;
602 md->value = value;
Craig Tiller8344daa2015-10-09 18:10:57 -0700603 md->user_data = 0;
604 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800605 md->bucket_next = shard->elems[idx];
606 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700607 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700608#ifdef GRPC_METADATA_REFCOUNT_DEBUG
609 gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
610 gpr_atm_no_barrier_load(&md->refcnt),
611 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
612 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
613#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800614 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800615
Craig Tillerb2b42612015-11-20 12:02:17 -0800616 if (shard->count > shard->capacity * 2) {
617 rehash_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800618 }
619
Craig Tillerb2b42612015-11-20 12:02:17 -0800620 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800621
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800622 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
623
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800624 return (grpc_mdelem *)md;
625}
626
Craig Tillerb2b42612015-11-20 12:02:17 -0800627grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) {
628 return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key),
629 grpc_mdstr_from_string(value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800630}
631
Craig Tillerb2b42612015-11-20 12:02:17 -0800632grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value) {
633 return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key),
634 grpc_mdstr_from_slice(value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800635}
636
Craig Tillerb2b42612015-11-20 12:02:17 -0800637grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800638 const gpr_uint8 *value,
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700639 size_t value_length) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800640 return grpc_mdelem_from_metadata_strings(
Craig Tillerb2b42612015-11-20 12:02:17 -0800641 grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800642}
643
Craig Tiller1a65a232015-07-06 10:22:32 -0700644grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800645 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800646 if (is_mdelem_static(gmd)) return gmd;
Craig Tiller1a65a232015-07-06 10:22:32 -0700647#ifdef GRPC_METADATA_REFCOUNT_DEBUG
648 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
649 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
650 gpr_atm_no_barrier_load(&md->refcnt),
651 gpr_atm_no_barrier_load(&md->refcnt) + 1,
652 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
653 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
654#endif
Craig Tiller9fa41b92015-04-10 15:08:03 -0700655 /* we can assume the ref count is >= 1 as the application is calling
656 this function - meaning that no adjustment to mdtab_free is necessary,
657 simplifying the logic here to be just an atomic increment */
658 /* use C assert to have this removed in opt builds */
Craig Tillerb8e82672015-10-10 13:52:47 -0700659 assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700660 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800661 return gmd;
662}
663
Craig Tiller1a65a232015-07-06 10:22:32 -0700664void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800665 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800666 if (!md) return;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800667 if (is_mdelem_static(gmd)) return;
Craig Tiller1a65a232015-07-06 10:22:32 -0700668#ifdef GRPC_METADATA_REFCOUNT_DEBUG
669 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
670 "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
671 gpr_atm_no_barrier_load(&md->refcnt),
672 gpr_atm_no_barrier_load(&md->refcnt) - 1,
673 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
674 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
675#endif
Craig Tiller63bda562015-10-09 17:40:19 -0700676 if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800677 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
678 mdtab_shard *shard =
679 &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800680 GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800681 gpr_mu_lock(&shard->mu);
Craig Tillerb8e82672015-10-10 13:52:47 -0700682 if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800683 shard->free++;
Craig Tillerb8e82672015-10-10 13:52:47 -0700684 gpr_atm_no_barrier_store(&md->refcnt, 0);
685 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800686 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800687 GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800688 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800689}
690
691const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
692 return (const char *)GPR_SLICE_START_PTR(s->slice);
693}
694
Craig Tiller1a65a232015-07-06 10:22:32 -0700695grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800696 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800697 if (is_mdstr_static(gs)) return gs;
Craig Tillerb2b42612015-11-20 12:02:17 -0800698 GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800699 return gs;
700}
701
Craig Tiller1a65a232015-07-06 10:22:32 -0700702void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800703 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800704 if (is_mdstr_static(gs)) return;
Craig Tillerb2b42612015-11-20 12:02:17 -0800705 if (2 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
706 strtab_shard *shard =
707 &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
708 gpr_mu_lock(&shard->mu);
709 if (1 == gpr_atm_no_barrier_load(&s->refcnt)) {
710 internal_destroy_string(shard, s);
711 }
712 gpr_mu_unlock(&shard->mu);
713 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800714}
715
Craig Tiller8344daa2015-10-09 18:10:57 -0700716void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800717 internal_metadata *im = (internal_metadata *)md;
Craig Tiller83901532015-07-10 14:02:45 -0700718 void *result;
Craig Tillerb2b42612015-11-20 12:02:17 -0800719 if (is_mdelem_static(md)) {
720 return (void *)grpc_static_mdelem_user_data[md - grpc_static_mdelem_table];
721 }
Craig Tiller8344daa2015-10-09 18:10:57 -0700722 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
723 return (void *)gpr_atm_no_barrier_load(&im->user_data);
724 } else {
725 return NULL;
726 }
Craig Tiller83901532015-07-10 14:02:45 -0700727 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800728}
729
730void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
731 void *user_data) {
732 internal_metadata *im = (internal_metadata *)md;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800733 GPR_ASSERT(!is_mdelem_static(md));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800734 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
Craig Tiller83901532015-07-10 14:02:45 -0700735 gpr_mu_lock(&im->mu_user_data);
Craig Tiller8344daa2015-10-09 18:10:57 -0700736 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
Craig Tiller83901532015-07-10 14:02:45 -0700737 /* user data can only be set once */
738 gpr_mu_unlock(&im->mu_user_data);
739 if (destroy_func != NULL) {
740 destroy_func(user_data);
741 }
742 return;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800743 }
Craig Tiller8344daa2015-10-09 18:10:57 -0700744 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
745 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
Craig Tiller83901532015-07-10 14:02:45 -0700746 gpr_mu_unlock(&im->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800747}
ctiller430c4992014-12-11 09:15:41 -0800748
749gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
750 internal_string *s = (internal_string *)gs;
751 gpr_slice slice;
Craig Tillerb2b42612015-11-20 12:02:17 -0800752 strtab_shard *shard =
753 &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
754 gpr_mu_lock(&shard->mu);
ctiller430c4992014-12-11 09:15:41 -0800755 if (!s->has_base64_and_huffman_encoded) {
756 s->base64_and_huffman =
757 grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
ctiller33023c42014-12-12 16:28:33 -0800758 s->has_base64_and_huffman_encoded = 1;
ctiller430c4992014-12-11 09:15:41 -0800759 }
760 slice = s->base64_and_huffman;
Craig Tillerb2b42612015-11-20 12:02:17 -0800761 gpr_mu_unlock(&shard->mu);
ctiller430c4992014-12-11 09:15:41 -0800762 return slice;
Craig Tiller190d3602015-02-18 09:23:38 -0800763}
Craig Tillerfe0104a2015-04-14 09:19:12 -0700764
Craig Tiller49772e02015-08-21 08:08:37 -0700765static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) {
Craig Tillerb96d0012015-05-06 15:33:23 -0700766 const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice);
767 const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice);
768 for (; p != e; p++) {
Craig Tiller49772e02015-08-21 08:08:37 -0700769 int idx = *p;
770 int byte = idx / 8;
771 int bit = idx % 8;
772 if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
Craig Tillerb96d0012015-05-06 15:33:23 -0700773 }
774 return 1;
775}
776
Craig Tiller49772e02015-08-21 08:08:37 -0700777int grpc_mdstr_is_legal_header(grpc_mdstr *s) {
778 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller80aa2802015-08-21 08:50:51 -0700779 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00,
780 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Craig Tiller49772e02015-08-21 08:08:37 -0700781 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Craig Tiller49772e02015-08-21 08:08:37 -0700782 return conforms_to(s, legal_header_bits);
783}
784
785int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
786 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller240b7db2015-08-27 15:35:32 -0700787 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
Craig Tiller49772e02015-08-21 08:08:37 -0700788 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
789 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
790 return conforms_to(s, legal_header_bits);
791}
792
Craig Tillerb96d0012015-05-06 15:33:23 -0700793int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
794 /* TODO(ctiller): consider caching this */
795 return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
796 GPR_SLICE_LENGTH(s->slice));
797}