blob: a72dee4399965974ddd9bf9acc258e60d2a3ec51 [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
Nicolas "Pixel" Nobled5a99852015-01-24 01:27:48 -080034#include "src/core/iomgr/sockaddr.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035#include "src/core/transport/metadata.h"
36
Craig Tiller9fa41b92015-04-10 15:08:03 -070037#include <assert.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038#include <stddef.h>
39#include <string.h>
40
41#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 Tiller9d35a1f2015-11-02 14:16:12 -080044#include <grpc/support/time.h>
45#include "src/core/profiling/timers.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080046#include "src/core/support/murmur_hash.h"
ctiller430c4992014-12-11 09:15:41 -080047#include "src/core/transport/chttp2/bin_encoder.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080048
49#define INITIAL_STRTAB_CAPACITY 4
50#define INITIAL_MDTAB_CAPACITY 4
51
Craig Tiller1a65a232015-07-06 10:22:32 -070052#ifdef GRPC_METADATA_REFCOUNT_DEBUG
53#define DEBUG_ARGS , const char *file, int line
54#define FWD_DEBUG_ARGS , file, line
55#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
56#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
57#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
58#else
59#define DEBUG_ARGS
60#define FWD_DEBUG_ARGS
61#define INTERNAL_STRING_REF(s) internal_string_ref((s))
62#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
63#define REF_MD_LOCKED(s) ref_md_locked((s))
64#endif
65
Craig Tiller8344daa2015-10-09 18:10:57 -070066typedef void (*destroy_user_data_func)(void *user_data);
67
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080068typedef struct internal_string {
69 /* must be byte compatible with grpc_mdstr */
70 gpr_slice slice;
71 gpr_uint32 hash;
72
73 /* private only data */
74 gpr_uint32 refs;
ctiller430c4992014-12-11 09:15:41 -080075 gpr_uint8 has_base64_and_huffman_encoded;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080076 gpr_slice_refcount refcount;
77
ctiller430c4992014-12-11 09:15:41 -080078 gpr_slice base64_and_huffman;
79
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080080 grpc_mdctx *context;
81
82 struct internal_string *bucket_next;
83} internal_string;
84
85typedef struct internal_metadata {
86 /* must be byte compatible with grpc_mdelem */
87 internal_string *key;
88 internal_string *value;
89
Craig Tiller9fa41b92015-04-10 15:08:03 -070090 gpr_atm refcnt;
91
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080092 /* private only data */
Craig Tiller83901532015-07-10 14:02:45 -070093 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -070094 gpr_atm destroy_user_data;
95 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080097 grpc_mdctx *context;
98 struct internal_metadata *bucket_next;
99} internal_metadata;
100
101struct grpc_mdctx {
102 gpr_uint32 hash_seed;
Craig Tiller9be83ee2015-02-18 14:16:15 -0800103 int refs;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800104
105 gpr_mu mu;
106
107 internal_string **strtab;
108 size_t strtab_count;
109 size_t strtab_capacity;
110
111 internal_metadata **mdtab;
112 size_t mdtab_count;
113 size_t mdtab_free;
114 size_t mdtab_capacity;
115};
116
Craig Tiller1a65a232015-07-06 10:22:32 -0700117static void internal_string_ref(internal_string *s DEBUG_ARGS);
118static void internal_string_unref(internal_string *s DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800119static void discard_metadata(grpc_mdctx *ctx);
120static void gc_mdtab(grpc_mdctx *ctx);
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700121static void metadata_context_destroy_locked(grpc_mdctx *ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122
123static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
124
125static void unlock(grpc_mdctx *ctx) {
126 /* If the context has been orphaned we'd like to delete it soon. We check
127 conditions in unlock as it signals the end of mutations on a context.
128
129 We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
130 first. This is equivalent to saying that both tables have zero counts,
131 which is equivalent to saying that strtab_count is zero (as mdelem's MUST
132 reference an mdstr for their key and value slots).
133
134 To encourage that to happen, we start discarding zero reference count
135 mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
136 case), since otherwise we can be stuck waiting for a garbage collection
137 that will never happen. */
Craig Tiller9be83ee2015-02-18 14:16:15 -0800138 if (ctx->refs == 0) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700139/* uncomment if you're having trouble diagnosing an mdelem leak to make
140 things clearer (slows down destruction a lot, however) */
Craig Tiller45ce9272015-07-31 11:22:35 -0700141#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller0d4836d2015-06-30 15:15:43 -0700142 gc_mdtab(ctx);
Craig Tiller45ce9272015-07-31 11:22:35 -0700143#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800144 if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
145 discard_metadata(ctx);
146 }
147 if (ctx->strtab_count == 0) {
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700148 metadata_context_destroy_locked(ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800149 return;
150 }
151 }
152 gpr_mu_unlock(&ctx->mu);
153}
154
Craig Tiller1a65a232015-07-06 10:22:32 -0700155static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
156#ifdef GRPC_METADATA_REFCOUNT_DEBUG
157 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
158 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
159 gpr_atm_no_barrier_load(&md->refcnt),
160 gpr_atm_no_barrier_load(&md->refcnt) + 1,
161 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
162 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
163#endif
Craig Tillerb8e82672015-10-10 13:52:47 -0700164 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800165 md->context->mdtab_free--;
Craig Tillerb8e82672015-10-10 13:52:47 -0700166 } else {
167 GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800168 }
169}
170
171grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
172 grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
173
Craig Tiller9be83ee2015-02-18 14:16:15 -0800174 ctx->refs = 1;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800175 ctx->hash_seed = seed;
176 gpr_mu_init(&ctx->mu);
177 ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
178 memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
179 ctx->strtab_count = 0;
180 ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
181 ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
182 memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
183 ctx->mdtab_count = 0;
184 ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
185 ctx->mdtab_free = 0;
186
187 return ctx;
188}
189
Craig Tiller32946d32015-01-15 11:37:30 -0800190grpc_mdctx *grpc_mdctx_create(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800191 /* This seed is used to prevent remote connections from controlling hash table
192 * collisions. It needs to be somewhat unpredictable to a remote connection.
193 */
Craig Tiller32ca48c2015-09-10 11:47:15 -0700194 return grpc_mdctx_create_with_seed(
195 (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800196}
197
198static void discard_metadata(grpc_mdctx *ctx) {
199 size_t i;
200 internal_metadata *next, *cur;
201
202 for (i = 0; i < ctx->mdtab_capacity; i++) {
203 cur = ctx->mdtab[i];
204 while (cur) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700205 void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700206 GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800207 next = cur->bucket_next;
Craig Tiller1a65a232015-07-06 10:22:32 -0700208 INTERNAL_STRING_UNREF(cur->key);
209 INTERNAL_STRING_UNREF(cur->value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700210 if (user_data != NULL) {
211 ((destroy_user_data_func)gpr_atm_no_barrier_load(
212 &cur->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800213 }
Craig Tiller83901532015-07-10 14:02:45 -0700214 gpr_mu_destroy(&cur->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800215 gpr_free(cur);
216 cur = next;
217 ctx->mdtab_free--;
218 ctx->mdtab_count--;
219 }
220 ctx->mdtab[i] = NULL;
221 }
222}
223
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700224static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800225 GPR_ASSERT(ctx->strtab_count == 0);
226 GPR_ASSERT(ctx->mdtab_count == 0);
227 GPR_ASSERT(ctx->mdtab_free == 0);
228 gpr_free(ctx->strtab);
229 gpr_free(ctx->mdtab);
230 gpr_mu_unlock(&ctx->mu);
231 gpr_mu_destroy(&ctx->mu);
232 gpr_free(ctx);
233}
234
Craig Tiller9be83ee2015-02-18 14:16:15 -0800235void grpc_mdctx_ref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800236 GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800237 lock(ctx);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800238 GPR_ASSERT(ctx->refs > 0);
239 ctx->refs++;
240 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800241 GPR_TIMER_END("grpc_mdctx_ref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800242}
243
244void grpc_mdctx_unref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800245 GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800246 lock(ctx);
247 GPR_ASSERT(ctx->refs > 0);
248 ctx->refs--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800249 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800250 GPR_TIMER_END("grpc_mdctx_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800251}
252
253static void grow_strtab(grpc_mdctx *ctx) {
254 size_t capacity = ctx->strtab_capacity * 2;
255 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800256 internal_string **strtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800257 internal_string *s, *next;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800258
259 GPR_TIMER_BEGIN("grow_strtab", 0);
260
261 strtab = gpr_malloc(sizeof(internal_string *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800262 memset(strtab, 0, sizeof(internal_string *) * capacity);
263
264 for (i = 0; i < ctx->strtab_capacity; i++) {
265 for (s = ctx->strtab[i]; s; s = next) {
266 next = s->bucket_next;
267 s->bucket_next = strtab[s->hash % capacity];
268 strtab[s->hash % capacity] = s;
269 }
270 }
271
272 gpr_free(ctx->strtab);
273 ctx->strtab = strtab;
274 ctx->strtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800275
276 GPR_TIMER_END("grow_strtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800277}
278
279static void internal_destroy_string(internal_string *is) {
280 internal_string **prev_next;
281 internal_string *cur;
282 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800283 GPR_TIMER_BEGIN("internal_destroy_string", 0);
ctiller430c4992014-12-11 09:15:41 -0800284 if (is->has_base64_and_huffman_encoded) {
285 gpr_slice_unref(is->base64_and_huffman);
286 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800287 for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
288 cur = *prev_next;
289 cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
290 ;
291 *prev_next = cur->bucket_next;
292 ctx->strtab_count--;
293 gpr_free(is);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800294 GPR_TIMER_END("internal_destroy_string", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800295}
296
Craig Tiller1a65a232015-07-06 10:22:32 -0700297static void internal_string_ref(internal_string *s DEBUG_ARGS) {
298#ifdef GRPC_METADATA_REFCOUNT_DEBUG
299 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
300 s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
301#endif
302 ++s->refs;
303}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800304
Craig Tiller1a65a232015-07-06 10:22:32 -0700305static void internal_string_unref(internal_string *s DEBUG_ARGS) {
306#ifdef GRPC_METADATA_REFCOUNT_DEBUG
307 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
308 s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
309#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800310 GPR_ASSERT(s->refs > 0);
311 if (0 == --s->refs) {
312 internal_destroy_string(s);
313 }
314}
315
316static void slice_ref(void *p) {
317 internal_string *is =
318 (internal_string *)((char *)p - offsetof(internal_string, refcount));
319 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800320 GPR_TIMER_BEGIN("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700322 INTERNAL_STRING_REF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800323 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800324 GPR_TIMER_END("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800325}
326
327static void slice_unref(void *p) {
328 internal_string *is =
329 (internal_string *)((char *)p - offsetof(internal_string, refcount));
330 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800331 GPR_TIMER_BEGIN("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800332 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700333 INTERNAL_STRING_UNREF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800334 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800335 GPR_TIMER_END("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800336}
337
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700338grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800339 return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
340}
341
342grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
343 grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
344 GPR_SLICE_LENGTH(slice));
345 gpr_slice_unref(slice);
346 return result;
347}
348
349grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
350 size_t length) {
351 gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
352 internal_string *s;
353
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800354 GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800355 lock(ctx);
356
357 /* search for an existing string */
358 for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
359 if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
360 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700361 INTERNAL_STRING_REF(s);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800362 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800363 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800364 return (grpc_mdstr *)s;
365 }
366 }
367
368 /* not found: create a new string */
369 if (length + 1 < GPR_SLICE_INLINED_SIZE) {
370 /* string data goes directly into the slice */
371 s = gpr_malloc(sizeof(internal_string));
372 s->refs = 1;
373 s->slice.refcount = NULL;
374 memcpy(s->slice.data.inlined.bytes, buf, length);
375 s->slice.data.inlined.bytes[length] = 0;
Craig Tiller6a6b36c2015-09-10 16:00:22 -0700376 s->slice.data.inlined.length = (gpr_uint8)length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800377 } else {
378 /* string data goes after the internal_string header, and we +1 for null
379 terminator */
380 s = gpr_malloc(sizeof(internal_string) + length + 1);
381 s->refs = 1;
382 s->refcount.ref = slice_ref;
383 s->refcount.unref = slice_unref;
384 s->slice.refcount = &s->refcount;
385 s->slice.data.refcounted.bytes = (gpr_uint8 *)(s + 1);
386 s->slice.data.refcounted.length = length;
387 memcpy(s->slice.data.refcounted.bytes, buf, length);
388 /* add a null terminator for cheap c string conversion when desired */
389 s->slice.data.refcounted.bytes[length] = 0;
390 }
ctiller430c4992014-12-11 09:15:41 -0800391 s->has_base64_and_huffman_encoded = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800392 s->hash = hash;
393 s->context = ctx;
394 s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
395 ctx->strtab[hash % ctx->strtab_capacity] = s;
396
397 ctx->strtab_count++;
398
399 if (ctx->strtab_count > ctx->strtab_capacity * 2) {
400 grow_strtab(ctx);
401 }
402
403 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800404 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800405
406 return (grpc_mdstr *)s;
407}
408
409static void gc_mdtab(grpc_mdctx *ctx) {
410 size_t i;
411 internal_metadata **prev_next;
412 internal_metadata *md, *next;
413
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800414 GPR_TIMER_BEGIN("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800415 for (i = 0; i < ctx->mdtab_capacity; i++) {
416 prev_next = &ctx->mdtab[i];
417 for (md = ctx->mdtab[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700418 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800419 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700420 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700421 INTERNAL_STRING_UNREF(md->key);
422 INTERNAL_STRING_UNREF(md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800423 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700424 ((destroy_user_data_func)gpr_atm_no_barrier_load(
425 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800426 }
427 gpr_free(md);
428 *prev_next = next;
429 ctx->mdtab_free--;
430 ctx->mdtab_count--;
431 } else {
432 prev_next = &md->bucket_next;
433 }
434 }
435 }
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800436 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800437}
438
439static void grow_mdtab(grpc_mdctx *ctx) {
440 size_t capacity = ctx->mdtab_capacity * 2;
441 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800442 internal_metadata **mdtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800443 internal_metadata *md, *next;
444 gpr_uint32 hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800445
446 GPR_TIMER_BEGIN("grow_mdtab", 0);
447
448 mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800449 memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
450
451 for (i = 0; i < ctx->mdtab_capacity; i++) {
452 for (md = ctx->mdtab[i]; md; md = next) {
ctillerfb93d192014-12-15 10:40:05 -0800453 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800454 next = md->bucket_next;
455 md->bucket_next = mdtab[hash % capacity];
456 mdtab[hash % capacity] = md;
457 }
458 }
459
460 gpr_free(ctx->mdtab);
461 ctx->mdtab = mdtab;
462 ctx->mdtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800463
464 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800465}
466
467static void rehash_mdtab(grpc_mdctx *ctx) {
468 if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
469 gc_mdtab(ctx);
470 } else {
471 grow_mdtab(ctx);
472 }
473}
474
475grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
476 grpc_mdstr *mkey,
477 grpc_mdstr *mvalue) {
478 internal_string *key = (internal_string *)mkey;
479 internal_string *value = (internal_string *)mvalue;
ctillerfb93d192014-12-15 10:40:05 -0800480 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800481 internal_metadata *md;
482
483 GPR_ASSERT(key->context == ctx);
484 GPR_ASSERT(value->context == ctx);
485
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800486 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
487
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800488 lock(ctx);
489
490 /* search for an existing pair */
491 for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
492 if (md->key == key && md->value == value) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700493 REF_MD_LOCKED(md);
494 INTERNAL_STRING_UNREF(key);
495 INTERNAL_STRING_UNREF(value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800496 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800497 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800498 return (grpc_mdelem *)md;
499 }
500 }
501
502 /* not found: create a new pair */
503 md = gpr_malloc(sizeof(internal_metadata));
Craig Tiller63bda562015-10-09 17:40:19 -0700504 gpr_atm_rel_store(&md->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800505 md->context = ctx;
506 md->key = key;
507 md->value = value;
Craig Tiller8344daa2015-10-09 18:10:57 -0700508 md->user_data = 0;
509 md->destroy_user_data = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800510 md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
Craig Tiller83901532015-07-10 14:02:45 -0700511 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700512#ifdef GRPC_METADATA_REFCOUNT_DEBUG
513 gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
514 gpr_atm_no_barrier_load(&md->refcnt),
515 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
516 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
517#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800518 ctx->mdtab[hash % ctx->mdtab_capacity] = md;
519 ctx->mdtab_count++;
520
521 if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
522 rehash_mdtab(ctx);
523 }
524
525 unlock(ctx);
526
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800527 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
528
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800529 return (grpc_mdelem *)md;
530}
531
532grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
533 const char *value) {
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700534 return grpc_mdelem_from_metadata_strings(ctx,
535 grpc_mdstr_from_string(ctx, key),
536 grpc_mdstr_from_string(ctx, value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800537}
538
539grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
540 gpr_slice value) {
541 return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
542 grpc_mdstr_from_slice(ctx, value));
543}
544
545grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
546 const char *key,
547 const gpr_uint8 *value,
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700548 size_t value_length) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800549 return grpc_mdelem_from_metadata_strings(
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700550 ctx, grpc_mdstr_from_string(ctx, key),
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800551 grpc_mdstr_from_buffer(ctx, value, value_length));
552}
553
Craig Tiller1a65a232015-07-06 10:22:32 -0700554grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800555 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller1a65a232015-07-06 10:22:32 -0700556#ifdef GRPC_METADATA_REFCOUNT_DEBUG
557 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
558 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
559 gpr_atm_no_barrier_load(&md->refcnt),
560 gpr_atm_no_barrier_load(&md->refcnt) + 1,
561 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
562 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
563#endif
Craig Tiller9fa41b92015-04-10 15:08:03 -0700564 /* we can assume the ref count is >= 1 as the application is calling
565 this function - meaning that no adjustment to mdtab_free is necessary,
566 simplifying the logic here to be just an atomic increment */
567 /* use C assert to have this removed in opt builds */
Craig Tillerb8e82672015-10-10 13:52:47 -0700568 assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700569 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800570 return gmd;
571}
572
Craig Tiller1a65a232015-07-06 10:22:32 -0700573void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800574 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800575 if (!md) return;
Craig Tiller1a65a232015-07-06 10:22:32 -0700576#ifdef GRPC_METADATA_REFCOUNT_DEBUG
577 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
578 "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
579 gpr_atm_no_barrier_load(&md->refcnt),
580 gpr_atm_no_barrier_load(&md->refcnt) - 1,
581 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
582 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
583#endif
Craig Tiller63bda562015-10-09 17:40:19 -0700584 if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
585 grpc_mdctx *ctx = md->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800586 GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
Craig Tiller63bda562015-10-09 17:40:19 -0700587 lock(ctx);
Craig Tillerb8e82672015-10-10 13:52:47 -0700588 if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
589 ctx->mdtab_free++;
590 gpr_atm_no_barrier_store(&md->refcnt, 0);
591 }
Craig Tiller63bda562015-10-09 17:40:19 -0700592 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800593 GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800594 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800595}
596
597const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
598 return (const char *)GPR_SLICE_START_PTR(s->slice);
599}
600
Craig Tiller1a65a232015-07-06 10:22:32 -0700601grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800602 internal_string *s = (internal_string *)gs;
603 grpc_mdctx *ctx = s->context;
604 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700605 internal_string_ref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800606 unlock(ctx);
607 return gs;
608}
609
Craig Tiller1a65a232015-07-06 10:22:32 -0700610void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800611 internal_string *s = (internal_string *)gs;
612 grpc_mdctx *ctx = s->context;
613 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700614 internal_string_unref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800615 unlock(ctx);
616}
617
618size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
619 return ctx->mdtab_capacity;
620}
621
622size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
623 return ctx->mdtab_count;
624}
625
626size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
627 return ctx->mdtab_free;
628}
629
Craig Tiller8344daa2015-10-09 18:10:57 -0700630void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800631 internal_metadata *im = (internal_metadata *)md;
Craig Tiller83901532015-07-10 14:02:45 -0700632 void *result;
Craig Tiller8344daa2015-10-09 18:10:57 -0700633 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
634 return (void *)gpr_atm_no_barrier_load(&im->user_data);
635 } else {
636 return NULL;
637 }
Craig Tiller83901532015-07-10 14:02:45 -0700638 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800639}
640
641void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
642 void *user_data) {
643 internal_metadata *im = (internal_metadata *)md;
644 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
Craig Tiller83901532015-07-10 14:02:45 -0700645 gpr_mu_lock(&im->mu_user_data);
Craig Tiller8344daa2015-10-09 18:10:57 -0700646 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
Craig Tiller83901532015-07-10 14:02:45 -0700647 /* user data can only be set once */
648 gpr_mu_unlock(&im->mu_user_data);
649 if (destroy_func != NULL) {
650 destroy_func(user_data);
651 }
652 return;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800653 }
Craig Tiller8344daa2015-10-09 18:10:57 -0700654 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
655 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
Craig Tiller83901532015-07-10 14:02:45 -0700656 gpr_mu_unlock(&im->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800657}
ctiller430c4992014-12-11 09:15:41 -0800658
659gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
660 internal_string *s = (internal_string *)gs;
661 gpr_slice slice;
662 grpc_mdctx *ctx = s->context;
663 lock(ctx);
664 if (!s->has_base64_and_huffman_encoded) {
665 s->base64_and_huffman =
666 grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
ctiller33023c42014-12-12 16:28:33 -0800667 s->has_base64_and_huffman_encoded = 1;
ctiller430c4992014-12-11 09:15:41 -0800668 }
669 slice = s->base64_and_huffman;
670 unlock(ctx);
671 return slice;
Craig Tiller190d3602015-02-18 09:23:38 -0800672}
Craig Tillerfe0104a2015-04-14 09:19:12 -0700673
Craig Tiller49772e02015-08-21 08:08:37 -0700674static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) {
Craig Tillerb96d0012015-05-06 15:33:23 -0700675 const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice);
676 const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice);
677 for (; p != e; p++) {
Craig Tiller49772e02015-08-21 08:08:37 -0700678 int idx = *p;
679 int byte = idx / 8;
680 int bit = idx % 8;
681 if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
Craig Tillerb96d0012015-05-06 15:33:23 -0700682 }
683 return 1;
684}
685
Craig Tiller49772e02015-08-21 08:08:37 -0700686int grpc_mdstr_is_legal_header(grpc_mdstr *s) {
687 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller80aa2802015-08-21 08:50:51 -0700688 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00,
689 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Craig Tiller49772e02015-08-21 08:08:37 -0700690 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Craig Tiller49772e02015-08-21 08:08:37 -0700691 return conforms_to(s, legal_header_bits);
692}
693
694int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
695 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller240b7db2015-08-27 15:35:32 -0700696 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
Craig Tiller49772e02015-08-21 08:08:37 -0700697 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
698 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
699 return conforms_to(s, legal_header_bits);
700}
701
Craig Tillerb96d0012015-05-06 15:33:23 -0700702int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
703 /* TODO(ctiller): consider caching this */
704 return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
705 GPR_SLICE_LENGTH(s->slice));
706}