|  | @@ -39,165 +39,214 @@
 | 
	
		
			
				|  |  |  #include <grpc/support/string_util.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/time.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#include "src/core/lib/transport/hashtable.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/transport/metadata.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  // grpc_method_config
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// bool vtable
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void* bool_copy(void* valuep) {
 | 
	
		
			
				|  |  | +  bool value = *(bool*)valuep;
 | 
	
		
			
				|  |  | +  bool* new_value = gpr_malloc(sizeof(bool));
 | 
	
		
			
				|  |  | +  *new_value = value;
 | 
	
		
			
				|  |  | +  return new_value;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int bool_cmp(void* v1, void* v2) {
 | 
	
		
			
				|  |  | +  bool b1 = *(bool*)v1;
 | 
	
		
			
				|  |  | +  bool b2 = *(bool*)v2;
 | 
	
		
			
				|  |  | +  if (!b1 && b2) return -1;
 | 
	
		
			
				|  |  | +  if (b1 && !b2) return 1;
 | 
	
		
			
				|  |  | +  return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_hash_table_vtable bool_vtable = {gpr_free, bool_copy, bool_cmp};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// timespec vtable
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void* timespec_copy(void* valuep) {
 | 
	
		
			
				|  |  | +  gpr_timespec value = *(gpr_timespec*)valuep;
 | 
	
		
			
				|  |  | +  gpr_timespec* new_value = gpr_malloc(sizeof(gpr_timespec));
 | 
	
		
			
				|  |  | +  *new_value = value;
 | 
	
		
			
				|  |  | +  return new_value;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int timespec_cmp(void* v1, void* v2) {
 | 
	
		
			
				|  |  | +  return gpr_time_cmp(*(gpr_timespec*)v1, *(gpr_timespec*)v2);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_hash_table_vtable timespec_vtable = {
 | 
	
		
			
				|  |  | +    gpr_free, timespec_copy, timespec_cmp};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// int32 vtable
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void* int32_copy(void* valuep) {
 | 
	
		
			
				|  |  | +  int32_t value = *(int32_t*)valuep;
 | 
	
		
			
				|  |  | +  int32_t* new_value = gpr_malloc(sizeof(int32_t));
 | 
	
		
			
				|  |  | +  *new_value = value;
 | 
	
		
			
				|  |  | +  return new_value;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int int32_cmp(void* v1, void* v2) {
 | 
	
		
			
				|  |  | +  int32_t i1 = *(int32_t*)v1;
 | 
	
		
			
				|  |  | +  int32_t i2 = *(int32_t*)v2;
 | 
	
		
			
				|  |  | +  if (i1 < i2) return -1;
 | 
	
		
			
				|  |  | +  if (i1 > i2) return 1;
 | 
	
		
			
				|  |  | +  return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_hash_table_vtable int32_vtable = {gpr_free, int32_copy, int32_cmp};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Hash table keys.
 | 
	
		
			
				|  |  | +#define GRPC_METHOD_CONFIG_WAIT_FOR_READY "grpc.wait_for_ready"  // bool
 | 
	
		
			
				|  |  | +#define GRPC_METHOD_CONFIG_TIMEOUT "grpc.timeout"  // gpr_timespec
 | 
	
		
			
				|  |  | +#define GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES \
 | 
	
		
			
				|  |  | +    "grpc.max_request_message_bytes"  // int32
 | 
	
		
			
				|  |  | +#define GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES \
 | 
	
		
			
				|  |  | +    "grpc.max_response_message_bytes"  // int32
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  struct grpc_method_config {
 | 
	
		
			
				|  |  | -  gpr_refcount refs;
 | 
	
		
			
				|  |  | -  bool* wait_for_ready;
 | 
	
		
			
				|  |  | -  gpr_timespec* timeout;
 | 
	
		
			
				|  |  | -  int32_t* max_request_message_bytes;
 | 
	
		
			
				|  |  | -  int32_t* max_response_message_bytes;
 | 
	
		
			
				|  |  | +  grpc_hash_table* table;
 | 
	
		
			
				|  |  | +  grpc_mdstr* wait_for_ready_key;
 | 
	
		
			
				|  |  | +  grpc_mdstr* timeout_key;
 | 
	
		
			
				|  |  | +  grpc_mdstr* max_request_message_bytes_key;
 | 
	
		
			
				|  |  | +  grpc_mdstr* max_response_message_bytes_key;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_method_config* grpc_method_config_create(
 | 
	
		
			
				|  |  |      bool* wait_for_ready, gpr_timespec* timeout,
 | 
	
		
			
				|  |  |      int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) {
 | 
	
		
			
				|  |  | -  grpc_method_config* config = gpr_malloc(sizeof(*config));
 | 
	
		
			
				|  |  | -  memset(config, 0, sizeof(*config));
 | 
	
		
			
				|  |  | -  gpr_ref_init(&config->refs, 1);
 | 
	
		
			
				|  |  | +  grpc_method_config* method_config = gpr_malloc(sizeof(grpc_method_config));
 | 
	
		
			
				|  |  | +  memset(method_config, 0, sizeof(grpc_method_config));
 | 
	
		
			
				|  |  | +  grpc_hash_table_entry entries[4];
 | 
	
		
			
				|  |  | +  size_t num_entries = 0;
 | 
	
		
			
				|  |  |    if (wait_for_ready != NULL) {
 | 
	
		
			
				|  |  | -    config->wait_for_ready = gpr_malloc(sizeof(*wait_for_ready));
 | 
	
		
			
				|  |  | -    *config->wait_for_ready = *wait_for_ready;
 | 
	
		
			
				|  |  | +    method_config->wait_for_ready_key =
 | 
	
		
			
				|  |  | +        grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY);
 | 
	
		
			
				|  |  | +    entries[num_entries].key = method_config->wait_for_ready_key;
 | 
	
		
			
				|  |  | +    entries[num_entries].value = wait_for_ready;
 | 
	
		
			
				|  |  | +    entries[num_entries].vtable = &bool_vtable;
 | 
	
		
			
				|  |  | +    ++num_entries;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (timeout != NULL) {
 | 
	
		
			
				|  |  | -    config->timeout = gpr_malloc(sizeof(*timeout));
 | 
	
		
			
				|  |  | -    *config->timeout = *timeout;
 | 
	
		
			
				|  |  | +    method_config->timeout_key =
 | 
	
		
			
				|  |  | +        grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT);
 | 
	
		
			
				|  |  | +    entries[num_entries].key = method_config->timeout_key;
 | 
	
		
			
				|  |  | +    entries[num_entries].value = timeout;
 | 
	
		
			
				|  |  | +    entries[num_entries].vtable = ×pec_vtable;
 | 
	
		
			
				|  |  | +    ++num_entries;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (max_request_message_bytes != NULL) {
 | 
	
		
			
				|  |  | -    config->max_request_message_bytes =
 | 
	
		
			
				|  |  | -        gpr_malloc(sizeof(*max_request_message_bytes));
 | 
	
		
			
				|  |  | -    *config->max_request_message_bytes = *max_request_message_bytes;
 | 
	
		
			
				|  |  | +    method_config->max_request_message_bytes_key =
 | 
	
		
			
				|  |  | +        grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES);
 | 
	
		
			
				|  |  | +    entries[num_entries].key = method_config->max_request_message_bytes_key;
 | 
	
		
			
				|  |  | +    entries[num_entries].value = max_request_message_bytes;
 | 
	
		
			
				|  |  | +    entries[num_entries].vtable = &int32_vtable;
 | 
	
		
			
				|  |  | +    ++num_entries;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (max_response_message_bytes != NULL) {
 | 
	
		
			
				|  |  | -    config->max_response_message_bytes =
 | 
	
		
			
				|  |  | -        gpr_malloc(sizeof(*max_response_message_bytes));
 | 
	
		
			
				|  |  | -    *config->max_response_message_bytes = *max_response_message_bytes;
 | 
	
		
			
				|  |  | +    method_config->max_response_message_bytes_key =
 | 
	
		
			
				|  |  | +        grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES);
 | 
	
		
			
				|  |  | +    entries[num_entries].key = method_config->max_response_message_bytes_key;
 | 
	
		
			
				|  |  | +    entries[num_entries].value = max_response_message_bytes;
 | 
	
		
			
				|  |  | +    entries[num_entries].vtable = &int32_vtable;
 | 
	
		
			
				|  |  | +    ++num_entries;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return config;
 | 
	
		
			
				|  |  | +  method_config->table = grpc_hash_table_create(num_entries, entries);
 | 
	
		
			
				|  |  | +  return method_config;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  gpr_ref(&method_config->refs);
 | 
	
		
			
				|  |  | +  grpc_hash_table_ref(method_config->table);
 | 
	
		
			
				|  |  |    return method_config;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_method_config_unref(grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  if (gpr_unref(&method_config->refs)) {
 | 
	
		
			
				|  |  | -    gpr_free(method_config->wait_for_ready);
 | 
	
		
			
				|  |  | -    gpr_free(method_config->timeout);
 | 
	
		
			
				|  |  | -    gpr_free(method_config->max_request_message_bytes);
 | 
	
		
			
				|  |  | -    gpr_free(method_config->max_response_message_bytes);
 | 
	
		
			
				|  |  | -    gpr_free(method_config);
 | 
	
		
			
				|  |  | +  if (grpc_hash_table_unref(method_config->table)) {
 | 
	
		
			
				|  |  | +    GRPC_MDSTR_UNREF(method_config->wait_for_ready_key);
 | 
	
		
			
				|  |  | +    GRPC_MDSTR_UNREF(method_config->timeout_key);
 | 
	
		
			
				|  |  | +    GRPC_MDSTR_UNREF(method_config->max_request_message_bytes_key);
 | 
	
		
			
				|  |  | +    GRPC_MDSTR_UNREF(method_config->max_response_message_bytes_key);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +int grpc_method_config_cmp(grpc_method_config* method_config1,
 | 
	
		
			
				|  |  | +                           grpc_method_config* method_config2) {
 | 
	
		
			
				|  |  | +  return grpc_hash_table_cmp(method_config1->table, method_config2->table);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  bool* grpc_method_config_get_wait_for_ready(grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  return method_config->wait_for_ready;
 | 
	
		
			
				|  |  | +  return grpc_hash_table_get(method_config->table,
 | 
	
		
			
				|  |  | +                             method_config->wait_for_ready_key);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  gpr_timespec* grpc_method_config_get_timeout(
 | 
	
		
			
				|  |  |      grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  return method_config->timeout;
 | 
	
		
			
				|  |  | +  return grpc_hash_table_get(method_config->table, method_config->timeout_key);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int32_t* grpc_method_config_get_max_request_message_bytes(
 | 
	
		
			
				|  |  |      grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  return method_config->max_request_message_bytes;
 | 
	
		
			
				|  |  | +  return grpc_hash_table_get(method_config->table,
 | 
	
		
			
				|  |  | +                             method_config->max_request_message_bytes_key);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int32_t* grpc_method_config_get_max_response_message_bytes(
 | 
	
		
			
				|  |  |      grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  return method_config->max_response_message_bytes;
 | 
	
		
			
				|  |  | +  return grpc_hash_table_get(method_config->table,
 | 
	
		
			
				|  |  | +                             method_config->max_response_message_bytes_key);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  // grpc_method_config_table
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef struct grpc_method_config_table_entry {
 | 
	
		
			
				|  |  | -  grpc_mdstr* path;
 | 
	
		
			
				|  |  | -  grpc_method_config* method_config;
 | 
	
		
			
				|  |  | -} grpc_method_config_table_entry;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#define METHOD_CONFIG_TABLE_SIZE 128
 | 
	
		
			
				|  |  | -struct grpc_method_config_table {
 | 
	
		
			
				|  |  | -  gpr_refcount refs;
 | 
	
		
			
				|  |  | -  grpc_method_config_table_entry entries[METHOD_CONFIG_TABLE_SIZE];
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -grpc_method_config_table* grpc_method_config_table_create() {
 | 
	
		
			
				|  |  | -  grpc_method_config_table* table = gpr_malloc(sizeof(*table));
 | 
	
		
			
				|  |  | -  memset(table, 0, sizeof(*table));
 | 
	
		
			
				|  |  | -  gpr_ref_init(&table->refs, 1);
 | 
	
		
			
				|  |  | -  return table;
 | 
	
		
			
				|  |  | +static void method_config_unref(void* valuep) {
 | 
	
		
			
				|  |  | +  grpc_method_config_unref(valuep);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -grpc_method_config_table* grpc_method_config_table_ref(
 | 
	
		
			
				|  |  | -    grpc_method_config_table* table) {
 | 
	
		
			
				|  |  | -  if (table != NULL) gpr_ref(&table->refs);
 | 
	
		
			
				|  |  | -  return table;
 | 
	
		
			
				|  |  | +static void* method_config_ref(void* valuep) {
 | 
	
		
			
				|  |  | +  return grpc_method_config_ref(valuep);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_method_config_table_unref(grpc_method_config_table* table) {
 | 
	
		
			
				|  |  | -  if (table != NULL && gpr_unref(&table->refs)) {
 | 
	
		
			
				|  |  | -    for (size_t i = 0; i < GPR_ARRAY_SIZE(table->entries); ++i) {
 | 
	
		
			
				|  |  | -      grpc_method_config_table_entry* entry = &table->entries[i];
 | 
	
		
			
				|  |  | -      if (entry->path != NULL) {
 | 
	
		
			
				|  |  | -        GRPC_MDSTR_UNREF(entry->path);
 | 
	
		
			
				|  |  | -        grpc_method_config_unref(entry->method_config);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +static int method_config_cmp(void* valuep1, void* valuep2) {
 | 
	
		
			
				|  |  | +  return grpc_method_config_cmp(valuep1, valuep2);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Helper function for insert and get operations that performs quadratic
 | 
	
		
			
				|  |  | -// probing (https://en.wikipedia.org/wiki/Quadratic_probing).
 | 
	
		
			
				|  |  | -static size_t grpc_method_config_table_find_index(
 | 
	
		
			
				|  |  | -    grpc_method_config_table* table, grpc_mdstr* path, bool find_empty) {
 | 
	
		
			
				|  |  | -  for (size_t i = 0; i < GPR_ARRAY_SIZE(table->entries); ++i) {
 | 
	
		
			
				|  |  | -    const size_t idx = (path->hash + i * i) % GPR_ARRAY_SIZE(table->entries);
 | 
	
		
			
				|  |  | -    if (table->entries[idx].path == NULL)
 | 
	
		
			
				|  |  | -      return find_empty ? idx : GPR_ARRAY_SIZE(table->entries);
 | 
	
		
			
				|  |  | -    if (table->entries[idx].path == path) return idx;
 | 
	
		
			
				|  |  | +static const grpc_hash_table_vtable method_config_table_vtable = {
 | 
	
		
			
				|  |  | +    method_config_unref, method_config_ref, method_config_cmp};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +grpc_method_config_table* grpc_method_config_table_create(
 | 
	
		
			
				|  |  | +    size_t num_entries, grpc_method_config_table_entry* entries) {
 | 
	
		
			
				|  |  | +  grpc_hash_table_entry hash_table_entries[num_entries];
 | 
	
		
			
				|  |  | +  for (size_t i = 0; i < num_entries; ++i) {
 | 
	
		
			
				|  |  | +    hash_table_entries[i].key = entries[i].method_name;
 | 
	
		
			
				|  |  | +    hash_table_entries[i].value = entries[i].method_config;
 | 
	
		
			
				|  |  | +    hash_table_entries[i].vtable = &method_config_table_vtable;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return GPR_ARRAY_SIZE(table->entries) + 1;  // Not found.
 | 
	
		
			
				|  |  | +  return grpc_hash_table_create(num_entries, hash_table_entries);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void grpc_method_config_table_insert(grpc_method_config_table* table,
 | 
	
		
			
				|  |  | -                                            grpc_mdstr* path,
 | 
	
		
			
				|  |  | -                                            grpc_method_config* config) {
 | 
	
		
			
				|  |  | -  const size_t idx =
 | 
	
		
			
				|  |  | -      grpc_method_config_table_find_index(table, path, true /* find_empty */);
 | 
	
		
			
				|  |  | -  // This can happen if the table is full.
 | 
	
		
			
				|  |  | -  GPR_ASSERT(idx != GPR_ARRAY_SIZE(table->entries));
 | 
	
		
			
				|  |  | -  grpc_method_config_table_entry* entry = &table->entries[idx];
 | 
	
		
			
				|  |  | -  entry->path = GRPC_MDSTR_REF(path);
 | 
	
		
			
				|  |  | -  entry->method_config = grpc_method_config_ref(config);
 | 
	
		
			
				|  |  | +grpc_method_config_table* grpc_method_config_table_ref(
 | 
	
		
			
				|  |  | +    grpc_method_config_table* table) {
 | 
	
		
			
				|  |  | +  return grpc_hash_table_ref(table);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static grpc_method_config* grpc_method_config_table_get(
 | 
	
		
			
				|  |  | -    grpc_method_config_table* table, grpc_mdstr* path) {
 | 
	
		
			
				|  |  | -  const size_t idx =
 | 
	
		
			
				|  |  | -      grpc_method_config_table_find_index(table, path, false /* find_empty */);
 | 
	
		
			
				|  |  | -  if (idx == GPR_ARRAY_SIZE(table->entries)) return NULL;  // Not found.
 | 
	
		
			
				|  |  | -  return table->entries[idx].method_config;
 | 
	
		
			
				|  |  | +void grpc_method_config_table_unref(grpc_method_config_table* table) {
 | 
	
		
			
				|  |  | +  grpc_hash_table_unref(table);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_method_config_table_add_method_config(
 | 
	
		
			
				|  |  | -    grpc_method_config_table* table, grpc_mdstr** paths, size_t num_paths,
 | 
	
		
			
				|  |  | -    grpc_method_config* method_config) {
 | 
	
		
			
				|  |  | -  for (size_t i = 0; i < num_paths; ++i) {
 | 
	
		
			
				|  |  | -    grpc_method_config_table_insert(table, paths[i], method_config);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +int grpc_method_config_table_cmp(grpc_method_config_table* table1,
 | 
	
		
			
				|  |  | +                                 grpc_method_config_table* table2) {
 | 
	
		
			
				|  |  | +  return grpc_hash_table_cmp(table1, table2);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_method_config* grpc_method_config_table_get_method_config(
 | 
	
		
			
				|  |  |      grpc_method_config_table* table, grpc_mdstr* path) {
 | 
	
		
			
				|  |  | -  grpc_method_config* method_config = grpc_method_config_table_get(table, path);
 | 
	
		
			
				|  |  | +  grpc_method_config* method_config = grpc_hash_table_get(table, path);
 | 
	
		
			
				|  |  |    // If we didn't find a match for the path, try looking for a wildcard
 | 
	
		
			
				|  |  |    // entry (i.e., change "/service/method" to "/service/*").
 | 
	
		
			
				|  |  |    if (method_config == NULL) {
 | 
	
	
		
			
				|  | @@ -209,7 +258,7 @@ grpc_method_config* grpc_method_config_table_get_method_config(
 | 
	
		
			
				|  |  |      buf[len] = '*';
 | 
	
		
			
				|  |  |      buf[len + 1] = '\0';
 | 
	
		
			
				|  |  |      grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
 | 
	
		
			
				|  |  | -    method_config = grpc_method_config_table_get(table, wildcard_path);
 | 
	
		
			
				|  |  | +    method_config = grpc_hash_table_get(table, wildcard_path);
 | 
	
		
			
				|  |  |      GRPC_MDSTR_UNREF(wildcard_path);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    return grpc_method_config_ref(method_config);
 | 
	
	
		
			
				|  | @@ -224,52 +273,7 @@ static void destroy_arg(void* p) {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int cmp_arg(void* p1, void* p2) {
 | 
	
		
			
				|  |  | -  grpc_method_config_table* t1 = p1;
 | 
	
		
			
				|  |  | -  grpc_method_config_table* t2 = p2;
 | 
	
		
			
				|  |  | -  for (size_t i = 0; i < GPR_ARRAY_SIZE(t1->entries); ++i) {
 | 
	
		
			
				|  |  | -    grpc_method_config_table_entry* e1 = &t1->entries[i];
 | 
	
		
			
				|  |  | -    grpc_method_config_table_entry* e2 = &t2->entries[i];
 | 
	
		
			
				|  |  | -    // Compare paths by hash value.
 | 
	
		
			
				|  |  | -    if (e1->path->hash < e2->path->hash) return -1;
 | 
	
		
			
				|  |  | -    if (e1->path->hash > e2->path->hash) return 1;
 | 
	
		
			
				|  |  | -    // Compare wait_for_ready.
 | 
	
		
			
				|  |  | -    const bool wait_for_ready1 =
 | 
	
		
			
				|  |  | -        e1->method_config->wait_for_ready == NULL
 | 
	
		
			
				|  |  | -        ? false : *e1->method_config->wait_for_ready;
 | 
	
		
			
				|  |  | -    const bool wait_for_ready2 =
 | 
	
		
			
				|  |  | -        e2->method_config->wait_for_ready == NULL
 | 
	
		
			
				|  |  | -        ? false : *e2->method_config->wait_for_ready;
 | 
	
		
			
				|  |  | -    if (wait_for_ready1 < wait_for_ready2) return -1;
 | 
	
		
			
				|  |  | -    if (wait_for_ready1 > wait_for_ready2) return 1;
 | 
	
		
			
				|  |  | -    // Compare timeout.
 | 
	
		
			
				|  |  | -    const gpr_timespec timeout1 =
 | 
	
		
			
				|  |  | -        e1->method_config->timeout == NULL
 | 
	
		
			
				|  |  | -        ? gpr_inf_past(GPR_CLOCK_MONOTONIC) : *e1->method_config->timeout;
 | 
	
		
			
				|  |  | -    const gpr_timespec timeout2 =
 | 
	
		
			
				|  |  | -        e2->method_config->timeout == NULL
 | 
	
		
			
				|  |  | -        ? gpr_inf_past(GPR_CLOCK_MONOTONIC) : *e2->method_config->timeout;
 | 
	
		
			
				|  |  | -    const int timeout_result = gpr_time_cmp(timeout1, timeout2);
 | 
	
		
			
				|  |  | -    if (timeout_result != 0) return timeout_result;
 | 
	
		
			
				|  |  | -    // Compare max_request_message_bytes.
 | 
	
		
			
				|  |  | -    const int32_t max_request_message_bytes1 =
 | 
	
		
			
				|  |  | -        e1->method_config->max_request_message_bytes == NULL
 | 
	
		
			
				|  |  | -        ? -1 : *e1->method_config->max_request_message_bytes;
 | 
	
		
			
				|  |  | -    const int32_t max_request_message_bytes2 =
 | 
	
		
			
				|  |  | -        e2->method_config->max_request_message_bytes == NULL
 | 
	
		
			
				|  |  | -        ? -1 : *e2->method_config->max_request_message_bytes;
 | 
	
		
			
				|  |  | -    if (max_request_message_bytes1 < max_request_message_bytes2) return -1;
 | 
	
		
			
				|  |  | -    if (max_request_message_bytes1 > max_request_message_bytes2) return 1;
 | 
	
		
			
				|  |  | -    // Compare max_response_message_bytes.
 | 
	
		
			
				|  |  | -    const int32_t max_response_message_bytes1 =
 | 
	
		
			
				|  |  | -        e1->method_config->max_response_message_bytes == NULL
 | 
	
		
			
				|  |  | -        ? -1 : *e1->method_config->max_response_message_bytes;
 | 
	
		
			
				|  |  | -    const int32_t max_response_message_bytes2 =
 | 
	
		
			
				|  |  | -        e2->method_config->max_response_message_bytes == NULL
 | 
	
		
			
				|  |  | -        ? -1 : *e2->method_config->max_response_message_bytes;
 | 
	
		
			
				|  |  | -    if (max_response_message_bytes1 < max_response_message_bytes2) return -1;
 | 
	
		
			
				|  |  | -    if (max_response_message_bytes1 > max_response_message_bytes2) return 1;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return 0;
 | 
	
		
			
				|  |  | +  return grpc_method_config_table_cmp(p1, p2);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg};
 |