3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
23 #include <grpc/byte_buffer_reader.h>
24 #include <grpc/grpc.h>
25 #include <grpc/grpc_security.h>
26 #include <grpc/slice.h>
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/log.h>
29 #include <grpc/support/string_util.h>
30 #include <grpc/support/thd_id.h>
33 #define GPR_EXPORT __declspec(dllexport)
34 #define GPR_CALLTYPE __stdcall
45 static grpc_byte_buffer* grpcsharp_create_byte_buffer_from_stolen_slices(
46 grpc_slice_buffer* slice_buffer) {
47 grpc_byte_buffer* bb =
48 (grpc_byte_buffer*)gpr_zalloc(sizeof(grpc_byte_buffer));
49 bb->type = GRPC_BB_RAW;
50 bb->data.raw.compression = GRPC_COMPRESS_NONE;
51 grpc_slice_buffer_init(&bb->data.raw.slice_buffer);
53 grpc_slice_buffer_swap(&bb->data.raw.slice_buffer, slice_buffer);
58 * Helper to maintain lifetime of batch op inputs and store batch op outputs.
60 typedef struct grpcsharp_batch_context {
61 grpc_metadata_array send_initial_metadata;
62 grpc_byte_buffer* send_message;
64 grpc_metadata_array trailing_metadata;
65 } send_status_from_server;
66 grpc_metadata_array recv_initial_metadata;
67 grpc_byte_buffer* recv_message;
68 grpc_byte_buffer_reader* recv_message_reader;
70 grpc_metadata_array trailing_metadata;
71 grpc_status_code status;
72 grpc_slice status_details;
73 const char* error_string;
74 } recv_status_on_client;
75 int recv_close_on_server_cancelled;
77 /* reserve space for byte_buffer_reader */
78 grpc_byte_buffer_reader reserved_recv_message_reader;
79 } grpcsharp_batch_context;
81 GPR_EXPORT grpcsharp_batch_context* GPR_CALLTYPE
82 grpcsharp_batch_context_create() {
83 grpcsharp_batch_context* ctx = gpr_malloc(sizeof(grpcsharp_batch_context));
84 memset(ctx, 0, sizeof(grpcsharp_batch_context));
90 grpc_call_details call_details;
91 grpc_metadata_array request_metadata;
92 } grpcsharp_request_call_context;
94 GPR_EXPORT grpcsharp_request_call_context* GPR_CALLTYPE
95 grpcsharp_request_call_context_create() {
96 grpcsharp_request_call_context* ctx =
97 gpr_malloc(sizeof(grpcsharp_request_call_context));
98 memset(ctx, 0, sizeof(grpcsharp_request_call_context));
103 * Destroys array->metadata.
104 * The array pointer itself is not freed.
106 void grpcsharp_metadata_array_destroy_metadata_only(
107 grpc_metadata_array* array) {
108 gpr_free(array->metadata);
112 * Destroys keys, values and array->metadata.
113 * The array pointer itself is not freed.
115 void grpcsharp_metadata_array_destroy_metadata_including_entries(
116 grpc_metadata_array* array) {
118 if (array->metadata) {
119 for (i = 0; i < array->count; i++) {
120 grpc_slice_unref(array->metadata[i].key);
121 grpc_slice_unref(array->metadata[i].value);
124 gpr_free(array->metadata);
128 * Fully destroys the metadata array.
130 GPR_EXPORT void GPR_CALLTYPE
131 grpcsharp_metadata_array_destroy_full(grpc_metadata_array* array) {
135 grpcsharp_metadata_array_destroy_metadata_including_entries(array);
140 * Creates an empty metadata array with given capacity.
141 * Array can later be destroyed by grpc_metadata_array_destroy_full.
143 GPR_EXPORT grpc_metadata_array* GPR_CALLTYPE
144 grpcsharp_metadata_array_create(size_t capacity) {
145 grpc_metadata_array* array =
146 (grpc_metadata_array*)gpr_malloc(sizeof(grpc_metadata_array));
147 grpc_metadata_array_init(array);
148 array->capacity = capacity;
152 (grpc_metadata*)gpr_malloc(sizeof(grpc_metadata) * capacity);
153 memset(array->metadata, 0, sizeof(grpc_metadata) * capacity);
155 array->metadata = NULL;
160 GPR_EXPORT void GPR_CALLTYPE
161 grpcsharp_metadata_array_add(grpc_metadata_array* array, const char* key,
162 const char* value, size_t value_length) {
163 size_t i = array->count;
164 GPR_ASSERT(array->count < array->capacity);
165 array->metadata[i].key = grpc_slice_from_copied_string(key);
166 array->metadata[i].value = grpc_slice_from_copied_buffer(value, value_length);
170 GPR_EXPORT intptr_t GPR_CALLTYPE
171 grpcsharp_metadata_array_count(grpc_metadata_array* array) {
172 return (intptr_t)array->count;
175 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_metadata_array_get_key(
176 grpc_metadata_array* array, size_t index, size_t* key_length) {
177 GPR_ASSERT(index < array->count);
178 *key_length = GRPC_SLICE_LENGTH(array->metadata[index].key);
179 return (char*)GRPC_SLICE_START_PTR(array->metadata[index].key);
182 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_metadata_array_get_value(
183 grpc_metadata_array* array, size_t index, size_t* value_length) {
184 GPR_ASSERT(index < array->count);
185 *value_length = GRPC_SLICE_LENGTH(array->metadata[index].value);
186 return (char*)GRPC_SLICE_START_PTR(array->metadata[index].value);
189 /* Move contents of metadata array */
190 void grpcsharp_metadata_array_move(grpc_metadata_array* dest,
191 grpc_metadata_array* src) {
195 dest->metadata = NULL;
199 dest->capacity = src->capacity;
200 dest->count = src->count;
201 dest->metadata = src->metadata;
205 src->metadata = NULL;
208 GPR_EXPORT void GPR_CALLTYPE
209 grpcsharp_batch_context_reset(grpcsharp_batch_context* ctx) {
210 grpcsharp_metadata_array_destroy_metadata_including_entries(
211 &(ctx->send_initial_metadata));
213 grpc_byte_buffer_destroy(ctx->send_message);
215 grpcsharp_metadata_array_destroy_metadata_including_entries(
216 &(ctx->send_status_from_server.trailing_metadata));
218 grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata));
220 if (ctx->recv_message_reader) {
221 grpc_byte_buffer_reader_destroy(ctx->recv_message_reader);
223 grpc_byte_buffer_destroy(ctx->recv_message);
225 grpcsharp_metadata_array_destroy_metadata_only(
226 &(ctx->recv_status_on_client.trailing_metadata));
227 grpc_slice_unref(ctx->recv_status_on_client.status_details);
228 gpr_free((void*)ctx->recv_status_on_client.error_string);
229 memset(ctx, 0, sizeof(grpcsharp_batch_context));
232 GPR_EXPORT void GPR_CALLTYPE
233 grpcsharp_batch_context_destroy(grpcsharp_batch_context* ctx) {
237 grpcsharp_batch_context_reset(ctx);
241 GPR_EXPORT void GPR_CALLTYPE
242 grpcsharp_request_call_context_reset(grpcsharp_request_call_context* ctx) {
243 /* NOTE: ctx->server_rpc_new.call is not destroyed because callback handler is
245 to take its ownership. */
247 grpc_call_details_destroy(&(ctx->call_details));
248 grpcsharp_metadata_array_destroy_metadata_only(&(ctx->request_metadata));
249 memset(ctx, 0, sizeof(grpcsharp_request_call_context));
252 GPR_EXPORT void GPR_CALLTYPE
253 grpcsharp_request_call_context_destroy(grpcsharp_request_call_context* ctx) {
257 grpcsharp_request_call_context_reset(ctx);
261 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
262 grpcsharp_batch_context_recv_initial_metadata(
263 const grpcsharp_batch_context* ctx) {
264 return &(ctx->recv_initial_metadata);
267 GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_batch_context_recv_message_length(
268 const grpcsharp_batch_context* ctx) {
269 grpc_byte_buffer_reader reader;
270 if (!ctx->recv_message) {
274 GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message));
275 intptr_t result = (intptr_t)grpc_byte_buffer_length(reader.buffer_out);
276 grpc_byte_buffer_reader_destroy(&reader);
282 * Gets the next slice from recv_message byte buffer.
283 * Returns 1 if a slice was get successfully, 0 if there are no more slices to
284 * read. Set slice_len to the length of the slice and the slice_data_ptr to
285 * point to slice's data. Caller must ensure that the byte buffer being read
286 * from stays alive as long as the data of the slice are being accessed
287 * (grpc_byte_buffer_reader_peek method is used internally)
290 * Slices can only be iterated once.
291 * Initializes recv_message_buffer_reader if it was not initialized yet.
293 GPR_EXPORT int GPR_CALLTYPE
294 grpcsharp_batch_context_recv_message_next_slice_peek(
295 grpcsharp_batch_context* ctx, size_t* slice_len, uint8_t** slice_data_ptr) {
297 *slice_data_ptr = NULL;
299 if (!ctx->recv_message) {
303 if (!ctx->recv_message_reader) {
304 ctx->recv_message_reader = &ctx->reserved_recv_message_reader;
305 GPR_ASSERT(grpc_byte_buffer_reader_init(ctx->recv_message_reader,
309 grpc_slice* slice_ptr;
310 if (!grpc_byte_buffer_reader_peek(ctx->recv_message_reader, &slice_ptr)) {
314 /* recv_message buffer must not be deleted before all the data is read */
315 *slice_len = GRPC_SLICE_LENGTH(*slice_ptr);
316 *slice_data_ptr = GRPC_SLICE_START_PTR(*slice_ptr);
320 GPR_EXPORT grpc_status_code GPR_CALLTYPE
321 grpcsharp_batch_context_recv_status_on_client_status(
322 const grpcsharp_batch_context* ctx) {
323 return ctx->recv_status_on_client.status;
326 GPR_EXPORT const char* GPR_CALLTYPE
327 grpcsharp_batch_context_recv_status_on_client_details(
328 const grpcsharp_batch_context* ctx, size_t* details_length) {
330 GRPC_SLICE_LENGTH(ctx->recv_status_on_client.status_details);
331 return (char*)GRPC_SLICE_START_PTR(ctx->recv_status_on_client.status_details);
334 GPR_EXPORT const char* GPR_CALLTYPE
335 grpcsharp_batch_context_recv_status_on_client_error_string(
336 const grpcsharp_batch_context* ctx) {
337 return ctx->recv_status_on_client.error_string;
340 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
341 grpcsharp_batch_context_recv_status_on_client_trailing_metadata(
342 const grpcsharp_batch_context* ctx) {
343 return &(ctx->recv_status_on_client.trailing_metadata);
346 GPR_EXPORT grpc_call* GPR_CALLTYPE
347 grpcsharp_request_call_context_call(const grpcsharp_request_call_context* ctx) {
351 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_request_call_context_method(
352 const grpcsharp_request_call_context* ctx, size_t* method_length) {
353 *method_length = GRPC_SLICE_LENGTH(ctx->call_details.method);
354 return (char*)GRPC_SLICE_START_PTR(ctx->call_details.method);
357 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_request_call_context_host(
358 const grpcsharp_request_call_context* ctx, size_t* host_length) {
359 *host_length = GRPC_SLICE_LENGTH(ctx->call_details.host);
360 return (char*)GRPC_SLICE_START_PTR(ctx->call_details.host);
363 GPR_EXPORT gpr_timespec GPR_CALLTYPE grpcsharp_request_call_context_deadline(
364 const grpcsharp_request_call_context* ctx) {
365 return ctx->call_details.deadline;
368 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
369 grpcsharp_request_call_context_request_metadata(
370 const grpcsharp_request_call_context* ctx) {
371 return &(ctx->request_metadata);
374 GPR_EXPORT int32_t GPR_CALLTYPE
375 grpcsharp_batch_context_recv_close_on_server_cancelled(
376 const grpcsharp_batch_context* ctx) {
377 return (int32_t)ctx->recv_close_on_server_cancelled;
380 /* Init & shutdown */
382 GPR_EXPORT void GPR_CALLTYPE grpcsharp_init(void) { grpc_init(); }
384 GPR_EXPORT void GPR_CALLTYPE grpcsharp_shutdown(void) { grpc_shutdown(); }
386 /* Completion queue */
388 GPR_EXPORT grpc_completion_queue* GPR_CALLTYPE
389 grpcsharp_completion_queue_create_async(void) {
390 return grpc_completion_queue_create_for_next(NULL);
393 GPR_EXPORT grpc_completion_queue* GPR_CALLTYPE
394 grpcsharp_completion_queue_create_sync(void) {
395 return grpc_completion_queue_create_for_pluck(NULL);
398 GPR_EXPORT void GPR_CALLTYPE
399 grpcsharp_completion_queue_shutdown(grpc_completion_queue* cq) {
400 grpc_completion_queue_shutdown(cq);
403 GPR_EXPORT void GPR_CALLTYPE
404 grpcsharp_completion_queue_destroy(grpc_completion_queue* cq) {
405 grpc_completion_queue_destroy(cq);
408 GPR_EXPORT grpc_event GPR_CALLTYPE
409 grpcsharp_completion_queue_next(grpc_completion_queue* cq) {
410 return grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME),
414 GPR_EXPORT grpc_event GPR_CALLTYPE
415 grpcsharp_completion_queue_pluck(grpc_completion_queue* cq, void* tag) {
416 return grpc_completion_queue_pluck(cq, tag,
417 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
422 GPR_EXPORT grpc_channel* GPR_CALLTYPE
424 grpcsharp_insecure_channel_create(const char* target,
425 const grpc_channel_args* args) {
426 return grpc_insecure_channel_create(target, args, NULL);
429 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel* channel) {
430 grpc_channel_destroy(channel);
433 GPR_EXPORT grpc_call* GPR_CALLTYPE grpcsharp_channel_create_call(
434 grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask,
435 grpc_completion_queue* cq, const char* method, const char* host,
436 gpr_timespec deadline) {
437 grpc_slice method_slice = grpc_slice_from_copied_string(method);
438 grpc_slice* host_slice_ptr = NULL;
439 grpc_slice host_slice;
441 host_slice = grpc_slice_from_copied_string(host);
442 host_slice_ptr = &host_slice;
445 grpc_channel_create_call(channel, parent_call, propagation_mask, cq,
446 method_slice, host_slice_ptr, deadline, NULL);
447 grpc_slice_unref(method_slice);
449 grpc_slice_unref(host_slice);
454 GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE
455 grpcsharp_channel_check_connectivity_state(grpc_channel* channel,
456 int32_t try_to_connect) {
457 return grpc_channel_check_connectivity_state(channel, try_to_connect);
460 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state(
461 grpc_channel* channel, grpc_connectivity_state last_observed_state,
462 gpr_timespec deadline, grpc_completion_queue* cq,
463 grpcsharp_batch_context* ctx) {
464 grpc_channel_watch_connectivity_state(channel, last_observed_state, deadline,
468 GPR_EXPORT char* GPR_CALLTYPE
469 grpcsharp_channel_get_target(grpc_channel* channel) {
470 return grpc_channel_get_target(channel);
475 GPR_EXPORT grpc_channel_args* GPR_CALLTYPE
476 grpcsharp_channel_args_create(size_t num_args) {
477 grpc_channel_args* args =
478 (grpc_channel_args*)gpr_malloc(sizeof(grpc_channel_args));
479 memset(args, 0, sizeof(grpc_channel_args));
481 args->num_args = num_args;
482 args->args = (grpc_arg*)gpr_malloc(sizeof(grpc_arg) * num_args);
483 memset(args->args, 0, sizeof(grpc_arg) * num_args);
487 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_args_set_string(
488 grpc_channel_args* args, size_t index, const char* key, const char* value) {
490 GPR_ASSERT(index < args->num_args);
491 args->args[index].type = GRPC_ARG_STRING;
492 args->args[index].key = gpr_strdup(key);
493 args->args[index].value.string = gpr_strdup(value);
496 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_args_set_integer(
497 grpc_channel_args* args, size_t index, const char* key, int value) {
499 GPR_ASSERT(index < args->num_args);
500 args->args[index].type = GRPC_ARG_INTEGER;
501 args->args[index].key = gpr_strdup(key);
502 args->args[index].value.integer = value;
505 GPR_EXPORT void GPR_CALLTYPE
506 grpcsharp_channel_args_destroy(grpc_channel_args* args) {
509 for (i = 0; i < args->num_args; i++) {
510 gpr_free(args->args[i].key);
511 if (args->args[i].type == GRPC_ARG_STRING) {
512 gpr_free(args->args[i].value.string);
515 gpr_free(args->args);
522 GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(gpr_clock_type clock_type) {
523 return gpr_now(clock_type);
526 GPR_EXPORT gpr_timespec GPR_CALLTYPE
527 gprsharp_inf_future(gpr_clock_type clock_type) {
528 return gpr_inf_future(clock_type);
531 GPR_EXPORT gpr_timespec GPR_CALLTYPE
532 gprsharp_inf_past(gpr_clock_type clock_type) {
533 return gpr_inf_past(clock_type);
536 GPR_EXPORT gpr_timespec GPR_CALLTYPE
537 gprsharp_convert_clock_type(gpr_timespec t, gpr_clock_type target_clock) {
538 return gpr_convert_clock_type(t, target_clock);
541 GPR_EXPORT int32_t GPR_CALLTYPE gprsharp_sizeof_timespec(void) {
542 return sizeof(gpr_timespec);
547 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel(grpc_call* call) {
548 return grpc_call_cancel(call, NULL);
551 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel_with_status(
552 grpc_call* call, grpc_status_code status, const char* description) {
553 return grpc_call_cancel_with_status(call, status, description, NULL);
556 GPR_EXPORT char* GPR_CALLTYPE grpcsharp_call_get_peer(grpc_call* call) {
557 return grpc_call_get_peer(call);
560 GPR_EXPORT void GPR_CALLTYPE gprsharp_free(void* p) { gpr_free(p); }
562 GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call* call) {
563 grpc_call_unref(call);
566 typedef grpc_call_error (*grpcsharp_call_start_batch_func)(grpc_call* call,
572 /* Only for testing */
573 static grpc_call_error grpcsharp_call_start_batch_nop(grpc_call* call,
575 size_t nops, void* tag,
585 static grpc_call_error grpcsharp_call_start_batch_default(grpc_call* call,
590 return grpc_call_start_batch(call, ops, nops, tag, reserved);
593 static grpcsharp_call_start_batch_func g_call_start_batch_func =
594 grpcsharp_call_start_batch_default;
596 static grpc_call_error grpcsharp_call_start_batch(grpc_call* call,
598 size_t nops, void* tag,
600 return g_call_start_batch_func(call, ops, nops, tag, reserved);
603 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
604 grpc_call* call, grpcsharp_batch_context* ctx,
605 grpc_slice_buffer* send_buffer, uint32_t write_flags,
606 grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
607 /* TODO: don't use magic number */
609 memset(ops, 0, sizeof(ops));
610 ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
611 grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
613 ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
614 ops[0].data.send_initial_metadata.metadata =
615 ctx->send_initial_metadata.metadata;
616 ops[0].flags = initial_metadata_flags;
617 ops[0].reserved = NULL;
619 ops[1].op = GRPC_OP_SEND_MESSAGE;
621 grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
622 ops[1].data.send_message.send_message = ctx->send_message;
623 ops[1].flags = write_flags;
624 ops[1].reserved = NULL;
626 ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
628 ops[2].reserved = NULL;
630 ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
631 ops[3].data.recv_initial_metadata.recv_initial_metadata =
632 &(ctx->recv_initial_metadata);
634 ops[3].reserved = NULL;
636 ops[4].op = GRPC_OP_RECV_MESSAGE;
637 ops[4].data.recv_message.recv_message = &(ctx->recv_message);
639 ops[4].reserved = NULL;
641 ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
642 ops[5].data.recv_status_on_client.trailing_metadata =
643 &(ctx->recv_status_on_client.trailing_metadata);
644 ops[5].data.recv_status_on_client.status =
645 &(ctx->recv_status_on_client.status);
646 ops[5].data.recv_status_on_client.status_details =
647 &(ctx->recv_status_on_client.status_details);
648 ops[5].data.recv_status_on_client.error_string =
649 &(ctx->recv_status_on_client.error_string);
651 ops[5].reserved = NULL;
653 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
657 /* Only for testing. Shortcircuits the unary call logic and only echoes the
658 message as if it was received from the server */
659 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_test_call_start_unary_echo(
660 grpc_call* call, grpcsharp_batch_context* ctx,
661 grpc_slice_buffer* send_buffer, uint32_t write_flags,
662 grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
665 (void)initial_metadata_flags;
666 // prepare as if we were performing a normal RPC.
667 grpc_byte_buffer* send_message =
668 grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
670 ctx->recv_message = send_message; // echo message sent by the client as if
671 // received from server.
672 ctx->recv_status_on_client.status = GRPC_STATUS_OK;
673 ctx->recv_status_on_client.status_details = grpc_empty_slice();
674 ctx->recv_status_on_client.error_string = NULL;
675 // echo initial metadata as if received from server (as trailing metadata)
676 grpcsharp_metadata_array_move(&(ctx->recv_status_on_client.trailing_metadata),
681 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_client_streaming(
682 grpc_call* call, grpcsharp_batch_context* ctx,
683 grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
684 /* TODO: don't use magic number */
686 memset(ops, 0, sizeof(ops));
687 ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
688 grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
690 ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
691 ops[0].data.send_initial_metadata.metadata =
692 ctx->send_initial_metadata.metadata;
693 ops[0].flags = initial_metadata_flags;
694 ops[0].reserved = NULL;
696 ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
697 ops[1].data.recv_initial_metadata.recv_initial_metadata =
698 &(ctx->recv_initial_metadata);
700 ops[1].reserved = NULL;
702 ops[2].op = GRPC_OP_RECV_MESSAGE;
703 ops[2].data.recv_message.recv_message = &(ctx->recv_message);
705 ops[2].reserved = NULL;
707 ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
708 ops[3].data.recv_status_on_client.trailing_metadata =
709 &(ctx->recv_status_on_client.trailing_metadata);
710 ops[3].data.recv_status_on_client.status =
711 &(ctx->recv_status_on_client.status);
712 ops[3].data.recv_status_on_client.status_details =
713 &(ctx->recv_status_on_client.status_details);
714 ops[3].data.recv_status_on_client.error_string =
715 &(ctx->recv_status_on_client.error_string);
717 ops[3].reserved = NULL;
719 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
723 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
724 grpc_call* call, grpcsharp_batch_context* ctx,
725 grpc_slice_buffer* send_buffer, uint32_t write_flags,
726 grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
727 /* TODO: don't use magic number */
729 memset(ops, 0, sizeof(ops));
730 ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
731 grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
733 ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
734 ops[0].data.send_initial_metadata.metadata =
735 ctx->send_initial_metadata.metadata;
736 ops[0].flags = initial_metadata_flags;
737 ops[0].reserved = NULL;
739 ops[1].op = GRPC_OP_SEND_MESSAGE;
741 grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
742 ops[1].data.send_message.send_message = ctx->send_message;
743 ops[1].flags = write_flags;
744 ops[1].reserved = NULL;
746 ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
748 ops[2].reserved = NULL;
750 ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
751 ops[3].data.recv_status_on_client.trailing_metadata =
752 &(ctx->recv_status_on_client.trailing_metadata);
753 ops[3].data.recv_status_on_client.status =
754 &(ctx->recv_status_on_client.status);
755 ops[3].data.recv_status_on_client.status_details =
756 &(ctx->recv_status_on_client.status_details);
757 ops[3].data.recv_status_on_client.error_string =
758 &(ctx->recv_status_on_client.error_string);
760 ops[3].reserved = NULL;
762 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
766 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_duplex_streaming(
767 grpc_call* call, grpcsharp_batch_context* ctx,
768 grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
769 /* TODO: don't use magic number */
771 memset(ops, 0, sizeof(ops));
772 ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
773 grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
775 ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
776 ops[0].data.send_initial_metadata.metadata =
777 ctx->send_initial_metadata.metadata;
778 ops[0].flags = initial_metadata_flags;
779 ops[0].reserved = NULL;
781 ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
782 ops[1].data.recv_status_on_client.trailing_metadata =
783 &(ctx->recv_status_on_client.trailing_metadata);
784 ops[1].data.recv_status_on_client.status =
785 &(ctx->recv_status_on_client.status);
786 ops[1].data.recv_status_on_client.status_details =
787 &(ctx->recv_status_on_client.status_details);
788 ops[1].data.recv_status_on_client.error_string =
789 &(ctx->recv_status_on_client.error_string);
791 ops[1].reserved = NULL;
793 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
797 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata(
798 grpc_call* call, grpcsharp_batch_context* ctx) {
799 /* TODO: don't use magic number */
801 ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
802 ops[0].data.recv_initial_metadata.recv_initial_metadata =
803 &(ctx->recv_initial_metadata);
805 ops[0].reserved = NULL;
807 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
811 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_message(
812 grpc_call* call, grpcsharp_batch_context* ctx,
813 grpc_slice_buffer* send_buffer, uint32_t write_flags,
814 int32_t send_empty_initial_metadata) {
815 /* TODO: don't use magic number */
817 memset(ops, 0, sizeof(ops));
818 size_t nops = send_empty_initial_metadata ? 2 : 1;
819 ops[0].op = GRPC_OP_SEND_MESSAGE;
821 grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
822 ops[0].data.send_message.send_message = ctx->send_message;
823 ops[0].flags = write_flags;
824 ops[0].reserved = NULL;
825 ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
827 ops[1].reserved = NULL;
829 return grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
832 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_close_from_client(
833 grpc_call* call, grpcsharp_batch_context* ctx) {
834 /* TODO: don't use magic number */
836 ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
838 ops[0].reserved = NULL;
840 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
844 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
845 grpc_call* call, grpcsharp_batch_context* ctx, grpc_status_code status_code,
846 const char* status_details, size_t status_details_len,
847 grpc_metadata_array* trailing_metadata, int32_t send_empty_initial_metadata,
848 grpc_slice_buffer* optional_send_buffer, uint32_t write_flags) {
849 /* TODO: don't use magic number */
851 memset(ops, 0, sizeof(ops));
853 grpc_slice status_details_slice =
854 grpc_slice_from_copied_buffer(status_details, status_details_len);
855 ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
856 ops[0].data.send_status_from_server.status = status_code;
857 ops[0].data.send_status_from_server.status_details = &status_details_slice;
858 grpcsharp_metadata_array_move(
859 &(ctx->send_status_from_server.trailing_metadata), trailing_metadata);
860 ops[0].data.send_status_from_server.trailing_metadata_count =
861 ctx->send_status_from_server.trailing_metadata.count;
862 ops[0].data.send_status_from_server.trailing_metadata =
863 ctx->send_status_from_server.trailing_metadata.metadata;
865 ops[0].reserved = NULL;
866 if (optional_send_buffer) {
867 ops[nops].op = GRPC_OP_SEND_MESSAGE;
869 grpcsharp_create_byte_buffer_from_stolen_slices(optional_send_buffer);
870 ops[nops].data.send_message.send_message = ctx->send_message;
871 ops[nops].flags = write_flags;
872 ops[nops].reserved = NULL;
875 if (send_empty_initial_metadata) {
876 ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA;
878 ops[nops].reserved = NULL;
881 grpc_call_error ret = grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
882 grpc_slice_unref(status_details_slice);
886 GPR_EXPORT grpc_call_error GPR_CALLTYPE
887 grpcsharp_call_recv_message(grpc_call* call, grpcsharp_batch_context* ctx) {
888 /* TODO: don't use magic number */
890 ops[0].op = GRPC_OP_RECV_MESSAGE;
891 ops[0].data.recv_message.recv_message = &(ctx->recv_message);
893 ops[0].reserved = NULL;
894 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
898 GPR_EXPORT grpc_call_error GPR_CALLTYPE
899 grpcsharp_call_start_serverside(grpc_call* call, grpcsharp_batch_context* ctx) {
900 /* TODO: don't use magic number */
902 ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
903 ops[0].data.recv_close_on_server.cancelled =
904 (&ctx->recv_close_on_server_cancelled);
906 ops[0].reserved = NULL;
908 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
912 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_initial_metadata(
913 grpc_call* call, grpcsharp_batch_context* ctx,
914 grpc_metadata_array* initial_metadata) {
915 /* TODO: don't use magic number */
917 memset(ops, 0, sizeof(ops));
918 ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
919 grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
921 ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
922 ops[0].data.send_initial_metadata.metadata =
923 ctx->send_initial_metadata.metadata;
925 ops[0].reserved = NULL;
927 return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
931 GPR_EXPORT grpc_call_error GPR_CALLTYPE
932 grpcsharp_call_set_credentials(grpc_call* call, grpc_call_credentials* creds) {
933 return grpc_call_set_credentials(call, creds);
938 GPR_EXPORT grpc_server* GPR_CALLTYPE
939 grpcsharp_server_create(const grpc_channel_args* args) {
940 return grpc_server_create(args, NULL);
943 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_register_completion_queue(
944 grpc_server* server, grpc_completion_queue* cq) {
945 grpc_server_register_completion_queue(server, cq, NULL);
948 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_server_add_insecure_http2_port(
949 grpc_server* server, const char* addr) {
950 return grpc_server_add_insecure_http2_port(server, addr);
953 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server* server) {
954 grpc_server_start(server);
957 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_shutdown_and_notify_callback(
958 grpc_server* server, grpc_completion_queue* cq,
959 grpcsharp_batch_context* ctx) {
960 grpc_server_shutdown_and_notify(server, cq, ctx);
963 GPR_EXPORT void GPR_CALLTYPE
964 grpcsharp_server_cancel_all_calls(grpc_server* server) {
965 grpc_server_cancel_all_calls(server);
968 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server* server) {
969 grpc_server_destroy(server);
972 GPR_EXPORT grpc_call_error GPR_CALLTYPE
973 grpcsharp_server_request_call(grpc_server* server, grpc_completion_queue* cq,
974 grpcsharp_request_call_context* ctx) {
975 return grpc_server_request_call(server, &(ctx->call), &(ctx->call_details),
976 &(ctx->request_metadata), cq, cq, ctx);
979 /* Native callback dispatcher */
981 typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)(
982 void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4,
985 static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher =
988 GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init(
989 grpcsharp_native_callback_dispatcher_func func) {
991 native_callback_dispatcher = func;
996 static char* default_pem_root_certs = NULL;
998 static grpc_ssl_roots_override_result override_ssl_roots_handler(
999 char** pem_root_certs) {
1000 if (!default_pem_root_certs) {
1001 *pem_root_certs = NULL;
1002 return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY;
1004 *pem_root_certs = gpr_strdup(default_pem_root_certs);
1005 return GRPC_SSL_ROOTS_OVERRIDE_OK;
1008 GPR_EXPORT void GPR_CALLTYPE
1009 grpcsharp_override_default_ssl_roots(const char* pem_root_certs) {
1011 * This currently wastes ~300kB of memory by keeping a copy of roots
1012 * in a static variable, but for desktop/server use, the overhead
1013 * is negligible. In the future, we might want to change the behavior
1014 * for mobile (e.g. Xamarin).
1016 default_pem_root_certs = gpr_strdup(pem_root_certs);
1017 grpc_set_ssl_roots_override_callback(override_ssl_roots_handler);
1020 static void grpcsharp_verify_peer_destroy_handler(void* userdata) {
1021 native_callback_dispatcher(userdata, NULL, NULL, (void*)1, NULL, NULL, NULL);
1024 static int grpcsharp_verify_peer_handler(const char* target_name,
1025 const char* peer_pem, void* userdata) {
1026 return native_callback_dispatcher(userdata, (void*)target_name,
1027 (void*)peer_pem, (void*)0, NULL, NULL,
1031 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE
1032 grpcsharp_ssl_credentials_create(const char* pem_root_certs,
1033 const char* key_cert_pair_cert_chain,
1034 const char* key_cert_pair_private_key,
1035 void* verify_peer_callback_tag) {
1036 grpc_ssl_pem_key_cert_pair key_cert_pair;
1037 verify_peer_options verify_options;
1038 grpc_ssl_pem_key_cert_pair* key_cert_pair_ptr = NULL;
1039 verify_peer_options* verify_options_ptr = NULL;
1041 if (key_cert_pair_cert_chain || key_cert_pair_private_key) {
1042 memset(&key_cert_pair, 0, sizeof(key_cert_pair));
1043 key_cert_pair.cert_chain = key_cert_pair_cert_chain;
1044 key_cert_pair.private_key = key_cert_pair_private_key;
1045 key_cert_pair_ptr = &key_cert_pair;
1047 GPR_ASSERT(!key_cert_pair_cert_chain);
1048 GPR_ASSERT(!key_cert_pair_private_key);
1051 if (verify_peer_callback_tag != NULL) {
1052 memset(&verify_options, 0, sizeof(verify_peer_options));
1053 verify_options.verify_peer_callback_userdata = verify_peer_callback_tag;
1054 verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler;
1055 verify_options.verify_peer_callback = grpcsharp_verify_peer_handler;
1056 verify_options_ptr = &verify_options;
1059 return grpc_ssl_credentials_create(pem_root_certs, key_cert_pair_ptr,
1060 verify_options_ptr, NULL);
1063 GPR_EXPORT void GPR_CALLTYPE
1064 grpcsharp_channel_credentials_release(grpc_channel_credentials* creds) {
1065 grpc_channel_credentials_release(creds);
1068 GPR_EXPORT void GPR_CALLTYPE
1069 grpcsharp_call_credentials_release(grpc_call_credentials* creds) {
1070 grpc_call_credentials_release(creds);
1073 GPR_EXPORT grpc_channel* GPR_CALLTYPE grpcsharp_secure_channel_create(
1074 grpc_channel_credentials* creds, const char* target,
1075 const grpc_channel_args* args) {
1076 return grpc_secure_channel_create(creds, target, args, NULL);
1079 GPR_EXPORT grpc_server_credentials* GPR_CALLTYPE
1080 grpcsharp_ssl_server_credentials_create(
1081 const char* pem_root_certs, const char** key_cert_pair_cert_chain_array,
1082 const char** key_cert_pair_private_key_array, size_t num_key_cert_pairs,
1083 grpc_ssl_client_certificate_request_type client_request_type) {
1085 grpc_server_credentials* creds;
1086 grpc_ssl_pem_key_cert_pair* key_cert_pairs =
1087 gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
1088 memset(key_cert_pairs, 0,
1089 sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
1091 for (i = 0; i < num_key_cert_pairs; i++) {
1092 if (key_cert_pair_cert_chain_array[i] ||
1093 key_cert_pair_private_key_array[i]) {
1094 key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i];
1095 key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i];
1098 creds = grpc_ssl_server_credentials_create_ex(pem_root_certs, key_cert_pairs,
1100 client_request_type, NULL);
1101 gpr_free(key_cert_pairs);
1105 GPR_EXPORT void GPR_CALLTYPE
1106 grpcsharp_server_credentials_release(grpc_server_credentials* creds) {
1107 grpc_server_credentials_release(creds);
1110 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_server_add_secure_http2_port(
1111 grpc_server* server, const char* addr, grpc_server_credentials* creds) {
1112 return grpc_server_add_secure_http2_port(server, addr, creds);
1115 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE
1116 grpcsharp_composite_channel_credentials_create(
1117 grpc_channel_credentials* channel_creds,
1118 grpc_call_credentials* call_creds) {
1119 return grpc_composite_channel_credentials_create(channel_creds, call_creds,
1123 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE
1124 grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1,
1125 grpc_call_credentials* creds2) {
1126 return grpc_composite_call_credentials_create(creds1, creds2, NULL);
1129 /* Metadata credentials plugin */
1131 GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
1132 grpc_credentials_plugin_metadata_cb cb, void* user_data,
1133 grpc_metadata_array* metadata, grpc_status_code status,
1134 const char* error_details) {
1136 cb(user_data, metadata->metadata, metadata->count, status, error_details);
1138 cb(user_data, NULL, 0, status, error_details);
1142 static int grpcsharp_get_metadata_handler(
1143 void* state, grpc_auth_metadata_context context,
1144 grpc_credentials_plugin_metadata_cb cb, void* user_data,
1145 grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
1146 size_t* num_creds_md, grpc_status_code* status,
1147 const char** error_details) {
1151 (void)error_details;
1152 // the "context" object and its contents are only guaranteed to live until
1153 // this handler returns (which could result in use-after-free for async
1154 // handling of the callback), so the C# counterpart of this handler
1155 // must make a copy of the "service_url" and "method_name" strings before
1156 // it returns if it wants to uses these strings.
1157 native_callback_dispatcher(state, (void*)context.service_url,
1158 (void*)context.method_name, cb, user_data,
1160 return 0; /* Asynchronous return. */
1163 static void grpcsharp_metadata_credentials_destroy_handler(void* state) {
1164 native_callback_dispatcher(state, NULL, NULL, NULL, NULL, (void*)1, NULL);
1167 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE
1168 grpcsharp_metadata_credentials_create_from_plugin(void* callback_tag) {
1169 grpc_metadata_credentials_plugin plugin;
1170 plugin.get_metadata = grpcsharp_get_metadata_handler;
1171 plugin.destroy = grpcsharp_metadata_credentials_destroy_handler;
1172 plugin.state = callback_tag;
1174 // TODO(yihuazhang): Expose min_security_level via the C# API so
1175 // that applications can decide what minimum security level their
1177 return grpc_metadata_credentials_create_from_plugin(
1178 plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL);
1183 GPR_EXPORT grpc_auth_context* GPR_CALLTYPE
1184 grpcsharp_call_auth_context(grpc_call* call) {
1185 return grpc_call_auth_context(call);
1188 GPR_EXPORT const char* GPR_CALLTYPE
1189 grpcsharp_auth_context_peer_identity_property_name(
1190 const grpc_auth_context* ctx) {
1191 return grpc_auth_context_peer_identity_property_name(ctx);
1194 GPR_EXPORT grpc_auth_property_iterator GPR_CALLTYPE
1195 grpcsharp_auth_context_property_iterator(const grpc_auth_context* ctx) {
1196 return grpc_auth_context_property_iterator(ctx);
1199 GPR_EXPORT const grpc_auth_property* GPR_CALLTYPE
1200 grpcsharp_auth_property_iterator_next(grpc_auth_property_iterator* it) {
1201 return grpc_auth_property_iterator_next(it);
1204 GPR_EXPORT void GPR_CALLTYPE
1205 grpcsharp_auth_context_release(grpc_auth_context* ctx) {
1206 grpc_auth_context_release(ctx);
1211 typedef void(GPR_CALLTYPE* grpcsharp_log_func)(const char* file, int32_t line,
1213 const char* severity_string,
1215 static grpcsharp_log_func log_func = NULL;
1217 /* Redirects gpr_log to log_func callback */
1218 static void grpcsharp_log_handler(gpr_log_func_args* args) {
1219 log_func(args->file, args->line, gpr_thd_currentid(),
1220 gpr_log_severity_string(args->severity), args->message);
1223 GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) {
1226 gpr_set_log_function(grpcsharp_log_handler);
1229 typedef void(GPR_CALLTYPE* test_callback_funcptr)(int32_t success);
1231 /* Slice buffer functionality */
1232 GPR_EXPORT grpc_slice_buffer* GPR_CALLTYPE grpcsharp_slice_buffer_create() {
1233 grpc_slice_buffer* slice_buffer =
1234 (grpc_slice_buffer*)gpr_malloc(sizeof(grpc_slice_buffer));
1235 grpc_slice_buffer_init(slice_buffer);
1236 return slice_buffer;
1239 GPR_EXPORT void GPR_CALLTYPE
1240 grpcsharp_slice_buffer_reset_and_unref(grpc_slice_buffer* buffer) {
1241 grpc_slice_buffer_reset_and_unref(buffer);
1244 GPR_EXPORT void GPR_CALLTYPE
1245 grpcsharp_slice_buffer_destroy(grpc_slice_buffer* buffer) {
1246 grpc_slice_buffer_destroy(buffer);
1250 GPR_EXPORT size_t GPR_CALLTYPE
1251 grpcsharp_slice_buffer_slice_count(grpc_slice_buffer* buffer) {
1252 return buffer->count;
1255 GPR_EXPORT void GPR_CALLTYPE
1256 grpcsharp_slice_buffer_slice_peek(grpc_slice_buffer* buffer, size_t index,
1257 size_t* slice_len, uint8_t** slice_data_ptr) {
1258 GPR_ASSERT(buffer->count > index);
1259 grpc_slice* slice_ptr = &buffer->slices[index];
1260 *slice_len = GRPC_SLICE_LENGTH(*slice_ptr);
1261 *slice_data_ptr = GRPC_SLICE_START_PTR(*slice_ptr);
1264 GPR_EXPORT void* GPR_CALLTYPE grpcsharp_slice_buffer_adjust_tail_space(
1265 grpc_slice_buffer* buffer, size_t available_tail_space,
1266 size_t requested_tail_space) {
1267 if (available_tail_space == requested_tail_space) {
1269 } else if (available_tail_space >= requested_tail_space) {
1270 grpc_slice_buffer_trim_end(
1271 buffer, available_tail_space - requested_tail_space, NULL);
1273 if (available_tail_space > 0) {
1274 grpc_slice_buffer_trim_end(buffer, available_tail_space, NULL);
1277 grpc_slice new_slice = grpc_slice_malloc(requested_tail_space);
1278 // grpc_slice_buffer_add_indexed always adds as a new slice entry into the
1279 // sb (which is suboptimal in some cases) but it doesn't have the problem of
1280 // sometimes splitting the continguous new_slice across two different slices
1281 // (like grpc_slice_buffer_add would)
1282 grpc_slice_buffer_add_indexed(buffer, new_slice);
1285 if (buffer->count == 0) {
1288 grpc_slice* last_slice = &(buffer->slices[buffer->count - 1]);
1289 return GRPC_SLICE_END_PTR(*last_slice) - requested_tail_space;
1293 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_version_string() {
1294 return grpc_version_string();
1298 GPR_EXPORT void GPR_CALLTYPE
1299 grpcsharp_test_callback(test_callback_funcptr callback) {
1304 GPR_EXPORT void* GPR_CALLTYPE grpcsharp_test_nop(void* ptr) { return ptr; }
1307 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) {
1308 return sizeof(grpc_event);
1311 /* Override a method for testing */
1312 GPR_EXPORT void GPR_CALLTYPE
1313 grpcsharp_test_override_method(const char* method_name, const char* variant) {
1314 if (strcmp("grpcsharp_call_start_batch", method_name) == 0) {
1315 if (strcmp("nop", variant) == 0) {
1316 g_call_start_batch_func = grpcsharp_call_start_batch_nop;