| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <ruby/ruby.h> |
| |
| #include "rb_compression_options.h" |
| |
| #include <string.h> |
| |
| #include "rb_byte_buffer.h" |
| #include "rb_grpc.h" |
| #include "rb_grpc_imports.generated.h" |
| |
| #include <grpc/compression.h> |
| #include <grpc/grpc.h> |
| #include <grpc/impl/codegen/compression_types.h> |
| #include <grpc/impl/grpc_types.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| |
| static VALUE grpc_rb_cCompressionOptions = Qnil; |
| |
| /* Ruby Ids for the names of valid compression levels. */ |
| static VALUE id_compress_level_none = Qnil; |
| static VALUE id_compress_level_low = Qnil; |
| static VALUE id_compress_level_medium = Qnil; |
| static VALUE id_compress_level_high = Qnil; |
| |
| /* grpc_rb_compression_options wraps a grpc_compression_options. |
| * It can be used to get the channel argument key-values for specific |
| * compression settings. */ |
| |
| /* Note that ruby objects of this type don't carry any state in other |
| * Ruby objects and don't have a mark for GC. */ |
| typedef struct grpc_rb_compression_options { |
| /* The actual compression options that's being wrapped */ |
| grpc_compression_options* wrapped; |
| } grpc_rb_compression_options; |
| |
| static void grpc_rb_compression_options_free_internal(void* p) { |
| grpc_rb_compression_options* wrapper = NULL; |
| if (p == NULL) { |
| return; |
| }; |
| wrapper = (grpc_rb_compression_options*)p; |
| if (wrapper->wrapped != NULL) { |
| gpr_free(wrapper->wrapped); |
| wrapper->wrapped = NULL; |
| } |
| xfree(p); |
| } |
| |
| /* Destroys the compression options instances and free the |
| * wrapped grpc compression options. */ |
| static void grpc_rb_compression_options_free(void* p) { |
| grpc_rb_compression_options_free_internal(p); |
| } |
| |
| /* Ruby recognized data type for the CompressionOptions class. */ |
| static rb_data_type_t grpc_rb_compression_options_data_type = { |
| "grpc_compression_options", |
| {NULL, |
| grpc_rb_compression_options_free, |
| GRPC_RB_MEMSIZE_UNAVAILABLE, |
| {NULL, NULL}}, |
| NULL, |
| NULL, |
| #ifdef RUBY_TYPED_FREE_IMMEDIATELY |
| RUBY_TYPED_FREE_IMMEDIATELY |
| #endif |
| }; |
| |
| /* Allocates CompressionOptions instances. |
| Allocate the wrapped grpc compression options and |
| initialize it here too. */ |
| static VALUE grpc_rb_compression_options_alloc(VALUE cls) { |
| grpc_ruby_init(); |
| grpc_rb_compression_options* wrapper = NULL; |
| |
| wrapper = gpr_malloc(sizeof(grpc_rb_compression_options)); |
| wrapper->wrapped = NULL; |
| wrapper->wrapped = gpr_malloc(sizeof(grpc_compression_options)); |
| grpc_compression_options_init(wrapper->wrapped); |
| |
| return TypedData_Wrap_Struct(cls, &grpc_rb_compression_options_data_type, |
| wrapper); |
| } |
| |
| /* Disables a compression algorithm, given the GRPC core internal number of a |
| * compression algorithm. */ |
| VALUE grpc_rb_compression_options_disable_compression_algorithm_internal( |
| VALUE self, VALUE algorithm_to_disable) { |
| grpc_compression_algorithm compression_algorithm = 0; |
| grpc_rb_compression_options* wrapper = NULL; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| compression_algorithm = |
| (grpc_compression_algorithm)NUM2INT(algorithm_to_disable); |
| |
| grpc_compression_options_disable_algorithm(wrapper->wrapped, |
| compression_algorithm); |
| |
| return Qnil; |
| } |
| |
| /* Gets the compression internal enum value of a compression level given its |
| * name. */ |
| grpc_compression_level grpc_rb_compression_options_level_name_to_value_internal( |
| VALUE level_name) { |
| Check_Type(level_name, T_SYMBOL); |
| |
| /* Check the compression level of the name passed in, and see which macro |
| * from the GRPC core header files match. */ |
| if (id_compress_level_none == SYM2ID(level_name)) { |
| return GRPC_COMPRESS_LEVEL_NONE; |
| } else if (id_compress_level_low == SYM2ID(level_name)) { |
| return GRPC_COMPRESS_LEVEL_LOW; |
| } else if (id_compress_level_medium == SYM2ID(level_name)) { |
| return GRPC_COMPRESS_LEVEL_MED; |
| } else if (id_compress_level_high == SYM2ID(level_name)) { |
| return GRPC_COMPRESS_LEVEL_HIGH; |
| } |
| |
| rb_raise(rb_eArgError, |
| "Unrecognized compression level name." |
| "Valid compression level names are none, low, medium, and high."); |
| |
| /* Phony return statement. */ |
| return GRPC_COMPRESS_LEVEL_NONE; |
| } |
| |
| /* Sets the default compression level, given the name of a compression level. |
| * Throws an error if no algorithm matched. */ |
| void grpc_rb_compression_options_set_default_level( |
| grpc_compression_options* options, VALUE new_level_name) { |
| options->default_level.level = |
| grpc_rb_compression_options_level_name_to_value_internal(new_level_name); |
| options->default_level.is_set = 1; |
| } |
| |
| /* Gets the internal value of a compression algorithm suitable as the value |
| * in a GRPC core channel arguments hash. |
| * algorithm_value is an out parameter. |
| * Raises an error if the name of the algorithm passed in is invalid. */ |
| void grpc_rb_compression_options_algorithm_name_to_value_internal( |
| grpc_compression_algorithm* algorithm_value, VALUE algorithm_name) { |
| grpc_slice name_slice; |
| VALUE algorithm_name_as_string = Qnil; |
| |
| Check_Type(algorithm_name, T_SYMBOL); |
| |
| /* Convert the algorithm symbol to a ruby string, so that we can get the |
| * correct C string out of it. */ |
| algorithm_name_as_string = rb_funcall(algorithm_name, rb_intern("to_s"), 0); |
| |
| name_slice = |
| grpc_slice_from_copied_buffer(RSTRING_PTR(algorithm_name_as_string), |
| RSTRING_LEN(algorithm_name_as_string)); |
| |
| /* Raise an error if the name isn't recognized as a compression algorithm by |
| * the algorithm parse function |
| * in GRPC core. */ |
| if (!grpc_compression_algorithm_parse(name_slice, algorithm_value)) { |
| char* name_slice_str = grpc_slice_to_c_string(name_slice); |
| char* error_message_str = NULL; |
| VALUE error_message_ruby_str = Qnil; |
| GPR_ASSERT(gpr_asprintf(&error_message_str, |
| "Invalid compression algorithm name: %s", |
| name_slice_str) != -1); |
| gpr_free(name_slice_str); |
| error_message_ruby_str = |
| rb_str_new(error_message_str, strlen(error_message_str)); |
| gpr_free(error_message_str); |
| rb_raise(rb_eNameError, "%s", StringValueCStr(error_message_ruby_str)); |
| } |
| |
| grpc_slice_unref(name_slice); |
| } |
| |
| /* Indicates whether a given algorithm is enabled on this instance, given the |
| * readable algorithm name. */ |
| VALUE grpc_rb_compression_options_is_algorithm_enabled(VALUE self, |
| VALUE algorithm_name) { |
| grpc_rb_compression_options* wrapper = NULL; |
| grpc_compression_algorithm internal_algorithm_value; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| grpc_rb_compression_options_algorithm_name_to_value_internal( |
| &internal_algorithm_value, algorithm_name); |
| |
| if (grpc_compression_options_is_algorithm_enabled(wrapper->wrapped, |
| internal_algorithm_value)) { |
| return Qtrue; |
| } |
| return Qfalse; |
| } |
| |
| /* Sets the default algorithm to the name of the algorithm passed in. |
| * Raises an error if the name is not a valid compression algorithm name. */ |
| void grpc_rb_compression_options_set_default_algorithm( |
| grpc_compression_options* options, VALUE algorithm_name) { |
| grpc_rb_compression_options_algorithm_name_to_value_internal( |
| &options->default_algorithm.algorithm, algorithm_name); |
| options->default_algorithm.is_set = 1; |
| } |
| |
| /* Disables an algorithm on the current instance, given the name of an |
| * algorithm. |
| * Fails if the algorithm name is invalid. */ |
| void grpc_rb_compression_options_disable_algorithm( |
| grpc_compression_options* compression_options, VALUE algorithm_name) { |
| grpc_compression_algorithm internal_algorithm_value; |
| |
| grpc_rb_compression_options_algorithm_name_to_value_internal( |
| &internal_algorithm_value, algorithm_name); |
| grpc_compression_options_disable_algorithm(compression_options, |
| internal_algorithm_value); |
| } |
| |
| /* Provides a ruby hash of GRPC core channel argument key-values that |
| * correspond to the compression settings on this instance. */ |
| VALUE grpc_rb_compression_options_to_hash(VALUE self) { |
| grpc_rb_compression_options* wrapper = NULL; |
| grpc_compression_options* compression_options = NULL; |
| VALUE channel_arg_hash = rb_hash_new(); |
| VALUE key = Qnil; |
| VALUE value = Qnil; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| compression_options = wrapper->wrapped; |
| |
| /* Add key-value pairs to the new Ruby hash. It can be used |
| * as GRPC core channel arguments. */ |
| if (compression_options->default_level.is_set) { |
| key = rb_str_new2(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL); |
| value = INT2NUM((int)compression_options->default_level.level); |
| rb_hash_aset(channel_arg_hash, key, value); |
| } |
| |
| if (compression_options->default_algorithm.is_set) { |
| key = rb_str_new2(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM); |
| value = INT2NUM((int)compression_options->default_algorithm.algorithm); |
| rb_hash_aset(channel_arg_hash, key, value); |
| } |
| |
| key = rb_str_new2(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET); |
| value = INT2NUM((int)compression_options->enabled_algorithms_bitset); |
| rb_hash_aset(channel_arg_hash, key, value); |
| |
| return channel_arg_hash; |
| } |
| |
| /* Converts an internal enum level value to a readable level name. |
| * Fails if the level value is invalid. */ |
| VALUE grpc_rb_compression_options_level_value_to_name_internal( |
| grpc_compression_level compression_value) { |
| switch (compression_value) { |
| case GRPC_COMPRESS_LEVEL_NONE: |
| return ID2SYM(id_compress_level_none); |
| case GRPC_COMPRESS_LEVEL_LOW: |
| return ID2SYM(id_compress_level_low); |
| case GRPC_COMPRESS_LEVEL_MED: |
| return ID2SYM(id_compress_level_medium); |
| case GRPC_COMPRESS_LEVEL_HIGH: |
| return ID2SYM(id_compress_level_high); |
| default: |
| rb_raise( |
| rb_eArgError, |
| "Failed to convert compression level value to name for value: %d", |
| (int)compression_value); |
| /* return something to avoid compiler error about no return */ |
| return Qnil; |
| } |
| } |
| |
| /* Converts an algorithm internal enum value to a readable name. |
| * Fails if the enum value is invalid. */ |
| VALUE grpc_rb_compression_options_algorithm_value_to_name_internal( |
| grpc_compression_algorithm internal_value) { |
| char* algorithm_name = NULL; |
| |
| if (!grpc_compression_algorithm_name(internal_value, &algorithm_name)) { |
| rb_raise(rb_eArgError, "Failed to convert algorithm value to name"); |
| } |
| |
| return ID2SYM(rb_intern(algorithm_name)); |
| } |
| |
| /* Gets the readable name of the default algorithm if one has been set. |
| * Returns nil if no algorithm has been set. */ |
| VALUE grpc_rb_compression_options_get_default_algorithm(VALUE self) { |
| grpc_compression_algorithm internal_value; |
| grpc_rb_compression_options* wrapper = NULL; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| |
| if (wrapper->wrapped->default_algorithm.is_set) { |
| internal_value = wrapper->wrapped->default_algorithm.algorithm; |
| return grpc_rb_compression_options_algorithm_value_to_name_internal( |
| internal_value); |
| } |
| |
| return Qnil; |
| } |
| |
| /* Gets the internal value of the default compression level that is to be passed |
| * to the GRPC core as a channel argument value. |
| * A nil return value means that it hasn't been set. */ |
| VALUE grpc_rb_compression_options_get_default_level(VALUE self) { |
| grpc_compression_level internal_value; |
| grpc_rb_compression_options* wrapper = NULL; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| |
| if (wrapper->wrapped->default_level.is_set) { |
| internal_value = wrapper->wrapped->default_level.level; |
| return grpc_rb_compression_options_level_value_to_name_internal( |
| internal_value); |
| } |
| |
| return Qnil; |
| } |
| |
| /* Gets a list of the disabled algorithms as readable names. |
| * Returns an empty list if no algorithms have been disabled. */ |
| VALUE grpc_rb_compression_options_get_disabled_algorithms(VALUE self) { |
| VALUE disabled_algorithms = rb_ary_new(); |
| grpc_compression_algorithm internal_value; |
| grpc_rb_compression_options* wrapper = NULL; |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| |
| for (internal_value = GRPC_COMPRESS_NONE; |
| internal_value < GRPC_COMPRESS_ALGORITHMS_COUNT; internal_value++) { |
| if (!grpc_compression_options_is_algorithm_enabled(wrapper->wrapped, |
| internal_value)) { |
| rb_ary_push(disabled_algorithms, |
| grpc_rb_compression_options_algorithm_value_to_name_internal( |
| internal_value)); |
| } |
| } |
| return disabled_algorithms; |
| } |
| |
| /* Initializes the compression options wrapper. |
| * Takes an optional hash parameter. |
| * |
| * Example call-seq: |
| * options = CompressionOptions.new( |
| * default_level: :none, |
| * disabled_algorithms: [:gzip] |
| * ) |
| * channel_arg hash = Hash.new[...] |
| * channel_arg_hash_with_compression_options = channel_arg_hash.merge(options) |
| */ |
| VALUE grpc_rb_compression_options_init(int argc, VALUE* argv, VALUE self) { |
| grpc_rb_compression_options* wrapper = NULL; |
| VALUE default_algorithm = Qnil; |
| VALUE default_level = Qnil; |
| VALUE disabled_algorithms = Qnil; |
| VALUE algorithm_name = Qnil; |
| VALUE hash_arg = Qnil; |
| |
| rb_scan_args(argc, argv, "01", &hash_arg); |
| |
| /* Check if the hash parameter was passed, or if invalid arguments were |
| * passed. */ |
| if (hash_arg == Qnil) { |
| return self; |
| } else if (TYPE(hash_arg) != T_HASH || argc > 1) { |
| rb_raise(rb_eArgError, |
| "Invalid arguments. Expecting optional hash parameter"); |
| } |
| |
| TypedData_Get_Struct(self, grpc_rb_compression_options, |
| &grpc_rb_compression_options_data_type, wrapper); |
| |
| /* Set the default algorithm if one was chosen. */ |
| default_algorithm = |
| rb_hash_aref(hash_arg, ID2SYM(rb_intern("default_algorithm"))); |
| if (default_algorithm != Qnil) { |
| grpc_rb_compression_options_set_default_algorithm(wrapper->wrapped, |
| default_algorithm); |
| } |
| |
| /* Set the default level if one was chosen. */ |
| default_level = rb_hash_aref(hash_arg, ID2SYM(rb_intern("default_level"))); |
| if (default_level != Qnil) { |
| grpc_rb_compression_options_set_default_level(wrapper->wrapped, |
| default_level); |
| } |
| |
| /* Set the disabled algorithms if any were chosen. */ |
| disabled_algorithms = |
| rb_hash_aref(hash_arg, ID2SYM(rb_intern("disabled_algorithms"))); |
| if (disabled_algorithms != Qnil) { |
| Check_Type(disabled_algorithms, T_ARRAY); |
| |
| for (int i = 0; i < RARRAY_LEN(disabled_algorithms); i++) { |
| algorithm_name = rb_ary_entry(disabled_algorithms, i); |
| grpc_rb_compression_options_disable_algorithm(wrapper->wrapped, |
| algorithm_name); |
| } |
| } |
| |
| return self; |
| } |
| |
| void Init_grpc_compression_options() { |
| grpc_rb_cCompressionOptions = rb_define_class_under( |
| grpc_rb_mGrpcCore, "CompressionOptions", rb_cObject); |
| |
| /* Allocates an object managed by the ruby runtime. */ |
| rb_define_alloc_func(grpc_rb_cCompressionOptions, |
| grpc_rb_compression_options_alloc); |
| |
| /* Initializes the ruby wrapper. #new method takes an optional hash argument. |
| */ |
| rb_define_method(grpc_rb_cCompressionOptions, "initialize", |
| grpc_rb_compression_options_init, -1); |
| |
| /* Methods for getting the default algorithm, default level, and disabled |
| * algorithms as readable names. */ |
| rb_define_method(grpc_rb_cCompressionOptions, "default_algorithm", |
| grpc_rb_compression_options_get_default_algorithm, 0); |
| rb_define_method(grpc_rb_cCompressionOptions, "default_level", |
| grpc_rb_compression_options_get_default_level, 0); |
| rb_define_method(grpc_rb_cCompressionOptions, "disabled_algorithms", |
| grpc_rb_compression_options_get_disabled_algorithms, 0); |
| |
| /* Determines whether or not an algorithm is enabled, given a readable |
| * algorithm name.*/ |
| rb_define_method(grpc_rb_cCompressionOptions, "algorithm_enabled?", |
| grpc_rb_compression_options_is_algorithm_enabled, 1); |
| |
| /* Provides a hash of the compression settings suitable |
| * for passing to server or channel args. */ |
| rb_define_method(grpc_rb_cCompressionOptions, "to_hash", |
| grpc_rb_compression_options_to_hash, 0); |
| rb_define_alias(grpc_rb_cCompressionOptions, "to_channel_arg_hash", |
| "to_hash"); |
| |
| /* Ruby ids for the names of the different compression levels. */ |
| id_compress_level_none = rb_intern("none"); |
| id_compress_level_low = rb_intern("low"); |
| id_compress_level_medium = rb_intern("medium"); |
| id_compress_level_high = rb_intern("high"); |
| } |