|  | /* | 
|  | * | 
|  | * Copyright 2015, Google Inc. | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions are | 
|  | * met: | 
|  | * | 
|  | *     * Redistributions of source code must retain the above copyright | 
|  | * notice, this list of conditions and the following disclaimer. | 
|  | *     * Redistributions in binary form must reproduce the above | 
|  | * copyright notice, this list of conditions and the following disclaimer | 
|  | * in the documentation and/or other materials provided with the | 
|  | * distribution. | 
|  | *     * Neither the name of Google Inc. nor the names of its | 
|  | * contributors may be used to endorse or promote products derived from | 
|  | * this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | * | 
|  | */ | 
|  |  | 
|  | // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. | 
|  |  | 
|  | // Because rust's union implementation is unstable and possibly buggy | 
|  | // (rust-lang/rust#32836), | 
|  | // so we need to wrap the type and expose more safer interfaces. | 
|  |  | 
|  | #include <grpc/support/port_platform.h> | 
|  |  | 
|  | #include <grpc/byte_buffer_reader.h> | 
|  | #include <grpc/grpc.h> | 
|  | #include <grpc/slice.h> | 
|  | #include <grpc/support/alloc.h> | 
|  | #include <grpc/support/log.h> | 
|  | #include <grpc/support/string_util.h> | 
|  | #include <grpc/support/thd_id.h> | 
|  |  | 
|  | #ifdef GRPC_SYS_SECURE | 
|  | #include <grpc/grpc_security.h> | 
|  | #endif | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #ifdef GPR_WINDOWS | 
|  | #define GPR_EXPORT extern "C" __declspec(dllexport) | 
|  | #define GPR_CALLTYPE __cdecl | 
|  | #endif | 
|  |  | 
|  | #ifndef GPR_EXPORT | 
|  | #define GPR_EXPORT extern "C" | 
|  | #endif | 
|  |  | 
|  | #ifndef GPR_CALLTYPE | 
|  | #define GPR_CALLTYPE | 
|  | #endif | 
|  |  | 
|  | grpc_byte_buffer* string_to_byte_buffer(const char* buffer, size_t len) { | 
|  | grpc_slice slice = grpc_slice_from_copied_buffer(buffer, len); | 
|  | grpc_byte_buffer* bb = grpc_raw_byte_buffer_create(&slice, 1); | 
|  | grpc_slice_unref(slice); | 
|  | return bb; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Helper to maintain lifetime of batch op inputs and store batch op outputs. | 
|  | */ | 
|  | typedef struct grpcwrap_batch_context { | 
|  | grpc_metadata_array send_initial_metadata; | 
|  | grpc_byte_buffer* send_message; | 
|  | struct { | 
|  | grpc_metadata_array trailing_metadata; | 
|  | } send_status_from_server; | 
|  | grpc_metadata_array recv_initial_metadata; | 
|  | grpc_byte_buffer* recv_message; | 
|  | struct { | 
|  | grpc_metadata_array trailing_metadata; | 
|  | grpc_status_code status; | 
|  | grpc_slice status_details; | 
|  | const char* error_string; | 
|  | } recv_status_on_client; | 
|  | int recv_close_on_server_cancelled; | 
|  | } grpcwrap_batch_context; | 
|  |  | 
|  | GPR_EXPORT grpcwrap_batch_context* GPR_CALLTYPE | 
|  | grpcwrap_batch_context_create() { | 
|  | auto* ctx = | 
|  | (grpcwrap_batch_context*)gpr_malloc(sizeof(grpcwrap_batch_context)); | 
|  | memset(ctx, 0, sizeof(grpcwrap_batch_context)); | 
|  | return ctx; | 
|  | } | 
|  |  | 
|  | typedef struct { | 
|  | grpc_call* call; | 
|  | grpc_call_details call_details; | 
|  | grpc_metadata_array request_metadata; | 
|  | } grpcwrap_request_call_context; | 
|  |  | 
|  | GPR_EXPORT grpcwrap_request_call_context* GPR_CALLTYPE | 
|  | grpcwrap_request_call_context_create() { | 
|  | auto* ctx = (grpcwrap_request_call_context*)gpr_malloc( | 
|  | sizeof(grpcwrap_request_call_context)); | 
|  | memset(ctx, 0, sizeof(grpcwrap_request_call_context)); | 
|  | return ctx; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Destroys array->metadata. | 
|  | * The array pointer itself is not freed. | 
|  | */ | 
|  | GPR_EXPORT void grpcwrap_metadata_array_destroy_metadata_only( | 
|  | grpc_metadata_array* array) { | 
|  | gpr_free(array->metadata); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Destroys keys, values and array->metadata. | 
|  | * The array pointer itself is not freed. | 
|  | */ | 
|  | GPR_EXPORT void grpcwrap_metadata_array_destroy_metadata_including_entries( | 
|  | grpc_metadata_array* array) { | 
|  | size_t i; | 
|  | if (array->metadata) { | 
|  | for (i = 0; i < array->count; i++) { | 
|  | grpc_slice_unref(array->metadata[i].key); | 
|  | grpc_slice_unref(array->metadata[i].value); | 
|  | } | 
|  | } | 
|  | gpr_free(array->metadata); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fully destroys the metadata array. | 
|  | */ | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_metadata_array_destroy_full(grpc_metadata_array* array) { | 
|  | if (!array) { | 
|  | return; | 
|  | } | 
|  | grpcwrap_metadata_array_destroy_metadata_including_entries(array); | 
|  | gpr_free(array); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Allocate metadata array with given capacity. | 
|  | */ | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_metadata_array_init(grpc_metadata_array* array, size_t capacity) { | 
|  | array->count = 0; | 
|  | array->capacity = capacity; | 
|  | if (!capacity) { | 
|  | array->metadata = nullptr; | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto* arr = (grpc_metadata*)gpr_malloc(sizeof(grpc_metadata) * capacity); | 
|  | memset(arr, 0, sizeof(grpc_metadata) * capacity); | 
|  | array->metadata = arr; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_metadata_array_add( | 
|  | grpc_metadata_array* array, const char* key, size_t key_length, | 
|  | const char* value, size_t value_length) { | 
|  | GPR_ASSERT(array->count <= array->capacity); | 
|  | size_t i = array->count; | 
|  | if (i == array->capacity) { | 
|  | array->capacity = array->capacity ? array->capacity * 2 : 4; | 
|  | array->metadata = (grpc_metadata*)gpr_realloc( | 
|  | array->metadata, array->capacity * sizeof(grpc_metadata)); | 
|  | memset(array->metadata + i, 0, | 
|  | sizeof(grpc_metadata) * (array->capacity - i)); | 
|  | } | 
|  | array->metadata[i].key = grpc_slice_from_copied_buffer(key, key_length); | 
|  | array->metadata[i].value = grpc_slice_from_copied_buffer(value, value_length); | 
|  | array->count++; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE grpcwrap_metadata_array_get_key( | 
|  | const grpc_metadata_array* array, size_t index, size_t* key_length) { | 
|  | GPR_ASSERT(index < array->count); | 
|  | *key_length = GRPC_SLICE_LENGTH(array->metadata[index].key); | 
|  | return (char*)GRPC_SLICE_START_PTR(array->metadata[index].key); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE grpcwrap_metadata_array_get_value( | 
|  | const grpc_metadata_array* array, size_t index, size_t* value_length) { | 
|  | GPR_ASSERT(index < array->count); | 
|  | *value_length = GRPC_SLICE_LENGTH(array->metadata[index].value); | 
|  | return (char*)GRPC_SLICE_START_PTR(array->metadata[index].value); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_metadata_array_cleanup(grpc_metadata_array* array) { | 
|  | grpcwrap_metadata_array_destroy_metadata_including_entries(array); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_metadata_array_shrink_to_fit(grpc_metadata_array* array) { | 
|  | GPR_ASSERT(array->count <= array->capacity); | 
|  | if (array->count == array->capacity) { | 
|  | return; | 
|  | } | 
|  | if (array->count) { | 
|  | array->metadata = (grpc_metadata*)gpr_realloc( | 
|  | array->metadata, array->count * sizeof(grpc_metadata)); | 
|  | array->capacity = array->count; | 
|  | } else { | 
|  | grpcwrap_metadata_array_cleanup(array); | 
|  | array->capacity = 0; | 
|  | array->metadata = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Move contents of metadata array */ | 
|  | GPR_EXPORT void grpcwrap_metadata_array_move(grpc_metadata_array* dest, | 
|  | grpc_metadata_array* src) { | 
|  | if (!src) { | 
|  | dest->capacity = 0; | 
|  | dest->count = 0; | 
|  | dest->metadata = nullptr; | 
|  | return; | 
|  | } | 
|  |  | 
|  | dest->capacity = src->capacity; | 
|  | dest->count = src->count; | 
|  | dest->metadata = src->metadata; | 
|  |  | 
|  | src->capacity = 0; | 
|  | src->count = 0; | 
|  | src->metadata = nullptr; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_batch_context_destroy(grpcwrap_batch_context* ctx) { | 
|  | if (!ctx) { | 
|  | return; | 
|  | } | 
|  | grpcwrap_metadata_array_destroy_metadata_including_entries( | 
|  | &(ctx->send_initial_metadata)); | 
|  |  | 
|  | grpc_byte_buffer_destroy(ctx->send_message); | 
|  |  | 
|  | grpcwrap_metadata_array_destroy_metadata_including_entries( | 
|  | &(ctx->send_status_from_server.trailing_metadata)); | 
|  |  | 
|  | grpcwrap_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata)); | 
|  |  | 
|  | grpc_byte_buffer_destroy(ctx->recv_message); | 
|  |  | 
|  | grpcwrap_metadata_array_destroy_metadata_only( | 
|  | &(ctx->recv_status_on_client.trailing_metadata)); | 
|  | grpc_slice_unref(ctx->recv_status_on_client.status_details); | 
|  | gpr_free((void*)ctx->recv_status_on_client.error_string); | 
|  |  | 
|  | gpr_free(ctx); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_request_call_context_destroy(grpcwrap_request_call_context* ctx) { | 
|  | if (!ctx) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (ctx->call) { | 
|  | grpc_call_unref(ctx->call); | 
|  | } | 
|  |  | 
|  | grpc_call_details_destroy(&(ctx->call_details)); | 
|  | grpcwrap_metadata_array_destroy_metadata_only(&(ctx->request_metadata)); | 
|  |  | 
|  | gpr_free(ctx); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_batch_context_take_recv_initial_metadata( | 
|  | grpcwrap_batch_context* ctx, grpc_metadata_array* res) { | 
|  | grpcwrap_metadata_array_move(res, &(ctx->recv_initial_metadata)); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_batch_context_take_recv_status_on_client_trailing_metadata( | 
|  | grpcwrap_batch_context* ctx, grpc_metadata_array* res) { | 
|  | grpcwrap_metadata_array_move(res, | 
|  | &(ctx->recv_status_on_client.trailing_metadata)); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE | 
|  | grpcwrap_slice_raw_offset(const grpc_slice* slice, size_t offset, size_t* len) { | 
|  | *len = GRPC_SLICE_LENGTH(*slice) - offset; | 
|  | return (const char*)(GRPC_SLICE_START_PTR(*slice)) + offset; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_slice GPR_CALLTYPE | 
|  | grpcwrap_slice_copy(const grpc_slice* slice) { | 
|  | return grpc_slice_copy(*slice); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_slice_unref(const grpc_slice* slice) { | 
|  | grpc_slice_unref(*slice); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_slice GPR_CALLTYPE grpcwrap_slice_ref(const grpc_slice* slice) { | 
|  | return grpc_slice_ref(*slice); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT size_t GPR_CALLTYPE grpcwrap_slice_length(const grpc_slice* slice) { | 
|  | return GRPC_SLICE_LENGTH(*slice); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_byte_buffer* GPR_CALLTYPE | 
|  | grpcwrap_batch_context_take_recv_message(grpcwrap_batch_context* ctx) { | 
|  | grpc_byte_buffer* buf = nullptr; | 
|  | if (ctx->recv_message) { | 
|  | buf = ctx->recv_message; | 
|  | ctx->recv_message = nullptr; | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_status_code GPR_CALLTYPE | 
|  | grpcwrap_batch_context_recv_status_on_client_status( | 
|  | const grpcwrap_batch_context* ctx) { | 
|  | return ctx->recv_status_on_client.status; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE | 
|  | grpcwrap_batch_context_recv_status_on_client_details( | 
|  | const grpcwrap_batch_context* ctx, size_t* details_length) { | 
|  | *details_length = | 
|  | GRPC_SLICE_LENGTH(ctx->recv_status_on_client.status_details); | 
|  | return (char*)GRPC_SLICE_START_PTR(ctx->recv_status_on_client.status_details); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE | 
|  | grpcwrap_batch_context_recv_status_on_client_trailing_metadata( | 
|  | const grpcwrap_batch_context* ctx) { | 
|  | return &(ctx->recv_status_on_client.trailing_metadata); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE | 
|  | grpcwrap_batch_context_recv_status_on_client_error_string( | 
|  | const grpcwrap_batch_context* ctx) { | 
|  | return ctx->recv_status_on_client.error_string; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call* GPR_CALLTYPE | 
|  | grpcwrap_request_call_context_ref_call(grpcwrap_request_call_context* ctx) { | 
|  | grpc_call* call = ctx->call; | 
|  | grpc_call_ref(call); | 
|  | return call; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call* GPR_CALLTYPE | 
|  | grpcwrap_request_call_context_get_call(grpcwrap_request_call_context* ctx) { | 
|  | return ctx->call; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE grpcwrap_request_call_context_method( | 
|  | const grpcwrap_request_call_context* ctx, size_t* method_length) { | 
|  | *method_length = GRPC_SLICE_LENGTH(ctx->call_details.method); | 
|  | return (char*)GRPC_SLICE_START_PTR(ctx->call_details.method); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const char* GPR_CALLTYPE grpcwrap_request_call_context_host( | 
|  | const grpcwrap_request_call_context* ctx, size_t* host_length) { | 
|  | *host_length = GRPC_SLICE_LENGTH(ctx->call_details.host); | 
|  | return (char*)GRPC_SLICE_START_PTR(ctx->call_details.host); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT gpr_timespec GPR_CALLTYPE grpcwrap_request_call_context_deadline( | 
|  | const grpcwrap_request_call_context* ctx) { | 
|  | return ctx->call_details.deadline; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE | 
|  | grpcwrap_request_call_context_metadata_array( | 
|  | const grpcwrap_request_call_context* ctx) { | 
|  | return &(ctx->request_metadata); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT int32_t GPR_CALLTYPE | 
|  | grpcwrap_batch_context_recv_close_on_server_cancelled( | 
|  | const grpcwrap_batch_context* ctx) { | 
|  | return (int32_t)ctx->recv_close_on_server_cancelled; | 
|  | } | 
|  |  | 
|  | /* Channel */ | 
|  |  | 
|  | GPR_EXPORT grpc_call* GPR_CALLTYPE grpcwrap_channel_create_call( | 
|  | grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, | 
|  | grpc_completion_queue* cq, const char* method, size_t method_len, | 
|  | const char* host, size_t host_len, gpr_timespec deadline) { | 
|  | grpc_slice method_slice = grpc_slice_from_copied_buffer(method, method_len); | 
|  | grpc_slice* host_slice_ptr = nullptr; | 
|  | grpc_slice host_slice; | 
|  | if (host != nullptr) { | 
|  | host_slice = grpc_slice_from_copied_buffer(host, host_len); | 
|  | host_slice_ptr = &host_slice; | 
|  | } else { | 
|  | // to silent msvc false warning | 
|  | host_slice = grpc_empty_slice(); | 
|  | } | 
|  | grpc_call* ret = | 
|  | grpc_channel_create_call(channel, parent_call, propagation_mask, cq, | 
|  | method_slice, host_slice_ptr, deadline, nullptr); | 
|  | grpc_slice_unref(method_slice); | 
|  | if (host != nullptr) { | 
|  | grpc_slice_unref(host_slice); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Channel args */ | 
|  |  | 
|  | GPR_EXPORT grpc_channel_args* GPR_CALLTYPE | 
|  | grpcwrap_channel_args_create(size_t num_args) { | 
|  | auto* args = (grpc_channel_args*)gpr_malloc(sizeof(grpc_channel_args)); | 
|  | memset(args, 0, sizeof(grpc_channel_args)); | 
|  |  | 
|  | args->num_args = num_args; | 
|  | args->args = (grpc_arg*)gpr_malloc(sizeof(grpc_arg) * num_args); | 
|  | memset(args->args, 0, sizeof(grpc_arg) * num_args); | 
|  | return args; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_channel_args_set_string( | 
|  | grpc_channel_args* args, size_t index, const char* key, const char* value) { | 
|  | GPR_ASSERT(args); | 
|  | GPR_ASSERT(index < args->num_args); | 
|  | args->args[index].type = GRPC_ARG_STRING; | 
|  | args->args[index].key = gpr_strdup(key); | 
|  | args->args[index].value.string = gpr_strdup(value); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_channel_args_set_integer( | 
|  | grpc_channel_args* args, size_t index, const char* key, int value) { | 
|  | GPR_ASSERT(args); | 
|  | GPR_ASSERT(index < args->num_args); | 
|  | args->args[index].type = GRPC_ARG_INTEGER; | 
|  | args->args[index].key = gpr_strdup(key); | 
|  | args->args[index].value.integer = value; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE grpcwrap_channel_args_set_pointer_vtable( | 
|  | grpc_channel_args* args, size_t index, const char* key, void* value, | 
|  | const grpc_arg_pointer_vtable* vtable) { | 
|  | GPR_ASSERT(args); | 
|  | GPR_ASSERT(index < args->num_args); | 
|  | args->args[index].type = GRPC_ARG_POINTER; | 
|  | args->args[index].key = gpr_strdup(key); | 
|  | args->args[index].value.pointer.p = vtable->copy(value); | 
|  | args->args[index].value.pointer.vtable = vtable; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT void GPR_CALLTYPE | 
|  | grpcwrap_channel_args_destroy(grpc_channel_args* args) { | 
|  | size_t i; | 
|  | if (args) { | 
|  | for (i = 0; i < args->num_args; i++) { | 
|  | gpr_free(args->args[i].key); | 
|  | if (args->args[i].type == GRPC_ARG_STRING) { | 
|  | gpr_free(args->args[i].value.string); | 
|  | } | 
|  | if (args->args[i].type == GRPC_ARG_POINTER) { | 
|  | args->args[i].value.pointer.vtable->destroy( | 
|  | args->args[i].value.pointer.p); | 
|  | } | 
|  | } | 
|  | gpr_free(args->args); | 
|  | gpr_free(args); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Call */ | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_start_unary( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, grpc_slice* send_buffer, | 
|  | uint32_t write_flags, grpc_metadata_array* initial_metadata, | 
|  | uint32_t initial_metadata_flags, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[6]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); | 
|  | ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; | 
|  | ops[0].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[0].flags = initial_metadata_flags; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | ops[1].op = GRPC_OP_SEND_MESSAGE; | 
|  | ctx->send_message = grpc_raw_byte_buffer_create(send_buffer, 1); | 
|  | ops[1].data.send_message.send_message = ctx->send_message; | 
|  | ops[1].flags = write_flags; | 
|  | ops[1].reserved = nullptr; | 
|  |  | 
|  | ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; | 
|  | ops[2].flags = 0; | 
|  | ops[2].reserved = nullptr; | 
|  |  | 
|  | ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; | 
|  | ops[3].data.recv_initial_metadata.recv_initial_metadata = | 
|  | &(ctx->recv_initial_metadata); | 
|  | ops[3].flags = 0; | 
|  | ops[3].reserved = nullptr; | 
|  |  | 
|  | ops[4].op = GRPC_OP_RECV_MESSAGE; | 
|  | ops[4].data.recv_message.recv_message = &(ctx->recv_message); | 
|  | ops[4].flags = 0; | 
|  | ops[4].reserved = nullptr; | 
|  |  | 
|  | ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT; | 
|  | ops[5].data.recv_status_on_client.trailing_metadata = | 
|  | &(ctx->recv_status_on_client.trailing_metadata); | 
|  | ops[5].data.recv_status_on_client.status = | 
|  | &(ctx->recv_status_on_client.status); | 
|  | ops[5].data.recv_status_on_client.status_details = | 
|  | &(ctx->recv_status_on_client.status_details); | 
|  | ops[5].data.recv_status_on_client.error_string = | 
|  | &(ctx->recv_status_on_client.error_string); | 
|  | ops[5].flags = 0; | 
|  | ops[5].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_start_client_streaming( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, | 
|  | grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags, | 
|  | void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[4]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); | 
|  | ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; | 
|  | ops[0].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[0].flags = initial_metadata_flags; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; | 
|  | ops[1].data.recv_initial_metadata.recv_initial_metadata = | 
|  | &(ctx->recv_initial_metadata); | 
|  | ops[1].flags = 0; | 
|  | ops[1].reserved = nullptr; | 
|  |  | 
|  | ops[2].op = GRPC_OP_RECV_MESSAGE; | 
|  | ops[2].data.recv_message.recv_message = &(ctx->recv_message); | 
|  | ops[2].flags = 0; | 
|  | ops[2].reserved = nullptr; | 
|  |  | 
|  | ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT; | 
|  | ops[3].data.recv_status_on_client.trailing_metadata = | 
|  | &(ctx->recv_status_on_client.trailing_metadata); | 
|  | ops[3].data.recv_status_on_client.status = | 
|  | &(ctx->recv_status_on_client.status); | 
|  | ops[3].data.recv_status_on_client.status_details = | 
|  | &(ctx->recv_status_on_client.status_details); | 
|  | ops[3].data.recv_status_on_client.error_string = | 
|  | &(ctx->recv_status_on_client.error_string); | 
|  | ops[3].flags = 0; | 
|  | ops[3].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_start_server_streaming( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, grpc_slice* send_buffer, | 
|  | uint32_t write_flags, grpc_metadata_array* initial_metadata, | 
|  | uint32_t initial_metadata_flags, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[4]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); | 
|  | ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; | 
|  | ops[0].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[0].flags = initial_metadata_flags; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | ops[1].op = GRPC_OP_SEND_MESSAGE; | 
|  | ctx->send_message = grpc_raw_byte_buffer_create(send_buffer, 1); | 
|  | ops[1].data.send_message.send_message = ctx->send_message; | 
|  | ops[1].flags = write_flags; | 
|  | ops[1].reserved = nullptr; | 
|  |  | 
|  | ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; | 
|  | ops[2].flags = 0; | 
|  | ops[2].reserved = nullptr; | 
|  |  | 
|  | ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT; | 
|  | ops[3].data.recv_status_on_client.trailing_metadata = | 
|  | &(ctx->recv_status_on_client.trailing_metadata); | 
|  | ops[3].data.recv_status_on_client.status = | 
|  | &(ctx->recv_status_on_client.status); | 
|  | ops[3].data.recv_status_on_client.status_details = | 
|  | &(ctx->recv_status_on_client.status_details); | 
|  | ops[3].data.recv_status_on_client.error_string = | 
|  | &(ctx->recv_status_on_client.error_string); | 
|  | ops[3].flags = 0; | 
|  | ops[3].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_start_duplex_streaming( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, | 
|  | grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags, | 
|  | void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[2]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); | 
|  | ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; | 
|  | ops[0].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[0].flags = initial_metadata_flags; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT; | 
|  | ops[1].data.recv_status_on_client.trailing_metadata = | 
|  | &(ctx->recv_status_on_client.trailing_metadata); | 
|  | ops[1].data.recv_status_on_client.status = | 
|  | &(ctx->recv_status_on_client.status); | 
|  | ops[1].data.recv_status_on_client.status_details = | 
|  | &(ctx->recv_status_on_client.status_details); | 
|  | ops[1].data.recv_status_on_client.error_string = | 
|  | &(ctx->recv_status_on_client.error_string); | 
|  | ops[1].flags = 0; | 
|  | ops[1].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_recv_initial_metadata( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[1]; | 
|  | ops[0].op = GRPC_OP_RECV_INITIAL_METADATA; | 
|  | ops[0].data.recv_initial_metadata.recv_initial_metadata = | 
|  | &(ctx->recv_initial_metadata); | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_send_message( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, grpc_slice* send_buffer, | 
|  | uint32_t write_flags, grpc_metadata_array* initial_metadata, | 
|  | uint32_t initial_metadata_flags, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[2]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | size_t nops = 1; | 
|  |  | 
|  | ops[0].op = GRPC_OP_SEND_MESSAGE; | 
|  | ctx->send_message = grpc_raw_byte_buffer_create(send_buffer, 1); | 
|  | ops[0].data.send_message.send_message = ctx->send_message; | 
|  | ops[0].flags = write_flags; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | if (initial_metadata) { | 
|  | ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), | 
|  | initial_metadata); | 
|  | ops[nops].data.send_initial_metadata.count = | 
|  | ctx->send_initial_metadata.count; | 
|  | ops[nops].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[nops].flags = initial_metadata_flags; | 
|  | ops[nops].reserved = nullptr; | 
|  | nops++; | 
|  | } | 
|  | return grpc_call_start_batch(call, ops, nops, tag, nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE | 
|  | grpcwrap_call_send_close_from_client(grpc_call* call, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[1]; | 
|  | ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_send_status_from_server( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, grpc_status_code status_code, | 
|  | const char* status_details, size_t status_details_len, | 
|  | grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags, | 
|  | grpc_metadata_array* trailing_metadata, grpc_slice* optional_send_buffer, | 
|  | uint32_t write_flags, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[3]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | size_t nops = 1; | 
|  |  | 
|  | grpc_slice status_details_slice = | 
|  | grpc_slice_from_copied_buffer(status_details, status_details_len); | 
|  |  | 
|  | ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER; | 
|  | ops[0].data.send_status_from_server.status = status_code; | 
|  | ops[0].data.send_status_from_server.status_details = &status_details_slice; | 
|  | grpcwrap_metadata_array_move( | 
|  | &(ctx->send_status_from_server.trailing_metadata), trailing_metadata); | 
|  | ops[0].data.send_status_from_server.trailing_metadata_count = | 
|  | ctx->send_status_from_server.trailing_metadata.count; | 
|  | ops[0].data.send_status_from_server.trailing_metadata = | 
|  | ctx->send_status_from_server.trailing_metadata.metadata; | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | if (optional_send_buffer) { | 
|  | ops[nops].op = GRPC_OP_SEND_MESSAGE; | 
|  | ctx->send_message = grpc_raw_byte_buffer_create(optional_send_buffer, 1); | 
|  | ops[nops].data.send_message.send_message = ctx->send_message; | 
|  | ops[nops].flags = write_flags; | 
|  | ops[nops].reserved = nullptr; | 
|  | nops++; | 
|  | } | 
|  | if (initial_metadata) { | 
|  | ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), | 
|  | initial_metadata); | 
|  | ops[nops].data.send_initial_metadata.count = | 
|  | ctx->send_initial_metadata.count; | 
|  | ops[nops].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[nops].flags = initial_metadata_flags; | 
|  | ops[nops].reserved = nullptr; | 
|  | nops++; | 
|  | } | 
|  |  | 
|  | grpc_call_error ret = grpc_call_start_batch(call, ops, nops, tag, nullptr); | 
|  | grpc_slice_unref(status_details_slice); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_recv_message( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[1]; | 
|  | ops[0].op = GRPC_OP_RECV_MESSAGE; | 
|  | ops[0].data.recv_message.recv_message = &(ctx->recv_message); | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_start_serverside( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[1]; | 
|  | ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER; | 
|  | ops[0].data.recv_close_on_server.cancelled = | 
|  | (&ctx->recv_close_on_server_cancelled); | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcwrap_call_send_initial_metadata( | 
|  | grpc_call* call, grpcwrap_batch_context* ctx, | 
|  | grpc_metadata_array* initial_metadata, void* tag) { | 
|  | /* TODO: don't use magic number */ | 
|  | grpc_op ops[1]; | 
|  | memset(ops, 0, sizeof(ops)); | 
|  | ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; | 
|  | grpcwrap_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); | 
|  | ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; | 
|  | ops[0].data.send_initial_metadata.metadata = | 
|  | ctx->send_initial_metadata.metadata; | 
|  | ops[0].flags = 0; | 
|  | ops[0].reserved = nullptr; | 
|  |  | 
|  | return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), tag, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | /** Kick call's completion queue, it should be called after there is an event | 
|  | ready to poll. | 
|  | THREAD SAFETY: grpcwrap_call_kick_completion_queue is thread-safe | 
|  | because it does not change the call's state. */ | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE | 
|  | grpcwrap_call_kick_completion_queue(grpc_call* call, void* tag) { | 
|  | // Empty batch grpc_op kicks call's completion queue immediately. | 
|  | return grpc_call_start_batch(call, nullptr, 0, tag, nullptr); | 
|  | } | 
|  |  | 
|  | /* Server */ | 
|  |  | 
|  | GPR_EXPORT grpc_call_error GPR_CALLTYPE | 
|  | grpcwrap_server_request_call(grpc_server* server, grpc_completion_queue* cq, | 
|  | grpcwrap_request_call_context* ctx, void* tag) { | 
|  | return grpc_server_request_call(server, &(ctx->call), &(ctx->call_details), | 
|  | &(ctx->request_metadata), cq, cq, tag); | 
|  | } |