blob: da52961fc9ae158a0d0aa7ec829c65d242b7d807 [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
40#include <grpc/support/alloc.h>
Craig Tiller9fa41b92015-04-10 15:08:03 -070041#include <grpc/support/atm.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080042#include <grpc/support/log.h>
Craig Tiller9d35a1f2015-11-02 14:16:12 -080043#include <grpc/support/time.h>
44#include "src/core/profiling/timers.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080045#include "src/core/support/murmur_hash.h"
ctiller430c4992014-12-11 09:15:41 -080046#include "src/core/transport/chttp2/bin_encoder.h"
Craig Tiller0e72ede2015-11-19 07:48:53 -080047#include "src/core/transport/static_metadata.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
Craig Tiller0e72ede2015-11-19 07:48:53 -080055#define INTERNAL_STRING_REF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_ref((s), __FILE__, __LINE__)
56#define INTERNAL_STRING_UNREF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_unref((s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070057#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
58#else
59#define DEBUG_ARGS
60#define FWD_DEBUG_ARGS
Craig Tiller0e72ede2015-11-19 07:48:53 -080061#define INTERNAL_STRING_REF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_ref((s))
62#define INTERNAL_STRING_UNREF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_unref((s))
Craig Tiller1a65a232015-07-06 10:22:32 -070063#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
Craig Tiller0e72ede2015-11-19 07:48:53 -0800101typedef struct static_string {
102 grpc_mdstr *mdstr;
103 gpr_uint32 hash;
104} static_string;
105
106typedef struct static_mdelem {
107 grpc_mdelem *mdelem;
108 gpr_uint32 hash;
109} static_mdelem;
110
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800111struct grpc_mdctx {
112 gpr_uint32 hash_seed;
Craig Tiller9be83ee2015-02-18 14:16:15 -0800113 int refs;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800114
115 gpr_mu mu;
116
Craig Tiller0e72ede2015-11-19 07:48:53 -0800117 static_string static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
118 static_mdelem static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
119 size_t static_strtab_maxprobe;
120 size_t static_mdtab_maxprobe;
121
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122 internal_string **strtab;
123 size_t strtab_count;
124 size_t strtab_capacity;
125
126 internal_metadata **mdtab;
127 size_t mdtab_count;
128 size_t mdtab_free;
129 size_t mdtab_capacity;
130};
131
Craig Tiller1a65a232015-07-06 10:22:32 -0700132static void internal_string_ref(internal_string *s DEBUG_ARGS);
133static void internal_string_unref(internal_string *s DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800134static void discard_metadata(grpc_mdctx *ctx);
135static void gc_mdtab(grpc_mdctx *ctx);
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700136static void metadata_context_destroy_locked(grpc_mdctx *ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800137
Craig Tiller0e72ede2015-11-19 07:48:53 -0800138void grpc_mdctx_global_init(void) {
139 size_t i;
140 for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
141 grpc_mdstr *elem = &grpc_static_mdstr_table[i];
142 const char *str = grpc_static_metadata_strings[i];
143 *(gpr_slice*)&elem->slice = gpr_slice_from_static_string(str);
144 *(gpr_uint32*)&elem->hash = gpr_murmur_hash3(str, strlen(str), 0);
145 }
146 for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
147 grpc_mdelem *elem = &grpc_static_mdelem_table[i];
148 grpc_mdstr *key = &grpc_static_mdstr_table[2 * i + 0];
149 grpc_mdstr *value = &grpc_static_mdstr_table[2 * i + 1];
150 *(grpc_mdstr**)&elem->key = key;
151 *(grpc_mdstr**)&elem->value = value;
152 }
153}
154
155void grpc_mdctx_global_shutdown(void) {
156}
157
158static int is_mdstr_static(grpc_mdstr *s) {
159 return s >= &grpc_static_mdstr_table[0] && s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
160}
161
162static int is_mdelem_static(grpc_mdelem *e) {
163 return e >= &grpc_static_mdelem_table[0] && e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
164}
165
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800166static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
167
168static void unlock(grpc_mdctx *ctx) {
169 /* If the context has been orphaned we'd like to delete it soon. We check
170 conditions in unlock as it signals the end of mutations on a context.
171
172 We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
173 first. This is equivalent to saying that both tables have zero counts,
174 which is equivalent to saying that strtab_count is zero (as mdelem's MUST
175 reference an mdstr for their key and value slots).
176
177 To encourage that to happen, we start discarding zero reference count
178 mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
179 case), since otherwise we can be stuck waiting for a garbage collection
180 that will never happen. */
Craig Tiller9be83ee2015-02-18 14:16:15 -0800181 if (ctx->refs == 0) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700182/* uncomment if you're having trouble diagnosing an mdelem leak to make
183 things clearer (slows down destruction a lot, however) */
Craig Tiller45ce9272015-07-31 11:22:35 -0700184#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller0d4836d2015-06-30 15:15:43 -0700185 gc_mdtab(ctx);
Craig Tiller45ce9272015-07-31 11:22:35 -0700186#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800187 if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
188 discard_metadata(ctx);
189 }
190 if (ctx->strtab_count == 0) {
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700191 metadata_context_destroy_locked(ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800192 return;
193 }
194 }
195 gpr_mu_unlock(&ctx->mu);
196}
197
Craig Tiller1a65a232015-07-06 10:22:32 -0700198static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
199#ifdef GRPC_METADATA_REFCOUNT_DEBUG
200 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
201 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
202 gpr_atm_no_barrier_load(&md->refcnt),
203 gpr_atm_no_barrier_load(&md->refcnt) + 1,
204 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
205 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
206#endif
Craig Tillerb8e82672015-10-10 13:52:47 -0700207 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800208 md->context->mdtab_free--;
Craig Tillerb8e82672015-10-10 13:52:47 -0700209 } else {
210 GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800211 }
212}
213
214grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
215 grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
Craig Tiller0e72ede2015-11-19 07:48:53 -0800216 size_t i, j;
217
218 memset(ctx, 0, sizeof(*ctx));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800219
Craig Tiller9be83ee2015-02-18 14:16:15 -0800220 ctx->refs = 1;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800221 ctx->hash_seed = seed;
222 gpr_mu_init(&ctx->mu);
223 ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
224 memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
225 ctx->strtab_count = 0;
226 ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
227 ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
228 memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
229 ctx->mdtab_count = 0;
230 ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
231 ctx->mdtab_free = 0;
232
Craig Tiller0e72ede2015-11-19 07:48:53 -0800233 for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
234 const char *str = grpc_static_metadata_strings[i];
235 gpr_uint32 lup_hash = gpr_murmur_hash3(str, strlen(str), seed);
236 for (j = 0;; j++) {
237 size_t idx = (lup_hash + j) % GPR_ARRAY_SIZE(ctx->static_strtab);
238 if (ctx->static_strtab[idx].mdstr == NULL) {
239 ctx->static_strtab[idx].mdstr = &grpc_static_mdstr_table[i];
240 ctx->static_strtab[idx].hash = lup_hash;
241 break;
242 }
243 }
244 if (j > ctx->static_strtab_maxprobe) {
245 ctx->static_strtab_maxprobe = j;
246 }
247 }
248
249 for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
250 grpc_mdelem *elem = &grpc_static_mdelem_table[i];
251 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(elem->key->hash, elem->value->hash);
252 for (j = 0;; j++) {
253 size_t idx = (hash + j) % GPR_ARRAY_SIZE(ctx->static_mdtab);
254 if (ctx->static_mdtab[idx].mdelem == NULL) {
255 ctx->static_mdtab[idx].mdelem = elem;
256 ctx->static_mdtab[idx].hash = hash;
257 break;
258 }
259 }
260 if (j > ctx->static_mdtab_maxprobe) {
261 ctx->static_mdtab_maxprobe = j;
262 }
263 }
264
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800265 return ctx;
266}
267
Craig Tiller32946d32015-01-15 11:37:30 -0800268grpc_mdctx *grpc_mdctx_create(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800269 /* This seed is used to prevent remote connections from controlling hash table
270 * collisions. It needs to be somewhat unpredictable to a remote connection.
271 */
Craig Tiller32ca48c2015-09-10 11:47:15 -0700272 return grpc_mdctx_create_with_seed(
273 (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800274}
275
276static void discard_metadata(grpc_mdctx *ctx) {
277 size_t i;
278 internal_metadata *next, *cur;
279
280 for (i = 0; i < ctx->mdtab_capacity; i++) {
281 cur = ctx->mdtab[i];
282 while (cur) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700283 void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700284 GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800285 next = cur->bucket_next;
Craig Tiller1a65a232015-07-06 10:22:32 -0700286 INTERNAL_STRING_UNREF(cur->key);
287 INTERNAL_STRING_UNREF(cur->value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700288 if (user_data != NULL) {
289 ((destroy_user_data_func)gpr_atm_no_barrier_load(
290 &cur->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800291 }
Craig Tiller83901532015-07-10 14:02:45 -0700292 gpr_mu_destroy(&cur->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800293 gpr_free(cur);
294 cur = next;
295 ctx->mdtab_free--;
296 ctx->mdtab_count--;
297 }
298 ctx->mdtab[i] = NULL;
299 }
300}
301
Vijay Pai7d3d9ca2015-04-02 14:34:27 -0700302static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800303 GPR_ASSERT(ctx->strtab_count == 0);
304 GPR_ASSERT(ctx->mdtab_count == 0);
305 GPR_ASSERT(ctx->mdtab_free == 0);
306 gpr_free(ctx->strtab);
307 gpr_free(ctx->mdtab);
308 gpr_mu_unlock(&ctx->mu);
309 gpr_mu_destroy(&ctx->mu);
310 gpr_free(ctx);
311}
312
Craig Tiller9be83ee2015-02-18 14:16:15 -0800313void grpc_mdctx_ref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800314 GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800315 lock(ctx);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800316 GPR_ASSERT(ctx->refs > 0);
317 ctx->refs++;
318 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800319 GPR_TIMER_END("grpc_mdctx_ref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800320}
321
322void grpc_mdctx_unref(grpc_mdctx *ctx) {
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800323 GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800324 lock(ctx);
325 GPR_ASSERT(ctx->refs > 0);
326 ctx->refs--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800327 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800328 GPR_TIMER_END("grpc_mdctx_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800329}
330
331static void grow_strtab(grpc_mdctx *ctx) {
332 size_t capacity = ctx->strtab_capacity * 2;
333 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800334 internal_string **strtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800335 internal_string *s, *next;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800336
337 GPR_TIMER_BEGIN("grow_strtab", 0);
338
339 strtab = gpr_malloc(sizeof(internal_string *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800340 memset(strtab, 0, sizeof(internal_string *) * capacity);
341
342 for (i = 0; i < ctx->strtab_capacity; i++) {
343 for (s = ctx->strtab[i]; s; s = next) {
344 next = s->bucket_next;
345 s->bucket_next = strtab[s->hash % capacity];
346 strtab[s->hash % capacity] = s;
347 }
348 }
349
350 gpr_free(ctx->strtab);
351 ctx->strtab = strtab;
352 ctx->strtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800353
354 GPR_TIMER_END("grow_strtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800355}
356
357static void internal_destroy_string(internal_string *is) {
358 internal_string **prev_next;
359 internal_string *cur;
360 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800361 GPR_TIMER_BEGIN("internal_destroy_string", 0);
ctiller430c4992014-12-11 09:15:41 -0800362 if (is->has_base64_and_huffman_encoded) {
363 gpr_slice_unref(is->base64_and_huffman);
364 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800365 for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
366 cur = *prev_next;
367 cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
368 ;
369 *prev_next = cur->bucket_next;
370 ctx->strtab_count--;
371 gpr_free(is);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800372 GPR_TIMER_END("internal_destroy_string", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800373}
374
Craig Tiller1a65a232015-07-06 10:22:32 -0700375static void internal_string_ref(internal_string *s DEBUG_ARGS) {
376#ifdef GRPC_METADATA_REFCOUNT_DEBUG
377 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
378 s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
379#endif
380 ++s->refs;
381}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800382
Craig Tiller1a65a232015-07-06 10:22:32 -0700383static void internal_string_unref(internal_string *s DEBUG_ARGS) {
384#ifdef GRPC_METADATA_REFCOUNT_DEBUG
385 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
386 s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
387#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800388 GPR_ASSERT(s->refs > 0);
389 if (0 == --s->refs) {
390 internal_destroy_string(s);
391 }
392}
393
394static void slice_ref(void *p) {
395 internal_string *is =
396 (internal_string *)((char *)p - offsetof(internal_string, refcount));
397 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800398 GPR_TIMER_BEGIN("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800399 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700400 INTERNAL_STRING_REF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800401 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800402 GPR_TIMER_END("slice_ref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800403}
404
405static void slice_unref(void *p) {
406 internal_string *is =
407 (internal_string *)((char *)p - offsetof(internal_string, refcount));
408 grpc_mdctx *ctx = is->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800409 GPR_TIMER_BEGIN("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800410 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700411 INTERNAL_STRING_UNREF(is);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800412 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800413 GPR_TIMER_END("slice_unref", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800414}
415
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700416grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800417 return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
418}
419
420grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
421 grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
422 GPR_SLICE_LENGTH(slice));
423 gpr_slice_unref(slice);
424 return result;
425}
426
427grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
428 size_t length) {
429 gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
430 internal_string *s;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800431 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800432
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800433 GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800434
435 /* search for a static string */
436 for (i = 0; i <= ctx->static_strtab_maxprobe; i++) {
437 size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_strtab);
438 static_string *ss = &ctx->static_strtab[idx];
439 if (ss->hash == hash && GPR_SLICE_LENGTH(ss->mdstr->slice) == length &&
440 0 == memcmp(buf, GPR_SLICE_START_PTR(ss->mdstr->slice), length)) {
441 return ss->mdstr;
442 }
443 }
444
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800445 lock(ctx);
446
447 /* search for an existing string */
448 for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
449 if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
450 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700451 INTERNAL_STRING_REF(s);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800452 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800453 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800454 return (grpc_mdstr *)s;
455 }
456 }
457
458 /* not found: create a new string */
459 if (length + 1 < GPR_SLICE_INLINED_SIZE) {
460 /* string data goes directly into the slice */
461 s = gpr_malloc(sizeof(internal_string));
462 s->refs = 1;
463 s->slice.refcount = NULL;
464 memcpy(s->slice.data.inlined.bytes, buf, length);
465 s->slice.data.inlined.bytes[length] = 0;
Craig Tiller6a6b36c2015-09-10 16:00:22 -0700466 s->slice.data.inlined.length = (gpr_uint8)length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800467 } else {
468 /* string data goes after the internal_string header, and we +1 for null
469 terminator */
470 s = gpr_malloc(sizeof(internal_string) + length + 1);
471 s->refs = 1;
472 s->refcount.ref = slice_ref;
473 s->refcount.unref = slice_unref;
474 s->slice.refcount = &s->refcount;
475 s->slice.data.refcounted.bytes = (gpr_uint8 *)(s + 1);
476 s->slice.data.refcounted.length = length;
477 memcpy(s->slice.data.refcounted.bytes, buf, length);
478 /* add a null terminator for cheap c string conversion when desired */
479 s->slice.data.refcounted.bytes[length] = 0;
480 }
ctiller430c4992014-12-11 09:15:41 -0800481 s->has_base64_and_huffman_encoded = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800482 s->hash = hash;
483 s->context = ctx;
484 s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
485 ctx->strtab[hash % ctx->strtab_capacity] = s;
486
487 ctx->strtab_count++;
488
489 if (ctx->strtab_count > ctx->strtab_capacity * 2) {
490 grow_strtab(ctx);
491 }
492
493 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800494 GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800495
496 return (grpc_mdstr *)s;
497}
498
499static void gc_mdtab(grpc_mdctx *ctx) {
500 size_t i;
501 internal_metadata **prev_next;
502 internal_metadata *md, *next;
503
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800504 GPR_TIMER_BEGIN("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800505 for (i = 0; i < ctx->mdtab_capacity; i++) {
506 prev_next = &ctx->mdtab[i];
507 for (md = ctx->mdtab[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700508 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800509 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700510 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700511 INTERNAL_STRING_UNREF(md->key);
512 INTERNAL_STRING_UNREF(md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800513 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700514 ((destroy_user_data_func)gpr_atm_no_barrier_load(
515 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800516 }
517 gpr_free(md);
518 *prev_next = next;
519 ctx->mdtab_free--;
520 ctx->mdtab_count--;
521 } else {
522 prev_next = &md->bucket_next;
523 }
524 }
525 }
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800526 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800527}
528
529static void grow_mdtab(grpc_mdctx *ctx) {
530 size_t capacity = ctx->mdtab_capacity * 2;
531 size_t i;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800532 internal_metadata **mdtab;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800533 internal_metadata *md, *next;
534 gpr_uint32 hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800535
536 GPR_TIMER_BEGIN("grow_mdtab", 0);
537
538 mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800539 memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
540
541 for (i = 0; i < ctx->mdtab_capacity; i++) {
542 for (md = ctx->mdtab[i]; md; md = next) {
ctillerfb93d192014-12-15 10:40:05 -0800543 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800544 next = md->bucket_next;
545 md->bucket_next = mdtab[hash % capacity];
546 mdtab[hash % capacity] = md;
547 }
548 }
549
550 gpr_free(ctx->mdtab);
551 ctx->mdtab = mdtab;
552 ctx->mdtab_capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800553
554 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800555}
556
557static void rehash_mdtab(grpc_mdctx *ctx) {
558 if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
559 gc_mdtab(ctx);
560 } else {
561 grow_mdtab(ctx);
562 }
563}
564
565grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
566 grpc_mdstr *mkey,
567 grpc_mdstr *mvalue) {
568 internal_string *key = (internal_string *)mkey;
569 internal_string *value = (internal_string *)mvalue;
ctillerfb93d192014-12-15 10:40:05 -0800570 gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800571 internal_metadata *md;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800572 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800573
Craig Tiller0e72ede2015-11-19 07:48:53 -0800574 GPR_ASSERT(is_mdstr_static(mkey) || key->context == ctx);
575 GPR_ASSERT(is_mdstr_static(mvalue) || value->context == ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800576
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800577 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
578
Craig Tiller0e72ede2015-11-19 07:48:53 -0800579 if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
580 for (i = 0; i <= ctx->static_mdtab_maxprobe; i++) {
581 size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_mdtab);
582 static_mdelem *smd = &ctx->static_mdtab[idx];
583 if (smd->hash == hash && smd->mdelem->key == mkey && smd->mdelem->value == mvalue) {
584 return smd->mdelem;
585 }
586 }
587 }
588
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800589 lock(ctx);
590
591 /* search for an existing pair */
592 for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
593 if (md->key == key && md->value == value) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700594 REF_MD_LOCKED(md);
595 INTERNAL_STRING_UNREF(key);
596 INTERNAL_STRING_UNREF(value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800597 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800598 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800599 return (grpc_mdelem *)md;
600 }
601 }
602
603 /* not found: create a new pair */
604 md = gpr_malloc(sizeof(internal_metadata));
Craig Tiller63bda562015-10-09 17:40:19 -0700605 gpr_atm_rel_store(&md->refcnt, 2);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800606 md->context = ctx;
607 md->key = key;
608 md->value = value;
Craig Tiller8344daa2015-10-09 18:10:57 -0700609 md->user_data = 0;
610 md->destroy_user_data = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800611 md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
Craig Tiller83901532015-07-10 14:02:45 -0700612 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700613#ifdef GRPC_METADATA_REFCOUNT_DEBUG
614 gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
615 gpr_atm_no_barrier_load(&md->refcnt),
616 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
617 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
618#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800619 ctx->mdtab[hash % ctx->mdtab_capacity] = md;
620 ctx->mdtab_count++;
621
622 if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
623 rehash_mdtab(ctx);
624 }
625
626 unlock(ctx);
627
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800628 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
629
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800630 return (grpc_mdelem *)md;
631}
632
633grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
634 const char *value) {
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700635 return grpc_mdelem_from_metadata_strings(ctx,
636 grpc_mdstr_from_string(ctx, key),
637 grpc_mdstr_from_string(ctx, value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800638}
639
640grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
641 gpr_slice value) {
642 return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
643 grpc_mdstr_from_slice(ctx, value));
644}
645
646grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
647 const char *key,
648 const gpr_uint8 *value,
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700649 size_t value_length) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800650 return grpc_mdelem_from_metadata_strings(
Craig Tiller4dbdd6a2015-09-25 15:12:16 -0700651 ctx, grpc_mdstr_from_string(ctx, key),
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800652 grpc_mdstr_from_buffer(ctx, value, value_length));
653}
654
Craig Tiller1a65a232015-07-06 10:22:32 -0700655grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800656 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800657 if (is_mdelem_static(gmd)) return gmd;
Craig Tiller1a65a232015-07-06 10:22:32 -0700658#ifdef GRPC_METADATA_REFCOUNT_DEBUG
659 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
660 "ELM REF:%p:%d->%d: '%s' = '%s'", md,
661 gpr_atm_no_barrier_load(&md->refcnt),
662 gpr_atm_no_barrier_load(&md->refcnt) + 1,
663 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
664 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
665#endif
Craig Tiller9fa41b92015-04-10 15:08:03 -0700666 /* we can assume the ref count is >= 1 as the application is calling
667 this function - meaning that no adjustment to mdtab_free is necessary,
668 simplifying the logic here to be just an atomic increment */
669 /* use C assert to have this removed in opt builds */
Craig Tillerb8e82672015-10-10 13:52:47 -0700670 assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2);
Craig Tiller9fa41b92015-04-10 15:08:03 -0700671 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800672 return gmd;
673}
674
Craig Tiller1a65a232015-07-06 10:22:32 -0700675void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800676 internal_metadata *md = (internal_metadata *)gmd;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800677 if (!md) return;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800678 if (is_mdelem_static(gmd)) return;
Craig Tiller1a65a232015-07-06 10:22:32 -0700679#ifdef GRPC_METADATA_REFCOUNT_DEBUG
680 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
681 "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
682 gpr_atm_no_barrier_load(&md->refcnt),
683 gpr_atm_no_barrier_load(&md->refcnt) - 1,
684 grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
685 grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
686#endif
Craig Tiller63bda562015-10-09 17:40:19 -0700687 if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
688 grpc_mdctx *ctx = md->context;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800689 GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
Craig Tiller63bda562015-10-09 17:40:19 -0700690 lock(ctx);
Craig Tillerb8e82672015-10-10 13:52:47 -0700691 if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
692 ctx->mdtab_free++;
693 gpr_atm_no_barrier_store(&md->refcnt, 0);
694 }
Craig Tiller63bda562015-10-09 17:40:19 -0700695 unlock(ctx);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800696 GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800697 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800698}
699
700const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
701 return (const char *)GPR_SLICE_START_PTR(s->slice);
702}
703
Craig Tiller1a65a232015-07-06 10:22:32 -0700704grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800705 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800706 grpc_mdctx *ctx;
707 if (is_mdstr_static(gs)) return gs;
708 ctx = s->context;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800709 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700710 internal_string_ref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800711 unlock(ctx);
712 return gs;
713}
714
Craig Tiller1a65a232015-07-06 10:22:32 -0700715void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800716 internal_string *s = (internal_string *)gs;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800717 grpc_mdctx *ctx;
718 if (is_mdstr_static(gs)) return;
719 ctx = s->context;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800720 lock(ctx);
Craig Tiller1a65a232015-07-06 10:22:32 -0700721 internal_string_unref(s FWD_DEBUG_ARGS);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800722 unlock(ctx);
723}
724
725size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
726 return ctx->mdtab_capacity;
727}
728
729size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
730 return ctx->mdtab_count;
731}
732
733size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
734 return ctx->mdtab_free;
735}
736
Craig Tiller8344daa2015-10-09 18:10:57 -0700737void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800738 internal_metadata *im = (internal_metadata *)md;
Craig Tiller83901532015-07-10 14:02:45 -0700739 void *result;
Craig Tiller8344daa2015-10-09 18:10:57 -0700740 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
741 return (void *)gpr_atm_no_barrier_load(&im->user_data);
742 } else {
743 return NULL;
744 }
Craig Tiller83901532015-07-10 14:02:45 -0700745 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800746}
747
748void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
749 void *user_data) {
750 internal_metadata *im = (internal_metadata *)md;
751 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
Craig Tiller83901532015-07-10 14:02:45 -0700752 gpr_mu_lock(&im->mu_user_data);
Craig Tiller8344daa2015-10-09 18:10:57 -0700753 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
Craig Tiller83901532015-07-10 14:02:45 -0700754 /* user data can only be set once */
755 gpr_mu_unlock(&im->mu_user_data);
756 if (destroy_func != NULL) {
757 destroy_func(user_data);
758 }
759 return;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800760 }
Craig Tiller8344daa2015-10-09 18:10:57 -0700761 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
762 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
Craig Tiller83901532015-07-10 14:02:45 -0700763 gpr_mu_unlock(&im->mu_user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800764}
ctiller430c4992014-12-11 09:15:41 -0800765
766gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
767 internal_string *s = (internal_string *)gs;
768 gpr_slice slice;
769 grpc_mdctx *ctx = s->context;
770 lock(ctx);
771 if (!s->has_base64_and_huffman_encoded) {
772 s->base64_and_huffman =
773 grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
ctiller33023c42014-12-12 16:28:33 -0800774 s->has_base64_and_huffman_encoded = 1;
ctiller430c4992014-12-11 09:15:41 -0800775 }
776 slice = s->base64_and_huffman;
777 unlock(ctx);
778 return slice;
Craig Tiller190d3602015-02-18 09:23:38 -0800779}
Craig Tillerfe0104a2015-04-14 09:19:12 -0700780
Craig Tiller49772e02015-08-21 08:08:37 -0700781static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) {
Craig Tillerb96d0012015-05-06 15:33:23 -0700782 const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice);
783 const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice);
784 for (; p != e; p++) {
Craig Tiller49772e02015-08-21 08:08:37 -0700785 int idx = *p;
786 int byte = idx / 8;
787 int bit = idx % 8;
788 if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
Craig Tillerb96d0012015-05-06 15:33:23 -0700789 }
790 return 1;
791}
792
Craig Tiller49772e02015-08-21 08:08:37 -0700793int grpc_mdstr_is_legal_header(grpc_mdstr *s) {
794 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller80aa2802015-08-21 08:50:51 -0700795 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00,
796 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Craig Tiller49772e02015-08-21 08:08:37 -0700797 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Craig Tiller49772e02015-08-21 08:08:37 -0700798 return conforms_to(s, legal_header_bits);
799}
800
801int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
802 static const gpr_uint8 legal_header_bits[256 / 8] = {
Craig Tiller240b7db2015-08-27 15:35:32 -0700803 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
Craig Tiller49772e02015-08-21 08:08:37 -0700804 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
805 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
806 return conforms_to(s, legal_header_bits);
807}
808
Craig Tillerb96d0012015-05-06 15:33:23 -0700809int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
810 /* TODO(ctiller): consider caching this */
811 return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
812 GPR_SLICE_LENGTH(s->slice));
813}