blob: cbec63c8686af8c90b1d0b928e7f614d4baeeff1 [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 Tillerb774be42015-11-19 07:56:13 -080067#define INTERNAL_STRING_REF(s) \
68 if (is_mdstr_static((grpc_mdstr *)(s))) \
69 ; \
70 else \
71 internal_string_ref((s), __FILE__, __LINE__)
72#define INTERNAL_STRING_UNREF(s) \
73 if (is_mdstr_static((grpc_mdstr *)(s))) \
74 ; \
75 else \
76 internal_string_unref((s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070077#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
78#else
79#define DEBUG_ARGS
80#define FWD_DEBUG_ARGS
Craig Tillerb774be42015-11-19 07:56:13 -080081#define INTERNAL_STRING_REF(s) \
82 if (is_mdstr_static((grpc_mdstr *)(s))) \
83 ; \
84 else \
85 internal_string_ref((s))
86#define INTERNAL_STRING_UNREF(s) \
87 if (is_mdstr_static((grpc_mdstr *)(s))) \
88 ; \
89 else \
90 internal_string_unref((s))
Craig Tiller1a65a232015-07-06 10:22:32 -070091#define REF_MD_LOCKED(s) ref_md_locked((s))
92#endif
93
Craig Tiller8344daa2015-10-09 18:10:57 -070094typedef void (*destroy_user_data_func)(void *user_data);
95
Craig Tiller70b080d2015-11-19 08:04:48 -080096/* Shadow structure for grpc_mdstr for non-static values */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080097typedef struct internal_string {
98 /* must be byte compatible with grpc_mdstr */
99 gpr_slice slice;
100 gpr_uint32 hash;
101
102 /* private only data */
103 gpr_uint32 refs;
ctiller430c4992014-12-11 09:15:41 -0800104 gpr_uint8 has_base64_and_huffman_encoded;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800105 gpr_slice_refcount refcount;
106
ctiller430c4992014-12-11 09:15:41 -0800107 gpr_slice base64_and_huffman;
108
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800109 grpc_mdctx *context;
110
111 struct internal_string *bucket_next;
112} internal_string;
113
Craig Tiller70b080d2015-11-19 08:04:48 -0800114/* Shadow structure for grpc_mdelem for non-static elements */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800115typedef struct internal_metadata {
116 /* must be byte compatible with grpc_mdelem */
117 internal_string *key;
118 internal_string *value;
119
Craig Tiller9fa41b92015-04-10 15:08:03 -0700120 gpr_atm refcnt;
121
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122 /* private only data */
Craig Tiller83901532015-07-10 14:02:45 -0700123 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -0700124 gpr_atm destroy_user_data;
125 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800126
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800127 grpc_mdctx *context;
128 struct internal_metadata *bucket_next;
129} internal_metadata;
130
Craig Tiller0e72ede2015-11-19 07:48:53 -0800131typedef struct static_string {
132 grpc_mdstr *mdstr;
133 gpr_uint32 hash;
134} static_string;
135
136typedef struct static_mdelem {
137 grpc_mdelem *mdelem;
138 gpr_uint32 hash;
139} static_mdelem;
140
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800141struct grpc_mdctx {
142 gpr_uint32 hash_seed;
Craig Tiller9be83ee2015-02-18 14:16:15 -0800143 int refs;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800144
145 gpr_mu mu;
146
Craig Tiller70b080d2015-11-19 08:04:48 -0800147 /* linearly probed hash tables for static element lookup */
Craig Tiller0e72ede2015-11-19 07:48:53 -0800148 static_string static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
149 static_mdelem static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
150 size_t static_strtab_maxprobe;
151 size_t static_mdtab_maxprobe;
152
Craig Tiller70b080d2015-11-19 08:04:48 -0800153 /* chained hash table of dynamically allocated strings */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800154 internal_string **strtab;
155 size_t strtab_count;
156 size_t strtab_capacity;
157
Craig Tiller70b080d2015-11-19 08:04:48 -0800158 /* chained hash table of dynamically allocated mdelems */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800159 internal_metadata **mdtab;
160 size_t mdtab_count;
161 size_t mdtab_free;
162 size_t mdtab_capacity;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800163
164 /* cache slots */
165 gpr_atm cache_slots[GRPC_MDELEM_CACHE_SLOT_COUNT];
166 /* compression algorithm mdelems: one per algorithm bitmask */
167 gpr_atm compression_algorithm_mdelem[1 << GRPC_COMPRESS_ALGORITHMS_COUNT];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800168};
169
Craig Tiller1a65a232015-07-06 10:22:32 -0700170static void internal_string_ref(internal_string *s DEBUG_ARGS);
171static void internal_string_unref(internal_string *s DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800172static void discard_metadata(grpc_mdctx *ctx);
173static void gc_mdtab(grpc_mdctx *ctx);
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700174static void metadata_context_destroy_locked(grpc_mdctx *ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800175
Craig Tiller0e72ede2015-11-19 07:48:53 -0800176void grpc_mdctx_global_init(void) {
177 size_t i;
178 for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
179 grpc_mdstr *elem = &grpc_static_mdstr_table[i];
180 const char *str = grpc_static_metadata_strings[i];
Craig Tillerb774be42015-11-19 07:56:13 -0800181 *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str);
182 *(gpr_uint32 *)&elem->hash = gpr_murmur_hash3(str, strlen(str), 0);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800183 }
184 for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
185 grpc_mdelem *elem = &grpc_static_mdelem_table[i];
Craig Tillerebdef9d2015-11-19 17:09:49 -0800186 grpc_mdstr *key =
187 &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]];
188 grpc_mdstr *value =
189 &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]];
Craig Tillerb774be42015-11-19 07:56:13 -0800190 *(grpc_mdstr **)&elem->key = key;
191 *(grpc_mdstr **)&elem->value = value;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800192 }
193}
194
Craig Tillerb774be42015-11-19 07:56:13 -0800195void grpc_mdctx_global_shutdown(void) {}
Craig Tiller0e72ede2015-11-19 07:48:53 -0800196
197static int is_mdstr_static(grpc_mdstr *s) {
Craig Tillerb774be42015-11-19 07:56:13 -0800198 return s >= &grpc_static_mdstr_table[0] &&
199 s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800200}
201
202static int is_mdelem_static(grpc_mdelem *e) {
Craig Tillerb774be42015-11-19 07:56:13 -0800203 return e >= &grpc_static_mdelem_table[0] &&
204 e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800205}
206
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800207static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
208
209static void unlock(grpc_mdctx *ctx) {
210 /* If the context has been orphaned we'd like to delete it soon. We check
211 conditions in unlock as it signals the end of mutations on a context.
212
213 We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
214 first. This is equivalent to saying that both tables have zero counts,
215 which is equivalent to saying that strtab_count is zero (as mdelem's MUST
216 reference an mdstr for their key and value slots).
217
218 To encourage that to happen, we start discarding zero reference count
219 mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
220 case), since otherwise we can be stuck waiting for a garbage collection
221 that will never happen. */
Craig Tiller9be83ee2015-02-18 14:16:15 -0800222 if (ctx->refs == 0) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700223/* uncomment if you're having trouble diagnosing an mdelem leak to make
224 things clearer (slows down destruction a lot, however) */
Craig Tiller45ce9272015-07-31 11:22:35 -0700225#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller0d4836d2015-06-30 15:15:43 -0700226 gc_mdtab(ctx);
Craig Tiller45ce9272015-07-31 11:22:35 -0700227#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800228 if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
229 discard_metadata(ctx);
230 }
231 if (ctx->strtab_count == 0) {
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700232 metadata_context_destroy_locked(ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800233 return;
234 }
235 }
236 gpr_mu_unlock(&ctx->mu);
237}
238
Craig Tiller1a65a232015-07-06 10:22:32 -0700239static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
240#ifdef GRPC_METADATA_REFCOUNT_DEBUG
241 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
242 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
243 gpr_atm_no_barrier_load(&md->refcnt),
244 gpr_atm_no_barrier_load(&md->refcnt) + 1,
245 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
246 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
247#endif
Craig Tillerb8e82672015-10-10 13:52:47 -0700248 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800249 md->context->mdtab_free--;
Craig Tillerb8e82672015-10-10 13:52:47 -0700250 } else {
251 GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800252 }
253}
254
255grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
256 grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
Craig Tiller0e72ede2015-11-19 07:48:53 -0800257 size_t i, j;
258
259 memset(ctx, 0, sizeof(*ctx));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800260
Craig Tiller9be83ee2015-02-18 14:16:15 -0800261 ctx->refs = 1;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800262 ctx->hash_seed = seed;
263 gpr_mu_init(&ctx->mu);
264 ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
265 memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
266 ctx->strtab_count = 0;
267 ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
268 ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
269 memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
270 ctx->mdtab_count = 0;
271 ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
272 ctx->mdtab_free = 0;
273
Craig Tiller0e72ede2015-11-19 07:48:53 -0800274 for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
275 const char *str = grpc_static_metadata_strings[i];
276 gpr_uint32 lup_hash = gpr_murmur_hash3(str, strlen(str), seed);
277 for (j = 0;; j++) {
278 size_t idx = (lup_hash + j) % GPR_ARRAY_SIZE(ctx->static_strtab);
279 if (ctx->static_strtab[idx].mdstr == NULL) {
280 ctx->static_strtab[idx].mdstr = &grpc_static_mdstr_table[i];
281 ctx->static_strtab[idx].hash = lup_hash;
282 break;
283 }
284 }
285 if (j > ctx->static_strtab_maxprobe) {
286 ctx->static_strtab_maxprobe = j;
287 }
288 }
289
290 for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
291 grpc_mdelem *elem = &grpc_static_mdelem_table[i];
292 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(elem->key->hash, elem->value->hash);
293 for (j = 0;; j++) {
294 size_t idx = (hash + j) % GPR_ARRAY_SIZE(ctx->static_mdtab);
295 if (ctx->static_mdtab[idx].mdelem == NULL) {
296 ctx->static_mdtab[idx].mdelem = elem;
297 ctx->static_mdtab[idx].hash = hash;
298 break;
299 }
300 }
301 if (j > ctx->static_mdtab_maxprobe) {
302 ctx->static_mdtab_maxprobe = j;
303 }
304 }
305
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800306 return ctx;
307}
308
Craig Tiller32946d32015-01-15 11:37:30 -0800309grpc_mdctx *grpc_mdctx_create(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800310 /* This seed is used to prevent remote connections from controlling hash table
311 * collisions. It needs to be somewhat unpredictable to a remote connection.
312 */
Craig Tiller32ca48c2015-09-10 11:47:15 -0700313 return grpc_mdctx_create_with_seed(
314 (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800315}
316
Craig Tillerebdef9d2015-11-19 17:09:49 -0800317static void drop_cached_elem(gpr_atm *slot) {
318 gpr_atm value = gpr_atm_no_barrier_load(slot);
319 gpr_atm_rel_store(slot, 0);
320 GRPC_MDELEM_UNREF((grpc_mdelem *)value);
321}
322
323void grpc_mdctx_drop_caches(grpc_mdctx *ctx) {
324 size_t i;
325 for (i = 0; i < GRPC_MDELEM_CACHE_SLOT_COUNT; i++) {
326 drop_cached_elem(&ctx->cache_slots[i]);
327 }
328 for (i = 0; i < GPR_ARRAY_SIZE(ctx->compression_algorithm_mdelem); i++) {
329 drop_cached_elem(&ctx->compression_algorithm_mdelem[i]);
330 }
331}
332
333static void set_cache(gpr_atm *slot, grpc_mdelem *elem) {
334 if (!gpr_atm_rel_cas(slot, 0, (gpr_atm)elem)) {
335 GRPC_MDELEM_UNREF(elem);
336 }
337}
338
339void grpc_mdctx_set_mdelem_cache(grpc_mdctx *ctx, grpc_mdelem_cache_slot slot,
340 grpc_mdelem *elem) {
341 set_cache(&ctx->cache_slots[slot], elem);
342}
343
344static grpc_mdelem *get_cache(gpr_atm *slot) {
345 return (grpc_mdelem *)gpr_atm_acq_load(slot);
346}
347
348grpc_mdelem *grpc_mdelem_from_cache(grpc_mdctx *ctx,
349 grpc_mdelem_cache_slot slot) {
350 return get_cache(&ctx->cache_slots[slot]);
351}
352
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800353static void discard_metadata(grpc_mdctx *ctx) {
354 size_t i;
355 internal_metadata *next, *cur;
356
357 for (i = 0; i < ctx->mdtab_capacity; i++) {
358 cur = ctx->mdtab[i];
359 while (cur) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700360 void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700361 GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800362 next = cur->bucket_next;
Craig Tiller1a65a232015-07-06 10:22:32 -0700363 INTERNAL_STRING_UNREF(cur->key);
364 INTERNAL_STRING_UNREF(cur->value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700365 if (user_data != NULL) {
366 ((destroy_user_data_func)gpr_atm_no_barrier_load(
367 &cur->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800368 }
Craig Tiller83901532015-07-10 14:02:45 -0700369 gpr_mu_destroy(&cur->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800370 gpr_free(cur);
371 cur = next;
372 ctx->mdtab_free--;
373 ctx->mdtab_count--;
374 }
375 ctx->mdtab[i] = NULL;
376 }
377}
378
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700379static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800380 GPR_ASSERT(ctx->strtab_count == 0);
381 GPR_ASSERT(ctx->mdtab_count == 0);
382 GPR_ASSERT(ctx->mdtab_free == 0);
383 gpr_free(ctx->strtab);
384 gpr_free(ctx->mdtab);
385 gpr_mu_unlock(&ctx->mu);
386 gpr_mu_destroy(&ctx->mu);
387 gpr_free(ctx);
388}
389
Craig Tiller9be83ee2015-02-18 14:16:15 -0800390void grpc_mdctx_ref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800391 GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800392 lock(ctx);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800393 GPR_ASSERT(ctx->refs > 0);
394 ctx->refs++;
395 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800396 GPR_TIMER_END("grpc_mdctx_ref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800397}
398
399void grpc_mdctx_unref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800400 GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800401 lock(ctx);
402 GPR_ASSERT(ctx->refs > 0);
403 ctx->refs--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800404 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800405 GPR_TIMER_END("grpc_mdctx_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800406}
407
408static void grow_strtab(grpc_mdctx *ctx) {
409 size_t capacity = ctx->strtab_capacity * 2;
410 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800411 internal_string **strtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800412 internal_string *s, *next;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800413
414 GPR_TIMER_BEGIN("grow_strtab", 0);
415
416 strtab = gpr_malloc(sizeof(internal_string *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800417 memset(strtab, 0, sizeof(internal_string *) * capacity);
418
419 for (i = 0; i < ctx->strtab_capacity; i++) {
420 for (s = ctx->strtab[i]; s; s = next) {
421 next = s->bucket_next;
422 s->bucket_next = strtab[s->hash % capacity];
423 strtab[s->hash % capacity] = s;
424 }
425 }
426
427 gpr_free(ctx->strtab);
428 ctx->strtab = strtab;
429 ctx->strtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800430
431 GPR_TIMER_END("grow_strtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800432}
433
434static void internal_destroy_string(internal_string *is) {
435 internal_string **prev_next;
436 internal_string *cur;
437 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800438 GPR_TIMER_BEGIN("internal_destroy_string", 0);
ctiller430c4992014-12-11 09:15:41 -0800439 if (is->has_base64_and_huffman_encoded) {
440 gpr_slice_unref(is->base64_and_huffman);
441 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800442 for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
443 cur = *prev_next;
444 cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
445 ;
446 *prev_next = cur->bucket_next;
447 ctx->strtab_count--;
448 gpr_free(is);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800449 GPR_TIMER_END("internal_destroy_string", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800450}
451
Craig Tiller1a65a232015-07-06 10:22:32 -0700452static void internal_string_ref(internal_string *s DEBUG_ARGS) {
453#ifdef GRPC_METADATA_REFCOUNT_DEBUG
454 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
455 s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
456#endif
457 ++s->refs;
458}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800459
Craig Tiller1a65a232015-07-06 10:22:32 -0700460static void internal_string_unref(internal_string *s DEBUG_ARGS) {
461#ifdef GRPC_METADATA_REFCOUNT_DEBUG
462 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
463 s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
464#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800465 GPR_ASSERT(s->refs > 0);
466 if (0 == --s->refs) {
467 internal_destroy_string(s);
468 }
469}
470
471static void slice_ref(void *p) {
472 internal_string *is =
473 (internal_string *)((char *)p - offsetof(internal_string, refcount));
474 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800475 GPR_TIMER_BEGIN("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800476 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700477 INTERNAL_STRING_REF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800478 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800479 GPR_TIMER_END("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800480}
481
482static void slice_unref(void *p) {
483 internal_string *is =
484 (internal_string *)((char *)p - offsetof(internal_string, refcount));
485 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800486 GPR_TIMER_BEGIN("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800487 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700488 INTERNAL_STRING_UNREF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800489 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800490 GPR_TIMER_END("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800491}
492
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700493grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800494 return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
495}
496
497grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
498 grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
499 GPR_SLICE_LENGTH(slice));
500 gpr_slice_unref(slice);
501 return result;
502}
503
504grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
505 size_t length) {
506 gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
507 internal_string *s;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800508 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800509
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800510 GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800511
512 /* search for a static string */
513 for (i = 0; i <= ctx->static_strtab_maxprobe; i++) {
514 size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_strtab);
515 static_string *ss = &ctx->static_strtab[idx];
516 if (ss->hash == hash && GPR_SLICE_LENGTH(ss->mdstr->slice) == length &&
517 0 == memcmp(buf, GPR_SLICE_START_PTR(ss->mdstr->slice), length)) {
518 return ss->mdstr;
519 }
520 }
521
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800522 lock(ctx);
523
524 /* search for an existing string */
525 for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
526 if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
527 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700528 INTERNAL_STRING_REF(s);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800529 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800530 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800531 return (grpc_mdstr *)s;
532 }
533 }
534
535 /* not found: create a new string */
536 if (length + 1 < GPR_SLICE_INLINED_SIZE) {
537 /* string data goes directly into the slice */
538 s = gpr_malloc(sizeof(internal_string));
539 s->refs = 1;
540 s->slice.refcount = NULL;
541 memcpy(s->slice.data.inlined.bytes, buf, length);
542 s->slice.data.inlined.bytes[length] = 0;
Craig Tiller6a6b36c2015-09-10 16:00:22 -0700543 s->slice.data.inlined.length = (gpr_uint8)length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800544 } else {
545 /* string data goes after the internal_string header, and we +1 for null
546 terminator */
547 s = gpr_malloc(sizeof(internal_string) + length + 1);
548 s->refs = 1;
549 s->refcount.ref = slice_ref;
550 s->refcount.unref = slice_unref;
551 s->slice.refcount = &s->refcount;
552 s->slice.data.refcounted.bytes = (gpr_uint8 *)(s + 1);
553 s->slice.data.refcounted.length = length;
554 memcpy(s->slice.data.refcounted.bytes, buf, length);
555 /* add a null terminator for cheap c string conversion when desired */
556 s->slice.data.refcounted.bytes[length] = 0;
557 }
ctiller430c4992014-12-11 09:15:41 -0800558 s->has_base64_and_huffman_encoded = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800559 s->hash = hash;
560 s->context = ctx;
561 s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
562 ctx->strtab[hash % ctx->strtab_capacity] = s;
563
564 ctx->strtab_count++;
565
566 if (ctx->strtab_count > ctx->strtab_capacity * 2) {
567 grow_strtab(ctx);
568 }
569
570 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800571 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800572
573 return (grpc_mdstr *)s;
574}
575
576static void gc_mdtab(grpc_mdctx *ctx) {
577 size_t i;
578 internal_metadata **prev_next;
579 internal_metadata *md, *next;
580
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800581 GPR_TIMER_BEGIN("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800582 for (i = 0; i < ctx->mdtab_capacity; i++) {
583 prev_next = &ctx->mdtab[i];
584 for (md = ctx->mdtab[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700585 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800586 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700587 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700588 INTERNAL_STRING_UNREF(md->key);
589 INTERNAL_STRING_UNREF(md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800590 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700591 ((destroy_user_data_func)gpr_atm_no_barrier_load(
592 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800593 }
594 gpr_free(md);
595 *prev_next = next;
596 ctx->mdtab_free--;
597 ctx->mdtab_count--;
598 } else {
599 prev_next = &md->bucket_next;
600 }
601 }
602 }
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800603 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800604}
605
606static void grow_mdtab(grpc_mdctx *ctx) {
607 size_t capacity = ctx->mdtab_capacity * 2;
608 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800609 internal_metadata **mdtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800610 internal_metadata *md, *next;
611 gpr_uint32 hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800612
613 GPR_TIMER_BEGIN("grow_mdtab", 0);
614
615 mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800616 memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
617
618 for (i = 0; i < ctx->mdtab_capacity; i++) {
619 for (md = ctx->mdtab[i]; md; md = next) {
ctillerfb93d192014-12-15 10:40:05 -0800620 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800621 next = md->bucket_next;
622 md->bucket_next = mdtab[hash % capacity];
623 mdtab[hash % capacity] = md;
624 }
625 }
626
627 gpr_free(ctx->mdtab);
628 ctx->mdtab = mdtab;
629 ctx->mdtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800630
631 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800632}
633
634static void rehash_mdtab(grpc_mdctx *ctx) {
635 if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
636 gc_mdtab(ctx);
637 } else {
638 grow_mdtab(ctx);
639 }
640}
641
642grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
643 grpc_mdstr *mkey,
644 grpc_mdstr *mvalue) {
645 internal_string *key = (internal_string *)mkey;
646 internal_string *value = (internal_string *)mvalue;
ctillerfb93d192014-12-15 10:40:05 -0800647 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800648 internal_metadata *md;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800649 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800650
Craig Tiller0e72ede2015-11-19 07:48:53 -0800651 GPR_ASSERT(is_mdstr_static(mkey) || key->context == ctx);
652 GPR_ASSERT(is_mdstr_static(mvalue) || value->context == ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800653
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800654 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
655
Craig Tiller0e72ede2015-11-19 07:48:53 -0800656 if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
657 for (i = 0; i <= ctx->static_mdtab_maxprobe; i++) {
658 size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_mdtab);
659 static_mdelem *smd = &ctx->static_mdtab[idx];
Craig Tillerb774be42015-11-19 07:56:13 -0800660 if (smd->hash == hash && smd->mdelem->key == mkey &&
661 smd->mdelem->value == mvalue) {
Craig Tiller0e72ede2015-11-19 07:48:53 -0800662 return smd->mdelem;
663 }
664 }
665 }
666
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800667 lock(ctx);
668
669 /* search for an existing pair */
670 for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
671 if (md->key == key && md->value == value) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700672 REF_MD_LOCKED(md);
673 INTERNAL_STRING_UNREF(key);
674 INTERNAL_STRING_UNREF(value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800675 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800676 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800677 return (grpc_mdelem *)md;
678 }
679 }
680
681 /* not found: create a new pair */
682 md = gpr_malloc(sizeof(internal_metadata));
Craig Tiller63bda562015-10-09 17:40:19 -0700683 gpr_atm_rel_store(&md->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800684 md->context = ctx;
685 md->key = key;
686 md->value = value;
Craig Tiller8344daa2015-10-09 18:10:57 -0700687 md->user_data = 0;
688 md->destroy_user_data = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800689 md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
Craig Tiller83901532015-07-10 14:02:45 -0700690 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700691#ifdef GRPC_METADATA_REFCOUNT_DEBUG
692 gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
693 gpr_atm_no_barrier_load(&md->refcnt),
694 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
695 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
696#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800697 ctx->mdtab[hash % ctx->mdtab_capacity] = md;
698 ctx->mdtab_count++;
699
700 if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
701 rehash_mdtab(ctx);
702 }
703
704 unlock(ctx);
705
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800706 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
707
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800708 return (grpc_mdelem *)md;
709}
710
711grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
712 const char *value) {
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700713 return grpc_mdelem_from_metadata_strings(ctx,
714 grpc_mdstr_from_string(ctx, key),
715 grpc_mdstr_from_string(ctx, value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800716}
717
718grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
719 gpr_slice value) {
720 return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
721 grpc_mdstr_from_slice(ctx, value));
722}
723
724grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
725 const char *key,
726 const gpr_uint8 *value,
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700727 size_t value_length) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800728 return grpc_mdelem_from_metadata_strings(
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700729 ctx, grpc_mdstr_from_string(ctx, key),
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800730 grpc_mdstr_from_buffer(ctx, value, value_length));
731}
732
Craig Tiller1a65a232015-07-06 10:22:32 -0700733grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800734 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800735 if (is_mdelem_static(gmd)) return gmd;
Craig Tiller1a65a232015-07-06 10:22:32 -0700736#ifdef GRPC_METADATA_REFCOUNT_DEBUG
737 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
738 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
739 gpr_atm_no_barrier_load(&md->refcnt),
740 gpr_atm_no_barrier_load(&md->refcnt) + 1,
741 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
742 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
743#endif
Craig Tiller9fa41b92015-04-10 15:08:03 -0700744 /* we can assume the ref count is >= 1 as the application is calling
745 this function - meaning that no adjustment to mdtab_free is necessary,
746 simplifying the logic here to be just an atomic increment */
747 /* use C assert to have this removed in opt builds */
Craig Tillerb8e82672015-10-10 13:52:47 -0700748 assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700749 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800750 return gmd;
751}
752
Craig Tiller1a65a232015-07-06 10:22:32 -0700753void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800754 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800755 if (!md) return;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800756 if (is_mdelem_static(gmd)) return;
Craig Tiller1a65a232015-07-06 10:22:32 -0700757#ifdef GRPC_METADATA_REFCOUNT_DEBUG
758 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
759 "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
760 gpr_atm_no_barrier_load(&md->refcnt),
761 gpr_atm_no_barrier_load(&md->refcnt) - 1,
762 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
763 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
764#endif
Craig Tiller63bda562015-10-09 17:40:19 -0700765 if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
766 grpc_mdctx *ctx = md->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800767 GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
Craig Tiller63bda562015-10-09 17:40:19 -0700768 lock(ctx);
Craig Tillerb8e82672015-10-10 13:52:47 -0700769 if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
770 ctx->mdtab_free++;
771 gpr_atm_no_barrier_store(&md->refcnt, 0);
772 }
Craig Tiller63bda562015-10-09 17:40:19 -0700773 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800774 GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800775 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800776}
777
778const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
779 return (const char *)GPR_SLICE_START_PTR(s->slice);
780}
781
Craig Tiller1a65a232015-07-06 10:22:32 -0700782grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800783 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800784 grpc_mdctx *ctx;
785 if (is_mdstr_static(gs)) return gs;
786 ctx = s->context;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800787 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700788 internal_string_ref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800789 unlock(ctx);
790 return gs;
791}
792
Craig Tiller1a65a232015-07-06 10:22:32 -0700793void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800794 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800795 grpc_mdctx *ctx;
796 if (is_mdstr_static(gs)) return;
797 ctx = s->context;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800798 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700799 internal_string_unref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800800 unlock(ctx);
801}
802
803size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
804 return ctx->mdtab_capacity;
805}
806
807size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
808 return ctx->mdtab_count;
809}
810
811size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
812 return ctx->mdtab_free;
813}
814
Craig Tiller8344daa2015-10-09 18:10:57 -0700815void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800816 internal_metadata *im = (internal_metadata *)md;
Craig Tiller83901532015-07-10 14:02:45 -0700817 void *result;
Craig Tiller8344daa2015-10-09 18:10:57 -0700818 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
819 return (void *)gpr_atm_no_barrier_load(&im->user_data);
820 } else {
821 return NULL;
822 }
Craig Tiller83901532015-07-10 14:02:45 -0700823 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800824}
825
826void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
827 void *user_data) {
828 internal_metadata *im = (internal_metadata *)md;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800829 GPR_ASSERT(!is_mdelem_static(md));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800830 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
Craig Tiller83901532015-07-10 14:02:45 -0700831 gpr_mu_lock(&im->mu_user_data);
Craig Tiller8344daa2015-10-09 18:10:57 -0700832 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
Craig Tiller83901532015-07-10 14:02:45 -0700833 /* user data can only be set once */
834 gpr_mu_unlock(&im->mu_user_data);
835 if (destroy_func != NULL) {
836 destroy_func(user_data);
837 }
838 return;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800839 }
Craig Tiller8344daa2015-10-09 18:10:57 -0700840 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
841 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
Craig Tiller83901532015-07-10 14:02:45 -0700842 gpr_mu_unlock(&im->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800843}
ctiller430c4992014-12-11 09:15:41 -0800844
845gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
846 internal_string *s = (internal_string *)gs;
847 gpr_slice slice;
848 grpc_mdctx *ctx = s->context;
849 lock(ctx);
850 if (!s->has_base64_and_huffman_encoded) {
851 s->base64_and_huffman =
852 grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
ctiller33023c42014-12-12 16:28:33 -0800853 s->has_base64_and_huffman_encoded = 1;
ctiller430c4992014-12-11 09:15:41 -0800854 }
855 slice = s->base64_and_huffman;
856 unlock(ctx);
857 return slice;
Craig Tiller190d3602015-02-18 09:23:38 -0800858}
Craig Tillerfe0104a2015-04-14 09:19:12 -0700859
Craig Tiller49772e02015-08-21 08:08:37 -0700860static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) {
Craig Tillerb96d0012015-05-06 15:33:23 -0700861 const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice);
862 const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice);
863 for (; p != e; p++) {
Craig Tiller49772e02015-08-21 08:08:37 -0700864 int idx = *p;
865 int byte = idx / 8;
866 int bit = idx % 8;
867 if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
Craig Tillerb96d0012015-05-06 15:33:23 -0700868 }
869 return 1;
870}
871
Craig Tiller49772e02015-08-21 08:08:37 -0700872int grpc_mdstr_is_legal_header(grpc_mdstr *s) {
873 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller80aa2802015-08-21 08:50:51 -0700874 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00,
875 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Craig Tiller49772e02015-08-21 08:08:37 -0700876 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Craig Tiller49772e02015-08-21 08:08:37 -0700877 return conforms_to(s, legal_header_bits);
878}
879
880int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
881 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller240b7db2015-08-27 15:35:32 -0700882 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
Craig Tiller49772e02015-08-21 08:08:37 -0700883 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
884 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
885 return conforms_to(s, legal_header_bits);
886}
887
Craig Tillerebdef9d2015-11-19 17:09:49 -0800888static grpc_mdelem *make_accept_encoding_mdelem_for_compression_algorithms(
889 grpc_mdctx *mdctx, gpr_uint32 algorithms) {
890 gpr_strvec sv;
891 int i;
892 char *str;
893 grpc_mdelem *out;
894
895 gpr_strvec_init(&sv);
896 for (i = 0; algorithms != 0; i++, algorithms >>= 1) {
897 if (algorithms & 1) {
898 char *name;
899 GPR_ASSERT(grpc_compression_algorithm_name((grpc_compression_algorithm)i,
900 &name));
901 if (sv.count) {
902 gpr_strvec_add(&sv, gpr_strdup(","));
903 }
904 gpr_strvec_add(&sv, gpr_strdup(name));
905 }
906 }
907 str = gpr_strvec_flatten(&sv, NULL);
908 out =
909 grpc_mdelem_from_metadata_strings(mdctx, GRPC_MDSTR_GRPC_ACCEPT_ENCODING,
910 grpc_mdstr_from_string(mdctx, str));
911 gpr_strvec_destroy(&sv);
912 gpr_free(str);
913 return out;
914}
915
916grpc_mdelem *grpc_accept_encoding_mdelem_from_compression_algorithms(
917 grpc_mdctx *ctx, gpr_uint32 algorithms) {
918 grpc_mdelem *ret;
919 gpr_atm *slot;
920 GPR_ASSERT(algorithms < GPR_ARRAY_SIZE(ctx->compression_algorithm_mdelem));
921
922 slot = &ctx->compression_algorithm_mdelem[algorithms];
923 ret = get_cache(slot);
924 if (ret == NULL) {
925 set_cache(slot, make_accept_encoding_mdelem_for_compression_algorithms(
926 ctx, algorithms));
927 ret = get_cache(slot);
928 GPR_ASSERT(ret != NULL);
929 }
930
931 return ret;
932}
933
Craig Tillerb96d0012015-05-06 15:33:23 -0700934int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
935 /* TODO(ctiller): consider caching this */
936 return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
937 GPR_SLICE_LENGTH(s->slice));
938}