1 /* 2 * 3 * Copyright 2015 gRPC authors. 4 * 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 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 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. 16 * 17 */ 18 19 #include <ruby/ruby.h> 20 21 #include "rb_channel_args.h" 22 #include "rb_grpc_imports.generated.h" 23 24 #include <grpc/grpc.h> 25 26 #include "rb_grpc.h" 27 28 static rb_data_type_t grpc_rb_channel_args_data_type = { 29 "grpc_channel_args", 30 {GRPC_RB_GC_NOT_MARKED, 31 GRPC_RB_GC_DONT_FREE, 32 GRPC_RB_MEMSIZE_UNAVAILABLE, 33 {NULL, NULL}}, 34 NULL, 35 NULL, 36 #ifdef RUBY_TYPED_FREE_IMMEDIATELY 37 RUBY_TYPED_FREE_IMMEDIATELY 38 #endif 39 }; 40 41 /* A callback the processes the hash key values in channel_args hash */ 42 static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key, 43 VALUE val, 44 VALUE args_obj) { 45 const char* the_key; 46 grpc_channel_args* args; 47 48 switch (TYPE(key)) { 49 case T_STRING: 50 the_key = StringValuePtr(key); 51 break; 52 53 case T_SYMBOL: 54 the_key = rb_id2name(SYM2ID(key)); 55 break; 56 57 default: 58 rb_raise(rb_eTypeError, "bad chan arg: got <%s>, want <String|Symbol>", 59 rb_obj_classname(key)); 60 return ST_STOP; 61 } 62 63 TypedData_Get_Struct(args_obj, grpc_channel_args, 64 &grpc_rb_channel_args_data_type, args); 65 if (args->num_args <= 0) { 66 rb_raise(rb_eRuntimeError, "hash_cb bug: num_args is %lu for key:%s", 67 args->num_args, StringValueCStr(key)); 68 return ST_STOP; 69 } 70 71 args->args[args->num_args - 1].key = (char*)the_key; 72 switch (TYPE(val)) { 73 case T_SYMBOL: 74 args->args[args->num_args - 1].type = GRPC_ARG_STRING; 75 args->args[args->num_args - 1].value.string = 76 (char*)rb_id2name(SYM2ID(val)); 77 --args->num_args; 78 return ST_CONTINUE; 79 80 case T_STRING: 81 args->args[args->num_args - 1].type = GRPC_ARG_STRING; 82 args->args[args->num_args - 1].value.string = StringValueCStr(val); 83 --args->num_args; 84 return ST_CONTINUE; 85 86 case T_FIXNUM: 87 args->args[args->num_args - 1].type = GRPC_ARG_INTEGER; 88 args->args[args->num_args - 1].value.integer = NUM2INT(val); 89 --args->num_args; 90 return ST_CONTINUE; 91 92 default: 93 rb_raise(rb_eTypeError, "%s: bad value: got <%s>, want <String|Fixnum>", 94 StringValueCStr(key), rb_obj_classname(val)); 95 return ST_STOP; 96 } 97 rb_raise(rb_eRuntimeError, "impl bug: hash_cb reached to far while on key:%s", 98 StringValueCStr(key)); 99 return ST_STOP; 100 } 101 102 /* channel_convert_params allows the call to 103 grpc_rb_hash_convert_to_channel_args to be made within an rb_protect 104 exception-handler. This allows any allocated memory to be freed before 105 propagating any exception that occurs */ 106 typedef struct channel_convert_params { 107 VALUE src_hash; 108 grpc_channel_args* dst; 109 } channel_convert_params; 110 111 static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) { 112 ID id_size = rb_intern("size"); 113 VALUE grpc_rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); 114 channel_convert_params* params = (channel_convert_params*)as_value; 115 size_t num_args = 0; 116 117 if (!NIL_P(params->src_hash) && TYPE(params->src_hash) != T_HASH) { 118 rb_raise(rb_eTypeError, "bad channel args: got:<%s> want: a hash or nil", 119 rb_obj_classname(params->src_hash)); 120 return Qnil; 121 } 122 123 if (TYPE(params->src_hash) == T_HASH) { 124 num_args = NUM2INT(rb_funcall(params->src_hash, id_size, 0)); 125 params->dst->num_args = num_args; 126 params->dst->args = ALLOC_N(grpc_arg, num_args); 127 MEMZERO(params->dst->args, grpc_arg, num_args); 128 rb_hash_foreach( 129 params->src_hash, grpc_rb_channel_create_in_process_add_args_hash_cb, 130 TypedData_Wrap_Struct(grpc_rb_cChannelArgs, 131 &grpc_rb_channel_args_data_type, params->dst)); 132 /* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb 133 * decrements it during has processing */ 134 params->dst->num_args = num_args; 135 } 136 return Qnil; 137 } 138 139 void grpc_rb_hash_convert_to_channel_args(VALUE src_hash, 140 grpc_channel_args* dst) { 141 channel_convert_params params; 142 int status = 0; 143 144 /* Make a protected call to grpc_rb_hash_convert_channel_args */ 145 params.src_hash = src_hash; 146 params.dst = dst; 147 rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE)¶ms, &status); 148 if (status != 0) { 149 if (dst->args != NULL) { 150 /* Free any allocated memory before propagating the error */ 151 xfree(dst->args); 152 } 153 rb_jump_tag(status); 154 } 155 } 156