|
|
|
@ -40,7 +40,9 @@ |
|
|
|
|
#include <grpc/support/time.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/lib/slice/slice_hash_table.h" |
|
|
|
|
#include "src/core/lib/slice/slice_internal.h" |
|
|
|
|
#include "src/core/lib/transport/metadata.h" |
|
|
|
|
#include "src/core/lib/transport/static_metadata.h" |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// grpc_method_config
|
|
|
|
@ -48,37 +50,37 @@ |
|
|
|
|
|
|
|
|
|
// bool vtable
|
|
|
|
|
|
|
|
|
|
static void* bool_copy(void* valuep) { |
|
|
|
|
bool value = *(bool*)valuep; |
|
|
|
|
bool* new_value = gpr_malloc(sizeof(bool)); |
|
|
|
|
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; |
|
|
|
|
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 void free_mem(grpc_exec_ctx* exec_ctx, void* p) { gpr_free(p); } |
|
|
|
|
static void free_mem(grpc_exec_ctx *exec_ctx, void *p) { gpr_free(p); } |
|
|
|
|
|
|
|
|
|
static grpc_slice_hash_table_vtable bool_vtable = {free_mem, 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)); |
|
|
|
|
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 int timespec_cmp(void *v1, void *v2) { |
|
|
|
|
return gpr_time_cmp(*(gpr_timespec *)v1, *(gpr_timespec *)v2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_slice_hash_table_vtable timespec_vtable = {free_mem, timespec_copy, |
|
|
|
@ -86,16 +88,16 @@ static grpc_slice_hash_table_vtable timespec_vtable = {free_mem, timespec_copy, |
|
|
|
|
|
|
|
|
|
// int32 vtable
|
|
|
|
|
|
|
|
|
|
static void* int32_copy(void* valuep) { |
|
|
|
|
int32_t value = *(int32_t*)valuep; |
|
|
|
|
int32_t* new_value = gpr_malloc(sizeof(int32_t)); |
|
|
|
|
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; |
|
|
|
|
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; |
|
|
|
@ -104,57 +106,37 @@ static int int32_cmp(void* v1, void* v2) { |
|
|
|
|
static grpc_slice_hash_table_vtable int32_vtable = {free_mem, 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 { |
|
|
|
|
grpc_slice_hash_table* table; |
|
|
|
|
grpc_slice wait_for_ready_key; |
|
|
|
|
grpc_slice timeout_key; |
|
|
|
|
grpc_slice max_request_message_bytes_key; |
|
|
|
|
grpc_slice max_response_message_bytes_key; |
|
|
|
|
grpc_slice_hash_table *table; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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* method_config = gpr_malloc(sizeof(grpc_method_config)); |
|
|
|
|
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 *method_config = gpr_malloc(sizeof(grpc_method_config)); |
|
|
|
|
memset(method_config, 0, sizeof(grpc_method_config)); |
|
|
|
|
method_config->wait_for_ready_key = |
|
|
|
|
grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY); |
|
|
|
|
method_config->timeout_key = |
|
|
|
|
grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT); |
|
|
|
|
method_config->max_request_message_bytes_key = |
|
|
|
|
grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES); |
|
|
|
|
method_config->max_response_message_bytes_key = |
|
|
|
|
grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES); |
|
|
|
|
grpc_slice_hash_table_entry entries[4]; |
|
|
|
|
size_t num_entries = 0; |
|
|
|
|
if (wait_for_ready != NULL) { |
|
|
|
|
entries[num_entries].key = method_config->wait_for_ready_key; |
|
|
|
|
entries[num_entries].key = GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY; |
|
|
|
|
entries[num_entries].value = wait_for_ready; |
|
|
|
|
entries[num_entries].vtable = &bool_vtable; |
|
|
|
|
++num_entries; |
|
|
|
|
} |
|
|
|
|
if (timeout != NULL) { |
|
|
|
|
entries[num_entries].key = method_config->timeout_key; |
|
|
|
|
entries[num_entries].key = GRPC_MDSTR_GRPC_DOT_TIMEOUT; |
|
|
|
|
entries[num_entries].value = timeout; |
|
|
|
|
entries[num_entries].vtable = ×pec_vtable; |
|
|
|
|
++num_entries; |
|
|
|
|
} |
|
|
|
|
if (max_request_message_bytes != NULL) { |
|
|
|
|
entries[num_entries].key = method_config->max_request_message_bytes_key; |
|
|
|
|
entries[num_entries].key = GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES; |
|
|
|
|
entries[num_entries].value = max_request_message_bytes; |
|
|
|
|
entries[num_entries].vtable = &int32_vtable; |
|
|
|
|
++num_entries; |
|
|
|
|
} |
|
|
|
|
if (max_response_message_bytes != NULL) { |
|
|
|
|
entries[num_entries].key = method_config->max_response_message_bytes_key; |
|
|
|
|
entries[num_entries].key = GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES; |
|
|
|
|
entries[num_entries].value = max_response_message_bytes; |
|
|
|
|
entries[num_entries].vtable = &int32_vtable; |
|
|
|
|
++num_entries; |
|
|
|
@ -163,137 +145,129 @@ grpc_method_config* grpc_method_config_create( |
|
|
|
|
return method_config; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) { |
|
|
|
|
grpc_method_config *grpc_method_config_ref(grpc_method_config *method_config) { |
|
|
|
|
grpc_slice_hash_table_ref(method_config->table); |
|
|
|
|
return method_config; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_method_config_unref(grpc_exec_ctx* exec_ctx, |
|
|
|
|
grpc_method_config* method_config) { |
|
|
|
|
void grpc_method_config_unref(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_method_config *method_config) { |
|
|
|
|
if (grpc_slice_hash_table_unref(exec_ctx, method_config->table)) { |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, method_config->wait_for_ready_key); |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, method_config->timeout_key); |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, method_config->max_request_message_bytes_key); |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, method_config->max_response_message_bytes_key); |
|
|
|
|
gpr_free(method_config); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int grpc_method_config_cmp(const grpc_method_config* method_config1, |
|
|
|
|
const grpc_method_config* method_config2) { |
|
|
|
|
int grpc_method_config_cmp(const grpc_method_config *method_config1, |
|
|
|
|
const grpc_method_config *method_config2) { |
|
|
|
|
return grpc_slice_hash_table_cmp(method_config1->table, |
|
|
|
|
method_config2->table); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const bool* grpc_method_config_get_wait_for_ready( |
|
|
|
|
const grpc_method_config* method_config) { |
|
|
|
|
const bool *grpc_method_config_get_wait_for_ready( |
|
|
|
|
const grpc_method_config *method_config) { |
|
|
|
|
return grpc_slice_hash_table_get(method_config->table, |
|
|
|
|
method_config->wait_for_ready_key); |
|
|
|
|
GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const gpr_timespec* grpc_method_config_get_timeout( |
|
|
|
|
const grpc_method_config* method_config) { |
|
|
|
|
const gpr_timespec *grpc_method_config_get_timeout( |
|
|
|
|
const grpc_method_config *method_config) { |
|
|
|
|
return grpc_slice_hash_table_get(method_config->table, |
|
|
|
|
method_config->timeout_key); |
|
|
|
|
GRPC_MDSTR_GRPC_DOT_TIMEOUT); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const int32_t* grpc_method_config_get_max_request_message_bytes( |
|
|
|
|
const grpc_method_config* method_config) { |
|
|
|
|
const int32_t *grpc_method_config_get_max_request_message_bytes( |
|
|
|
|
const grpc_method_config *method_config) { |
|
|
|
|
return grpc_slice_hash_table_get( |
|
|
|
|
method_config->table, method_config->max_request_message_bytes_key); |
|
|
|
|
method_config->table, GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const int32_t* grpc_method_config_get_max_response_message_bytes( |
|
|
|
|
const grpc_method_config* method_config) { |
|
|
|
|
const int32_t *grpc_method_config_get_max_response_message_bytes( |
|
|
|
|
const grpc_method_config *method_config) { |
|
|
|
|
return grpc_slice_hash_table_get( |
|
|
|
|
method_config->table, method_config->max_response_message_bytes_key); |
|
|
|
|
method_config->table, GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// grpc_method_config_table
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static void method_config_unref(grpc_exec_ctx* exec_ctx, void* valuep) { |
|
|
|
|
static void method_config_unref(grpc_exec_ctx *exec_ctx, void *valuep) { |
|
|
|
|
grpc_method_config_unref(exec_ctx, valuep); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void* method_config_ref(void* valuep) { |
|
|
|
|
static void *method_config_ref(void *valuep) { |
|
|
|
|
return grpc_method_config_ref(valuep); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int method_config_cmp(void* valuep1, void* valuep2) { |
|
|
|
|
static int method_config_cmp(void *valuep1, void *valuep2) { |
|
|
|
|
return grpc_method_config_cmp(valuep1, valuep2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const grpc_slice_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_slice_hash_table_entry* hash_table_entries = |
|
|
|
|
grpc_method_config_table *grpc_method_config_table_create( |
|
|
|
|
size_t num_entries, grpc_method_config_table_entry *entries) { |
|
|
|
|
grpc_slice_hash_table_entry *hash_table_entries = |
|
|
|
|
gpr_malloc(sizeof(grpc_slice_hash_table_entry) * 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; |
|
|
|
|
} |
|
|
|
|
grpc_method_config_table* method_config_table = |
|
|
|
|
grpc_method_config_table *method_config_table = |
|
|
|
|
grpc_slice_hash_table_create(num_entries, hash_table_entries); |
|
|
|
|
gpr_free(hash_table_entries); |
|
|
|
|
return method_config_table; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_method_config_table* grpc_method_config_table_ref( |
|
|
|
|
grpc_method_config_table* table) { |
|
|
|
|
grpc_method_config_table *grpc_method_config_table_ref( |
|
|
|
|
grpc_method_config_table *table) { |
|
|
|
|
return grpc_slice_hash_table_ref(table); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_method_config_table_unref(grpc_exec_ctx* exec_ctx, |
|
|
|
|
grpc_method_config_table* table) { |
|
|
|
|
void grpc_method_config_table_unref(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_method_config_table *table) { |
|
|
|
|
grpc_slice_hash_table_unref(exec_ctx, table); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int grpc_method_config_table_cmp(const grpc_method_config_table* table1, |
|
|
|
|
const grpc_method_config_table* table2) { |
|
|
|
|
int grpc_method_config_table_cmp(const grpc_method_config_table *table1, |
|
|
|
|
const grpc_method_config_table *table2) { |
|
|
|
|
return grpc_slice_hash_table_cmp(table1, table2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx, |
|
|
|
|
const grpc_slice_hash_table* table, |
|
|
|
|
const grpc_slice path) { |
|
|
|
|
void* value = grpc_slice_hash_table_get(table, path); |
|
|
|
|
void *grpc_method_config_table_get(grpc_exec_ctx *exec_ctx, |
|
|
|
|
const grpc_slice_hash_table *table, |
|
|
|
|
const grpc_slice path) { |
|
|
|
|
void *value = grpc_slice_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 (value == NULL) { |
|
|
|
|
const char* path_str = grpc_mdstr_as_c_string(path); |
|
|
|
|
const char* sep = strrchr(path_str, '/') + 1; |
|
|
|
|
const size_t len = (size_t)(sep - path_str); |
|
|
|
|
char* buf = gpr_malloc(len + 2); // '*' and NUL
|
|
|
|
|
memcpy(buf, path_str, len); |
|
|
|
|
buf[len] = '*'; |
|
|
|
|
buf[len + 1] = '\0'; |
|
|
|
|
grpc_slice wildcard_path = grpc_mdstr_from_string(buf); |
|
|
|
|
gpr_free(buf); |
|
|
|
|
value = grpc_slice_hash_table_get(table, wildcard_path); |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, wildcard_path); |
|
|
|
|
int sep_pos = grpc_slice_rchr(path, '/') + 1; |
|
|
|
|
grpc_slice search = grpc_slice_malloc((size_t)(sep_pos + 1)); |
|
|
|
|
memcpy(GRPC_SLICE_START_PTR(search), GRPC_SLICE_START_PTR(path), |
|
|
|
|
(size_t)sep_pos); |
|
|
|
|
GRPC_SLICE_START_PTR(search)[sep_pos] = '*'; |
|
|
|
|
value = grpc_slice_hash_table_get(table, search); |
|
|
|
|
grpc_slice_unref_internal(exec_ctx, search); |
|
|
|
|
} |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); } |
|
|
|
|
static void *copy_arg(void *p) { return grpc_method_config_table_ref(p); } |
|
|
|
|
|
|
|
|
|
static void destroy_arg(grpc_exec_ctx* exec_ctx, void* p) { |
|
|
|
|
static void destroy_arg(grpc_exec_ctx *exec_ctx, void *p) { |
|
|
|
|
grpc_method_config_table_unref(exec_ctx, p); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int cmp_arg(void* p1, void* p2) { |
|
|
|
|
static int cmp_arg(void *p1, void *p2) { |
|
|
|
|
return grpc_method_config_table_cmp(p1, p2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg}; |
|
|
|
|
|
|
|
|
|
grpc_arg grpc_method_config_table_create_channel_arg( |
|
|
|
|
grpc_method_config_table* table) { |
|
|
|
|
grpc_method_config_table *table) { |
|
|
|
|
grpc_arg arg; |
|
|
|
|
arg.type = GRPC_ARG_POINTER; |
|
|
|
|
arg.key = GRPC_ARG_SERVICE_CONFIG; |
|
|
|
@ -304,27 +278,27 @@ grpc_arg grpc_method_config_table_create_channel_arg( |
|
|
|
|
|
|
|
|
|
// State used by convert_entry() below.
|
|
|
|
|
typedef struct conversion_state { |
|
|
|
|
void* (*convert_value)(const grpc_method_config* method_config); |
|
|
|
|
const grpc_slice_hash_table_vtable* vtable; |
|
|
|
|
void *(*convert_value)(const grpc_method_config *method_config); |
|
|
|
|
const grpc_slice_hash_table_vtable *vtable; |
|
|
|
|
size_t num_entries; |
|
|
|
|
grpc_slice_hash_table_entry* entries; |
|
|
|
|
grpc_slice_hash_table_entry *entries; |
|
|
|
|
} conversion_state; |
|
|
|
|
|
|
|
|
|
// A function to be passed to grpc_slice_hash_table_iterate() to create
|
|
|
|
|
// a copy of the entries.
|
|
|
|
|
static void convert_entry(const grpc_slice_hash_table_entry* entry, |
|
|
|
|
void* user_data) { |
|
|
|
|
conversion_state* state = user_data; |
|
|
|
|
state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key); |
|
|
|
|
static void convert_entry(const grpc_slice_hash_table_entry *entry, |
|
|
|
|
void *user_data) { |
|
|
|
|
conversion_state *state = user_data; |
|
|
|
|
state->entries[state->num_entries].key = grpc_slice_ref_internal(entry->key); |
|
|
|
|
state->entries[state->num_entries].value = state->convert_value(entry->value); |
|
|
|
|
state->entries[state->num_entries].vtable = state->vtable; |
|
|
|
|
++state->num_entries; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_slice_hash_table* grpc_method_config_table_convert( |
|
|
|
|
grpc_exec_ctx* exec_ctx, const grpc_method_config_table* table, |
|
|
|
|
void* (*convert_value)(const grpc_method_config* method_config), |
|
|
|
|
const grpc_slice_hash_table_vtable* vtable) { |
|
|
|
|
grpc_slice_hash_table *grpc_method_config_table_convert( |
|
|
|
|
grpc_exec_ctx *exec_ctx, const grpc_method_config_table *table, |
|
|
|
|
void *(*convert_value)(const grpc_method_config *method_config), |
|
|
|
|
const grpc_slice_hash_table_vtable *vtable) { |
|
|
|
|
// Create an array of the entries in the table with converted values.
|
|
|
|
|
conversion_state state; |
|
|
|
|
state.convert_value = convert_value; |
|
|
|
@ -334,11 +308,11 @@ grpc_slice_hash_table* grpc_method_config_table_convert( |
|
|
|
|
grpc_slice_hash_table_num_entries(table)); |
|
|
|
|
grpc_slice_hash_table_iterate(table, convert_entry, &state); |
|
|
|
|
// Create a new table based on the array we just constructed.
|
|
|
|
|
grpc_slice_hash_table* new_table = |
|
|
|
|
grpc_slice_hash_table *new_table = |
|
|
|
|
grpc_slice_hash_table_create(state.num_entries, state.entries); |
|
|
|
|
// Clean up the array.
|
|
|
|
|
for (size_t i = 0; i < state.num_entries; ++i) { |
|
|
|
|
GRPC_MDSTR_UNREF(exec_ctx, state.entries[i].key); |
|
|
|
|
grpc_slice_unref_internal(exec_ctx, state.entries[i].key); |
|
|
|
|
vtable->destroy_value(exec_ctx, state.entries[i].value); |
|
|
|
|
} |
|
|
|
|
gpr_free(state.entries); |
|
|
|
|