diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index be4c4d2b176a8700cdb5199bda2eedc1a75b6abb..1cf5b5513b3365d66d7245ce41306abbc0dbbdc1 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -77,6 +77,12 @@ typedef enum { GRPC_ARG_POINTER } grpc_arg_type; +typedef struct grpc_arg_pointer_vtable { + void *(*copy)(void *p); + void (*destroy)(void *p); + int (*cmp)(void *p, void *q); +} grpc_arg_pointer_vtable; + /** A single argument... each argument has a key and a value A note on naming keys: @@ -97,8 +103,7 @@ typedef struct { int integer; struct { void *p; - void *(*copy)(void *p); - void (*destroy)(void *p); + const grpc_arg_pointer_vtable *vtable; } pointer; } value; } grpc_arg; diff --git a/include/grpc/support/useful.h b/include/grpc/support/useful.h index 9f08d788c0d5de2d7fcec406c4883831872157f3..003e096cf9a43f96ae1838fb1a44a64576a63da9 100644 --- a/include/grpc/support/useful.h +++ b/include/grpc/support/useful.h @@ -72,4 +72,6 @@ 0x0f0f0f0f) % \ 255) +#define GPR_ICMP(a, b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0)) + #endif /* GRPC_SUPPORT_USEFUL_H */ diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c index 487db1119a5bf33ab473ef35538ad05c5bc07961..cd35d2f701c430957afcb6a6c5d035d522027a1b 100644 --- a/src/core/channel/channel_args.c +++ b/src/core/channel/channel_args.c @@ -54,9 +54,7 @@ static grpc_arg copy_arg(const grpc_arg *src) { break; case GRPC_ARG_POINTER: dst.value.pointer = src->value.pointer; - dst.value.pointer.p = src->value.pointer.copy - ? src->value.pointer.copy(src->value.pointer.p) - : src->value.pointer.p; + dst.value.pointer.p = src->value.pointer.vtable->copy(src->value.pointer.p); break; } return dst; @@ -93,6 +91,60 @@ grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, return grpc_channel_args_copy_and_add(a, b->args, b->num_args); } +static int cmp_arg(const grpc_arg *a, const grpc_arg *b) { + int c = a->type - b->type; + if (c != 0) return c; + c = strcmp(a->key, b->key); + if (c != 0) return c; + switch (a->type) { + case GRPC_ARG_STRING: + c = strcmp(a->value.string, b->value.string); + break; + case GRPC_ARG_INTEGER: + c = GPR_ICMP(a->value.integer, b->value.integer); + break; + case GRPC_ARG_POINTER: + c = GPR_ICMP(a->value.pointer.p, + b->value.pointer.p); + if (c != 0) { + c = GPR_ICMP(a->value.pointer.vtable, + b->value.pointer.vtable); + if (c == 0) { + c = a->value.pointer.vtable->cmp(a->value.pointer.p, + b->value.pointer.p); + } + } + break; + } + return c; +} + +static int cmp_key_stable(const void *ap, const void *bp) { + const grpc_arg *const *a = ap; + const grpc_arg *const *b = bp; + int c = strcmp((*a)->key, (*b)->key); + if (c == 0) c = GPR_ICMP(*a, *b); + return c; +} + +grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a) { + grpc_arg **args = gpr_malloc(sizeof(grpc_arg*) * a->num_args); + for (size_t i = 0; i < a->num_args; i++) { + args[i] = &a->args[i]; + } + qsort(args, a->num_args, sizeof(grpc_arg*), cmp_key_stable); + + grpc_channel_args *b = gpr_malloc(sizeof(grpc_channel_args)); + b->num_args = a->num_args; + b->args = gpr_malloc(sizeof(grpc_arg) * b->num_args); + for (size_t i = 0; i < a->num_args; i++) { + b->args[i] = copy_arg(args[i]); + } + + gpr_free(args); + return b; +} + void grpc_channel_args_destroy(grpc_channel_args *a) { size_t i; for (i = 0; i < a->num_args; i++) { @@ -103,9 +155,7 @@ void grpc_channel_args_destroy(grpc_channel_args *a) { case GRPC_ARG_INTEGER: break; case GRPC_ARG_POINTER: - if (a->args[i].value.pointer.destroy) { - a->args[i].value.pointer.destroy(a->args[i].value.pointer.p); - } + a->args[i].value.pointer.vtable->destroy(a->args[i].value.pointer.p); break; } gpr_free(a->args[i].key); @@ -207,3 +257,14 @@ int grpc_channel_args_compression_algorithm_get_states( return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */ } } + +int grpc_channel_args_compare(const grpc_channel_args *a, + const grpc_channel_args *b) { + int c = GPR_ICMP(a->num_args, b->num_args); + if (c != 0) return c; + for (size_t i = 0; i < a->num_args; i++) { + c = cmp_arg(&a->args[i], &b->args[i]); + if (c != 0) return c; + } + return 0; +} diff --git a/src/core/channel/channel_args.h b/src/core/channel/channel_args.h index 480cc9aec2fc0e0ae4b4c0667e5816a1168974d3..ce848782e3f8afeec0cc1a70595b5a62ad2f125c 100644 --- a/src/core/channel/channel_args.h +++ b/src/core/channel/channel_args.h @@ -40,6 +40,9 @@ /* Copy some arguments */ grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src); +/* Copy some arguments, stably sorting keys */ +grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a); + /** Copy some arguments and add the to_add parameter in the end. If to_add is NULL, it is equivalent to call grpc_channel_args_copy. */ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, @@ -85,4 +88,6 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( int grpc_channel_args_compression_algorithm_get_states( const grpc_channel_args *a); +int grpc_channel_args_compare(const grpc_channel_args *a, const grpc_channel_args *b); + #endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H */ diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 8b56c576458bbabd7073e850937fde9f76dfefa2..28f41b2041f23d94995ce28c4de18870fd70b6a3 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -196,14 +196,23 @@ static void *server_credentials_pointer_arg_copy(void *p) { return grpc_server_credentials_ref(p); } +static int server_credentials_pointer_cmp(void *a, void *b) { + return GPR_ICMP(a, b); +} + +static const grpc_arg_pointer_vtable cred_ptr_vtable = { + server_credentials_pointer_arg_copy, + server_credentials_pointer_arg_destroy, + server_credentials_pointer_cmp +}; + grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) { grpc_arg arg; memset(&arg, 0, sizeof(grpc_arg)); arg.type = GRPC_ARG_POINTER; arg.key = GRPC_SERVER_CREDENTIALS_ARG; arg.value.pointer.p = p; - arg.value.pointer.copy = server_credentials_pointer_arg_copy; - arg.value.pointer.destroy = server_credentials_pointer_arg_destroy; + arg.value.pointer.vtable = &cred_ptr_vtable; return arg; } diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index 61336a1057df088fc9bfcb740fe4e4de4628010c..40f486128b7d630350ccef9079c5031cd6cc493e 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -194,12 +194,21 @@ static void *connector_pointer_arg_copy(void *p) { return GRPC_SECURITY_CONNECTOR_REF(p, "connector_pointer_arg"); } +static int connector_pointer_cmp(void *a, void *b) { + return GPR_ICMP(a, b); +} + +static const grpc_arg_pointer_vtable connector_pointer_vtable = { + connector_pointer_arg_copy, + connector_pointer_arg_destroy, + connector_pointer_cmp +}; + grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) { grpc_arg result; result.type = GRPC_ARG_POINTER; result.key = GRPC_SECURITY_CONNECTOR_ARG; - result.value.pointer.destroy = connector_pointer_arg_destroy; - result.value.pointer.copy = connector_pointer_arg_copy; + result.value.pointer.vtable = &connector_pointer_vtable; result.value.pointer.p = sc; return result; } diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index 2068c97d78c22328708a1dd06b43b38a36829a6f..6e948f61bc38fd1aace14511a74b66db8f69e1ed 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -309,14 +309,23 @@ static void *auth_context_pointer_arg_copy(void *p) { return GRPC_AUTH_CONTEXT_REF(p, "auth_context_pointer_arg"); } +static int auth_context_pointer_cmp(void *a, void *b) { + return GPR_ICMP(a, b); +} + +static const grpc_arg_pointer_vtable auth_context_pointer_vtable = { + auth_context_pointer_arg_copy, + auth_context_pointer_arg_destroy, + auth_context_pointer_cmp +}; + grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) { grpc_arg arg; memset(&arg, 0, sizeof(grpc_arg)); arg.type = GRPC_ARG_POINTER; arg.key = GRPC_AUTH_CONTEXT_ARG; arg.value.pointer.p = p; - arg.value.pointer.copy = auth_context_pointer_arg_copy; - arg.value.pointer.destroy = auth_context_pointer_arg_destroy; + arg.value.pointer.vtable = &auth_context_pointer_vtable; return arg; } diff --git a/src/cpp/common/channel_arguments.cc b/src/cpp/common/channel_arguments.cc index 90cd5136af356ce2410da95e0959efee2e6d754e..3536e354287aafaf47275116048190a8f427e65b 100644 --- a/src/cpp/common/channel_arguments.cc +++ b/src/cpp/common/channel_arguments.cc @@ -62,9 +62,7 @@ ChannelArguments::ChannelArguments(const ChannelArguments& other) break; case GRPC_ARG_POINTER: ap.value.pointer = a->value.pointer; - ap.value.pointer.p = a->value.pointer.copy - ? a->value.pointer.copy(ap.value.pointer.p) - : ap.value.pointer.p; + ap.value.pointer.p = a->value.pointer.vtable->copy(ap.value.pointer.p); break; } args_.push_back(ap); @@ -92,13 +90,27 @@ void ChannelArguments::SetInt(const grpc::string& key, int value) { } void ChannelArguments::SetPointer(const grpc::string& key, void* value) { + struct VtableMembers { + static void* Copy(void* in) { return in; } + static void Destroy(void* in) {} + static int Compare(void* a, void *b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + } + }; + static const grpc_arg_pointer_vtable vtable = { + &VtableMembers::Copy, + &VtableMembers::Destroy, + &VtableMembers::Compare + }; + grpc_arg arg; arg.type = GRPC_ARG_POINTER; strings_.push_back(key); arg.key = const_cast<char*>(strings_.back().c_str()); arg.value.pointer.p = value; - arg.value.pointer.copy = nullptr; - arg.value.pointer.destroy = nullptr; + arg.value.pointer.vtable = &vtable; args_.push_back(arg); }