Use static metadata table in metadata.c
diff --git a/include/grpc/support/slice.h b/include/grpc/support/slice.h
index 3abb1b7..507cb19 100644
--- a/include/grpc/support/slice.h
+++ b/include/grpc/support/slice.h
@@ -144,6 +144,9 @@
      memcpy(slice->data, source, len); */
 gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len);
 
+/* Create a slice pointing to constant memory */
+gpr_slice gpr_slice_from_static_string(const char *source);
+
 /* Return a result slice derived from s, which shares a ref count with s, where
    result.data==s.data+begin, and result.length==end-begin.
    The ref count of s is increased by one.
diff --git a/src/core/support/slice.c b/src/core/support/slice.c
index 53024e8..0470533 100644
--- a/src/core/support/slice.c
+++ b/src/core/support/slice.c
@@ -57,6 +57,20 @@
   }
 }
 
+/* gpr_slice_from_static_string support structure - a refcount that does
+   nothing */
+static void noop_ref_or_unref(void *unused) {}
+
+static gpr_slice_refcount noop_refcount = {noop_ref_or_unref, noop_ref_or_unref};
+
+gpr_slice gpr_slice_from_static_string(const char *s) {
+  gpr_slice slice;
+  slice.refcount = &noop_refcount;
+  slice.data.refcounted.bytes = (gpr_uint8*)s;
+  slice.data.refcounted.length = strlen(s);
+  return slice;
+}
+
 /* gpr_slice_new support structures - we create a refcount object extended
    with the user provided data pointer & destroy function */
 typedef struct new_slice_refcount {
diff --git a/src/core/surface/init.c b/src/core/surface/init.c
index f8cba01..04d6862 100644
--- a/src/core/surface/init.c
+++ b/src/core/surface/init.c
@@ -93,6 +93,7 @@
   gpr_mu_lock(&g_init_mu);
   if (++g_initializations == 1) {
     gpr_time_init();
+    grpc_mdctx_global_init();
     grpc_lb_policy_registry_init(grpc_pick_first_lb_factory_create());
     grpc_register_lb_policy(grpc_pick_first_lb_factory_create());
     grpc_register_lb_policy(grpc_round_robin_lb_factory_create());
@@ -147,6 +148,7 @@
         g_all_of_the_plugins[i].destroy();
       }
     }
+    grpc_mdctx_global_shutdown();
   }
   gpr_mu_unlock(&g_init_mu);
 }
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index a72dee4..da52961 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -31,7 +31,6 @@
  *
  */
 
-#include "src/core/iomgr/sockaddr.h"
 #include "src/core/transport/metadata.h"
 
 #include <assert.h>
@@ -45,6 +44,7 @@
 #include "src/core/profiling/timers.h"
 #include "src/core/support/murmur_hash.h"
 #include "src/core/transport/chttp2/bin_encoder.h"
+#include "src/core/transport/static_metadata.h"
 
 #define INITIAL_STRTAB_CAPACITY 4
 #define INITIAL_MDTAB_CAPACITY 4
@@ -52,14 +52,14 @@
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
-#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
+#define INTERNAL_STRING_REF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_ref((s), __FILE__, __LINE__)
+#define INTERNAL_STRING_UNREF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_unref((s), __FILE__, __LINE__)
 #define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
 #else
 #define DEBUG_ARGS
 #define FWD_DEBUG_ARGS
-#define INTERNAL_STRING_REF(s) internal_string_ref((s))
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
+#define INTERNAL_STRING_REF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_ref((s))
+#define INTERNAL_STRING_UNREF(s) if (is_mdstr_static((grpc_mdstr*)(s))); else internal_string_unref((s))
 #define REF_MD_LOCKED(s) ref_md_locked((s))
 #endif
 
@@ -98,12 +98,27 @@
   struct internal_metadata *bucket_next;
 } internal_metadata;
 
+typedef struct static_string {
+  grpc_mdstr *mdstr;
+  gpr_uint32 hash;
+} static_string;
+
+typedef struct static_mdelem {
+  grpc_mdelem *mdelem;
+  gpr_uint32 hash;
+} static_mdelem;
+
 struct grpc_mdctx {
   gpr_uint32 hash_seed;
   int refs;
 
   gpr_mu mu;
 
+  static_string static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
+  static_mdelem static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
+  size_t static_strtab_maxprobe;
+  size_t static_mdtab_maxprobe;
+
   internal_string **strtab;
   size_t strtab_count;
   size_t strtab_capacity;
@@ -120,6 +135,34 @@
 static void gc_mdtab(grpc_mdctx *ctx);
 static void metadata_context_destroy_locked(grpc_mdctx *ctx);
 
+void grpc_mdctx_global_init(void) {
+  size_t i;
+  for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+    grpc_mdstr *elem = &grpc_static_mdstr_table[i];
+    const char *str = grpc_static_metadata_strings[i];
+    *(gpr_slice*)&elem->slice = gpr_slice_from_static_string(str);
+    *(gpr_uint32*)&elem->hash = gpr_murmur_hash3(str, strlen(str), 0);
+  }
+  for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
+    grpc_mdelem *elem = &grpc_static_mdelem_table[i];
+    grpc_mdstr *key = &grpc_static_mdstr_table[2 * i + 0];
+    grpc_mdstr *value = &grpc_static_mdstr_table[2 * i + 1];
+    *(grpc_mdstr**)&elem->key = key;
+    *(grpc_mdstr**)&elem->value = value;
+  }
+}
+
+void grpc_mdctx_global_shutdown(void) {
+}
+
+static int is_mdstr_static(grpc_mdstr *s) {
+  return s >= &grpc_static_mdstr_table[0] && s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+}
+
+static int is_mdelem_static(grpc_mdelem *e) {
+  return e >= &grpc_static_mdelem_table[0] && e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+}
+
 static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
 
 static void unlock(grpc_mdctx *ctx) {
@@ -170,6 +213,9 @@
 
 grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
   grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
+  size_t i, j;
+
+  memset(ctx, 0, sizeof(*ctx));
 
   ctx->refs = 1;
   ctx->hash_seed = seed;
@@ -184,6 +230,38 @@
   ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
   ctx->mdtab_free = 0;
 
+  for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+    const char *str = grpc_static_metadata_strings[i];
+    gpr_uint32 lup_hash = gpr_murmur_hash3(str, strlen(str), seed);
+    for (j = 0;; j++) {
+      size_t idx = (lup_hash + j) % GPR_ARRAY_SIZE(ctx->static_strtab);
+      if (ctx->static_strtab[idx].mdstr == NULL) {
+        ctx->static_strtab[idx].mdstr = &grpc_static_mdstr_table[i];
+        ctx->static_strtab[idx].hash = lup_hash;
+        break;
+      }
+    }
+    if (j > ctx->static_strtab_maxprobe) {
+      ctx->static_strtab_maxprobe = j;
+    }
+  }
+
+  for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
+    grpc_mdelem *elem = &grpc_static_mdelem_table[i];
+    gpr_uint32 hash = GRPC_MDSTR_KV_HASH(elem->key->hash, elem->value->hash);
+    for (j = 0;; j++) {
+      size_t idx = (hash + j) % GPR_ARRAY_SIZE(ctx->static_mdtab);
+      if (ctx->static_mdtab[idx].mdelem == NULL) {
+        ctx->static_mdtab[idx].mdelem = elem;
+        ctx->static_mdtab[idx].hash = hash;
+        break;
+      }
+    }
+    if (j > ctx->static_mdtab_maxprobe) {
+      ctx->static_mdtab_maxprobe = j;
+    }
+  }
+
   return ctx;
 }
 
@@ -350,8 +428,20 @@
                                    size_t length) {
   gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
   internal_string *s;
+  size_t i;
 
   GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
+
+  /* search for a static string */
+  for (i = 0; i <= ctx->static_strtab_maxprobe; i++) {
+    size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_strtab);
+    static_string *ss = &ctx->static_strtab[idx];
+    if (ss->hash == hash && GPR_SLICE_LENGTH(ss->mdstr->slice) == length &&
+        0 == memcmp(buf, GPR_SLICE_START_PTR(ss->mdstr->slice), length)) {
+      return ss->mdstr;
+    }
+  }
+
   lock(ctx);
 
   /* search for an existing string */
@@ -479,12 +569,23 @@
   internal_string *value = (internal_string *)mvalue;
   gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
   internal_metadata *md;
+  size_t i;
 
-  GPR_ASSERT(key->context == ctx);
-  GPR_ASSERT(value->context == ctx);
+  GPR_ASSERT(is_mdstr_static(mkey) || key->context == ctx);
+  GPR_ASSERT(is_mdstr_static(mvalue) || value->context == ctx);
 
   GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
 
+  if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
+    for (i = 0; i <= ctx->static_mdtab_maxprobe; i++) {
+      size_t idx = (hash + i) % GPR_ARRAY_SIZE(ctx->static_mdtab);
+      static_mdelem *smd = &ctx->static_mdtab[idx];
+      if (smd->hash == hash && smd->mdelem->key == mkey && smd->mdelem->value == mvalue) {
+        return smd->mdelem;
+      }
+    }
+  }
+
   lock(ctx);
 
   /* search for an existing pair */
@@ -553,6 +654,7 @@
 
 grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
   internal_metadata *md = (internal_metadata *)gmd;
+  if (is_mdelem_static(gmd)) return gmd;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
@@ -573,6 +675,7 @@
 void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
   internal_metadata *md = (internal_metadata *)gmd;
   if (!md) return;
+  if (is_mdelem_static(gmd)) return;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
@@ -600,7 +703,9 @@
 
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx = s->context;
+  grpc_mdctx *ctx;
+  if (is_mdstr_static(gs)) return gs;
+  ctx = s->context;
   lock(ctx);
   internal_string_ref(s FWD_DEBUG_ARGS);
   unlock(ctx);
@@ -609,7 +714,9 @@
 
 void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx = s->context;
+  grpc_mdctx *ctx;
+  if (is_mdstr_static(gs)) return;
+  ctx = s->context;
   lock(ctx);
   internal_string_unref(s FWD_DEBUG_ARGS);
   unlock(ctx);
diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h
index 9a81640..4c52896 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -157,4 +157,7 @@
 
 #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
 
+void grpc_mdctx_global_init(void);
+void grpc_mdctx_global_shutdown(void);
+
 #endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index ed46e7b..fb2cd2d 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -84,7 +84,7 @@
   gpr_thd_id id;
   char *hex;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
+  grpc_mdctx *mdctx;
   gpr_slice slice =
       gpr_slice_from_copied_buffer(client_payload, client_payload_length);
   gpr_slice_buffer outgoing;
@@ -102,6 +102,8 @@
   /* Init grpc */
   grpc_init();
 
+  mdctx = grpc_mdctx_create();
+
   /* Create endpoints */
   sfd = grpc_iomgr_create_endpoint_pair("fixture", 65536);
 
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 5e1ba11..08550b4 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -140,6 +140,8 @@
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_create_channel_stack();
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/chttp2/hpack_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 6553e0d..30cb6c1 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -191,8 +191,10 @@
 int main(int argc, char **argv) {
   size_t i;
   grpc_test_init(argc, argv);
+  grpc_init();
   TEST(test_basic_headers);
   TEST(test_decode_table_overflow);
+  grpc_shutdown();
   for (i = 0; i < num_to_delete; i++) {
     gpr_free(to_delete[i]);
   }
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index 3a31337..4e52b0e 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -217,7 +217,9 @@
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_vectors(GRPC_SLICE_SPLIT_MERGE_ALL);
   test_vectors(GRPC_SLICE_SPLIT_ONE_BYTE);
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index aa3e273..adc69bf 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -36,10 +36,12 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include <grpc/grpc.h>
+
+#include "src/core/support/string.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
@@ -268,8 +270,10 @@
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_static_lookup();
   test_many_additions();
   test_find();
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index 080e86c..fc9c93a 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -270,6 +270,7 @@
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_no_op();
   test_create_string();
   test_create_metadata();
@@ -279,5 +280,6 @@
   test_things_stick_around();
   test_slices_work();
   test_base64_and_huffman_works();
+  grpc_shutdown();
   return 0;
 }