From d788b458cf8e229aad25afc04ad5ec85e24537a3 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn <apolcyn@google.com> Date: Wed, 6 Jul 2016 13:58:09 -0700 Subject: [PATCH] Replaced private ruby methods with internal c functions --- src/ruby/ext/grpc/rb_compression_options.c | 528 +++++++++++++----- src/ruby/lib/grpc.rb | 1 - src/ruby/lib/grpc/core/compression_options.rb | 93 --- src/ruby/spec/compression_options_spec.rb | 266 +++++---- 4 files changed, 543 insertions(+), 345 deletions(-) delete mode 100644 src/ruby/lib/grpc/core/compression_options.rb diff --git a/src/ruby/ext/grpc/rb_compression_options.c b/src/ruby/ext/grpc/rb_compression_options.c index 66ba8fb84a..2d3be4c7d0 100644 --- a/src/ruby/ext/grpc/rb_compression_options.c +++ b/src/ruby/ext/grpc/rb_compression_options.c @@ -48,7 +48,10 @@ static VALUE grpc_rb_cCompressionOptions = Qnil; /* grpc_rb_compression_options wraps a grpc_compression_options. - * Note that ruby objects of this type don't carry any state in other + * 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 */ @@ -120,107 +123,242 @@ VALUE grpc_rb_compression_options_disable_compression_algorithm_internal( /* Provides a bitset as a ruby number that is suitable to pass to * the GRPC core as a channel argument to enable compression algorithms. */ -VALUE grpc_rb_compression_options_get_enabled_algorithms_bitset(VALUE self) { - grpc_rb_compression_options *wrapper = NULL; +/* 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) { + VALUE none_symbol = Qnil; + VALUE low_symbol = Qnil; + VALUE medium_symbol = Qnil; + VALUE high_symbol = Qnil; + + Check_Type(level_name, T_SYMBOL); + + /* Ruby symbols that correspond to names of valid compression levels */ + none_symbol = + rb_const_get(grpc_rb_cCompressionOptions, rb_intern("COMPRESS_NONE_SYM")); + low_symbol = + rb_const_get(grpc_rb_cCompressionOptions, rb_intern("COMPRESS_LOW_SYM")); + medium_symbol = rb_const_get(grpc_rb_cCompressionOptions, + rb_intern("COMPRESS_MEDIUM_SYM")); + high_symbol = + rb_const_get(grpc_rb_cCompressionOptions, rb_intern("COMPRESS_HIGH_SYM")); - TypedData_Get_Struct(self, grpc_rb_compression_options, - &grpc_rb_compression_options_data_type, wrapper); - return INT2NUM((int)wrapper->wrapped->enabled_algorithms_bitset); + /* Check the compression level of the name passed in, and see which macro + * from the GRPC core header files match. */ + if (RTEST(rb_funcall(level_name, rb_intern("=="), 1, none_symbol)) != 0) { + return GRPC_COMPRESS_LEVEL_NONE; + } else if (RTEST(rb_funcall(level_name, rb_intern("=="), 1, low_symbol)) != + 0) { + return GRPC_COMPRESS_LEVEL_LOW; + } else if (RTEST(rb_funcall(level_name, rb_intern("=="), 1, medium_symbol)) != + 0) { + return GRPC_COMPRESS_LEVEL_MED; + } else if (RTEST(rb_funcall(level_name, rb_intern("=="), 1, high_symbol)) != + 0) { + return GRPC_COMPRESS_LEVEL_HIGH; + } else { + rb_raise(rb_eArgError, + "Unrecognized compression level name." + "Valid compression level names are none, low, medium, and high."); + } } -void grpc_rb_compression_options_set_default_level_helper( - grpc_compression_options *compression_options, - grpc_compression_level level) { - compression_options->default_level.is_set |= 1; - compression_options->default_level.level = level; +/* Wrapper over grpc_rb_compression_options_level_name_to_value available for + * use or testing. + * Raises an exception for unrecognized level names. */ +VALUE grpc_rb_compression_options_level_name_to_value(VALUE self, + VALUE level_name) { + return INT2NUM((int)grpc_rb_compression_options_level_name_to_value_internal( + level_name)); } /* Sets the default compression level, given the name of a compression level. * Throws an error if no algorithm matched. */ -VALUE grpc_rb_compression_options_set_default_level(VALUE self, - VALUE new_level) { - char *level_name = NULL; - grpc_rb_compression_options *wrapper = NULL; - long name_len = 0; - VALUE ruby_str = Qnil; - - TypedData_Get_Struct(self, grpc_rb_compression_options, - &grpc_rb_compression_options_data_type, wrapper); - - /* Take both string and symbol parameters */ - ruby_str = rb_funcall(new_level, rb_intern("to_s"), 0); - - level_name = RSTRING_PTR(ruby_str); - name_len = RSTRING_LEN(ruby_str); - - /* Check the compression level of the name passed in, and see which macro - * from the GRPC core header files match. */ - if (strncmp(level_name, "none", name_len) == 0) { - grpc_rb_compression_options_set_default_level_helper( - wrapper->wrapped, GRPC_COMPRESS_LEVEL_NONE); - } else if (strncmp(level_name, "low", name_len) == 0) { - grpc_rb_compression_options_set_default_level_helper( - wrapper->wrapped, GRPC_COMPRESS_LEVEL_LOW); - } else if (strncmp(level_name, "medium", name_len) == 0) { - grpc_rb_compression_options_set_default_level_helper( - wrapper->wrapped, GRPC_COMPRESS_LEVEL_MED); - } else if (strncmp(level_name, "high", name_len) == 0) { - grpc_rb_compression_options_set_default_level_helper( - wrapper->wrapped, GRPC_COMPRESS_LEVEL_HIGH); - } else { - rb_raise(rb_eNameError, - "Invalid compression level name. Supported levels: none, low, " - "medium, high"); - } - - return Qnil; +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_get_internal_value_of_algorithm( - VALUE algorithm_name, grpc_compression_algorithm *compression_algorithm) { - VALUE ruby_str = Qnil; +void grpc_rb_compression_options_algorithm_name_to_value_internal( + grpc_compression_algorithm *algorithm_value, VALUE algorithm_name) { char *name_str = NULL; long name_len = 0; + VALUE algorithm_name_as_string = Qnil; - /* Accept ruby symbol and string parameters. */ - ruby_str = rb_funcall(algorithm_name, rb_intern("to_s"), 0); - name_str = RSTRING_PTR(ruby_str); - name_len = RSTRING_LEN(ruby_str); + 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_str = RSTRING_PTR(algorithm_name_as_string); + name_len = 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_str, name_len, - compression_algorithm)) { - rb_raise(rb_eNameError, - "Invalid compression algorithm name."); + if (!grpc_compression_algorithm_parse(name_str, name_len, algorithm_value)) { + rb_raise(rb_eNameError, "Invalid compression algorithm name: %s", + StringValueCStr(algorithm_name_as_string)); } } +/* Wrapper around algorithm_name_to_value_internal function available for use or + * testing. */ +VALUE grpc_rb_compression_options_algorithm_name_to_value( + VALUE self, VALUE algorithm_name) { + grpc_compression_algorithm algorithm_value; + grpc_rb_compression_options_algorithm_name_to_value_internal(&algorithm_value, + algorithm_name); + + return INT2NUM((int)algorithm_value); +} + +/* 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. */ -VALUE grpc_rb_compression_options_set_default_algorithm(VALUE self, - VALUE 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; - grpc_rb_compression_options_get_internal_value_of_algorithm( - algorithm_name, &wrapper->wrapped->default_algorithm.algorithm); - wrapper->wrapped->default_algorithm.is_set |= 1; + /* 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); + } - return Qnil; + 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 rb_const_get(grpc_rb_cCompressionOptions, + rb_intern("COMPRESS_NONE_SYM")); + case GRPC_COMPRESS_LEVEL_LOW: + return rb_const_get(grpc_rb_cCompressionOptions, + rb_intern("COMPRESS_LOW_SYM")); + case GRPC_COMPRESS_LEVEL_MED: + return rb_const_get(grpc_rb_cCompressionOptions, + rb_intern("COMPRESS_MEDIUM_SYM")); + case GRPC_COMPRESS_LEVEL_HIGH: + return rb_const_get(grpc_rb_cCompressionOptions, + rb_intern("COMPRESS_HIGH_SYM")); + default: + rb_raise( + rb_eArgError, + "Failed to convert compression level value to name for value: %d", + (int)compression_value); + } +} + +/* Wrapper of internal method that makes it available for use and testing. */ +VALUE grpc_rb_compression_options_level_value_to_name(VALUE self, + VALUE compression_value) { + Check_Type(compression_value, T_FIXNUM); + return grpc_rb_compression_options_level_value_to_name_internal( + (grpc_compression_level)NUM2INT(compression_value)); +} + +/* 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)); +} + +/* Wrapper of algorithm_to_name internal function available for ues and testing. + */ +VALUE grpc_rb_compression_options_algorithm_value_to_name( + VALUE self, VALUE algorithm_value) { + grpc_compression_algorithm algorithm_internal_value; + algorithm_internal_value = + (grpc_compression_algorithm)NUM2INT(algorithm_value); + + return grpc_rb_compression_options_algorithm_value_to_name_internal( + algorithm_internal_value); } /* Gets the internal value of the default compression level that is to be passed - * to the - * the GRPC core as a channel argument value. + * 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_default_algorithm_internal_value(VALUE self) { +VALUE grpc_rb_compression_options_get_default_algorithm_internal_value( + VALUE self) { grpc_rb_compression_options *wrapper = NULL; TypedData_Get_Struct(self, grpc_rb_compression_options, @@ -228,15 +366,27 @@ VALUE grpc_rb_compression_options_default_algorithm_internal_value(VALUE self) { if (wrapper->wrapped->default_algorithm.is_set) { return INT2NUM(wrapper->wrapped->default_algorithm.algorithm); - } else { - return Qnil; } + 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_default_level_internal_value(VALUE self) { +/* 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) { + VALUE algorithm_value = + grpc_rb_compression_options_get_default_algorithm_internal_value(self); + + if (RTEST(algorithm_value)) { + return grpc_rb_compression_options_algorithm_value_to_name(self, + algorithm_value); + } + + return Qnil; +} + +/* Gets the internal enum value of the default algorithm if one has been set. + * Returns nil if no default algorithm has been set. */ +VALUE grpc_rb_compression_options_get_default_level_internal_value(VALUE self) { grpc_rb_compression_options *wrapper = NULL; TypedData_Get_Struct(self, grpc_rb_compression_options, @@ -244,64 +394,121 @@ VALUE grpc_rb_compression_options_default_level_internal_value(VALUE self) { if (wrapper->wrapped->default_level.is_set) { return INT2NUM((int)wrapper->wrapped->default_level.level); - } else { - return Qnil; } + return Qnil; } -/* Disables compression algorithms by their names. Raises an error if an unkown - * name was passed. */ -VALUE grpc_rb_compression_options_disable_algorithms(int argc, VALUE *argv, - VALUE self) { - VALUE algorithm_names = Qnil; - VALUE ruby_str = Qnil; - grpc_compression_algorithm internal_algorithm_value; +/* 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; + VALUE ruby_value = + grpc_rb_compression_options_get_default_level_internal_value(self); + + if (RTEST(ruby_value)) { + internal_value = (grpc_compression_level)NUM2INT(ruby_value); + return grpc_rb_compression_options_level_value_to_name_internal( + internal_value); + } + + return Qnil; +} - /* read variadic argument list of names into the algorithm_name ruby array. */ - rb_scan_args(argc, argv, "0*", &algorithm_names); +/* Gets a list of the disabled algorithms as readable names. + * Returns an empty list of 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 (int i = 0; i < RARRAY_LEN(algorithm_names); i++) { - ruby_str = - rb_funcall(rb_ary_entry(algorithm_names, i), rb_intern("to_s"), 0); - grpc_rb_compression_options_get_internal_value_of_algorithm( - ruby_str, &internal_algorithm_value); - rb_funcall(self, rb_intern("disable_algorithm_internal"), 1, - LONG2NUM((long)internal_algorithm_value)); + 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; +} - return Qnil; +/* Provides a bitset as a ruby number that is suitable to pass to + * the GRPC core as a channel argument to enable compression algorithms. */ +VALUE grpc_rb_compression_options_get_enabled_algorithms_bitset(VALUE self) { + grpc_rb_compression_options *wrapper = NULL; + + TypedData_Get_Struct(self, grpc_rb_compression_options, + &grpc_rb_compression_options_data_type, wrapper); + return INT2NUM((int)wrapper->wrapped->enabled_algorithms_bitset); } -/* 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) { +/* 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; - grpc_compression_options *compression_options = NULL; - VALUE channel_arg_hash = rb_funcall(rb_cHash, rb_intern("new"), 0); + 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); - 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) { - rb_funcall(channel_arg_hash, rb_intern("[]="), 2, - rb_str_new2(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL), - INT2NUM((int)compression_options->default_level.level)); + /* 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); } - if (compression_options->default_algorithm.is_set) { - rb_funcall(channel_arg_hash, rb_intern("[]="), 2, - rb_str_new2(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM), - INT2NUM((int)compression_options->default_algorithm.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); } - rb_funcall(channel_arg_hash, rb_intern("[]="), 2, - rb_str_new2(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET), - INT2NUM((int)compression_options->enabled_algorithms_bitset)); + /* 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 channel_arg_hash; + return self; } void Init_grpc_compression_options() { @@ -312,40 +519,71 @@ void Init_grpc_compression_options() { rb_define_alloc_func(grpc_rb_cCompressionOptions, grpc_rb_compression_options_alloc); - /* Private method for disabling algorithms by a variadic list of names. */ - rb_define_private_method(grpc_rb_cCompressionOptions, "disable_algorithms", - grpc_rb_compression_options_disable_algorithms, -1); - /* Private method for disabling an algorithm by its enum value. */ - rb_define_private_method( - grpc_rb_cCompressionOptions, "disable_algorithm_internal", - grpc_rb_compression_options_disable_compression_algorithm_internal, 1); - - /* Private method for getting the bitset of enabled algorithms. */ - rb_define_private_method( - grpc_rb_cCompressionOptions, "enabled_algorithms_bitset", - grpc_rb_compression_options_get_enabled_algorithms_bitset, 0); - - /* Private method for setting the default algorithm by name. */ - rb_define_private_method(grpc_rb_cCompressionOptions, "set_default_algorithm", - grpc_rb_compression_options_set_default_algorithm, - 1); - /* Private method for getting the internal enum value of the default - * algorithm. */ - rb_define_private_method( + /* Initializes the ruby wrapper. #new method takes an optional hash argument. + */ + rb_define_method(grpc_rb_cCompressionOptions, "initialize", + grpc_rb_compression_options_init, -1); + + /* Gets the bitset of enabled algorithms. */ + rb_define_method(grpc_rb_cCompressionOptions, "enabled_algorithms_bitset", + grpc_rb_compression_options_get_enabled_algorithms_bitset, + 0); + + /* 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); + + /* Methods for getting the internal enum default algorithm and level enum + * values of an instance. */ + rb_define_method( grpc_rb_cCompressionOptions, "default_algorithm_internal_value", - grpc_rb_compression_options_default_algorithm_internal_value, 0); - - /* Private method for setting the default compression level by name. */ - rb_define_private_method(grpc_rb_cCompressionOptions, "set_default_level", - grpc_rb_compression_options_set_default_level, 1); - - /* Private method for getting the internal enum value of the default level. */ - rb_define_private_method( - grpc_rb_cCompressionOptions, "default_level_internal_value", - grpc_rb_compression_options_default_level_internal_value, 0); - - /* Public method for returning a hash of the compression settings suitable + grpc_rb_compression_options_get_default_algorithm_internal_value, 0); + rb_define_method(grpc_rb_cCompressionOptions, "default_level_internal_value", + grpc_rb_compression_options_get_default_level_internal_value, + 0); + + /* Determines whether or not an algorithm is enabled, given a readable + * algorithm name.*/ + rb_define_method(grpc_rb_cCompressionOptions, "is_algorithm_enabled", + grpc_rb_compression_options_is_algorithm_enabled, 1); + + /* Methods for converting to and from algorithm enum values and their readable + * names. */ + rb_define_singleton_method( + grpc_rb_cCompressionOptions, "algorithm_name_to_value", + grpc_rb_compression_options_algorithm_name_to_value, 1); + rb_define_singleton_method( + grpc_rb_cCompressionOptions, "algorithm_value_to_name", + grpc_rb_compression_options_algorithm_value_to_name, 1); + + /* Methods for converting to and from compression level enum values and their + * readable names. */ + rb_define_singleton_method(grpc_rb_cCompressionOptions, "level_name_to_value", + grpc_rb_compression_options_level_name_to_value, + 1); + rb_define_singleton_method(grpc_rb_cCompressionOptions, "level_value_to_name", + grpc_rb_compression_options_level_value_to_name, + 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 symbols for the names of the different compression levels. */ + rb_define_const(grpc_rb_cCompressionOptions, "COMPRESS_NONE_SYM", + ID2SYM(rb_intern("none"))); + rb_define_const(grpc_rb_cCompressionOptions, "COMPRESS_LOW_SYM", + ID2SYM(rb_intern("low"))); + rb_define_const(grpc_rb_cCompressionOptions, "COMPRESS_MEDIUM_SYM", + ID2SYM(rb_intern("medium"))); + rb_define_const(grpc_rb_cCompressionOptions, "COMPRESS_HIGH_SYM", + ID2SYM(rb_intern("high"))); } diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb index 2dae3a64d6..79fa705b1c 100644 --- a/src/ruby/lib/grpc.rb +++ b/src/ruby/lib/grpc.rb @@ -35,7 +35,6 @@ require_relative 'grpc/logconfig' require_relative 'grpc/notifier' require_relative 'grpc/version' require_relative 'grpc/core/time_consts' -require_relative 'grpc/core/compression_options' require_relative 'grpc/generic/active_call' require_relative 'grpc/generic/client_stub' require_relative 'grpc/generic/service' diff --git a/src/ruby/lib/grpc/core/compression_options.rb b/src/ruby/lib/grpc/core/compression_options.rb deleted file mode 100644 index 757c69f8e0..0000000000 --- a/src/ruby/lib/grpc/core/compression_options.rb +++ /dev/null @@ -1,93 +0,0 @@ -# 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. - -require_relative '../grpc' - -# GRPC contains the General RPC module. -module GRPC - module Core - # Wrapper for grpc_compression_options in core - # This class is defined as a C extension but is reopened here - # to add the initialization logic. - # - # This class wraps a GRPC core compression options. - # - # It can be used to create a channel argument key-value hash - # with keys and values that correspond to the compression settings - # provided here. - # - # call-seq: - # options = CompressionOptions.new( - # default_level: low, - # disabled_algorithms: [:<valid_algorithm_name>]) - # - # channel_args = Hash.new[...] - # channel_args_with_compression_args = channel_args.merge(options) - class CompressionOptions - alias_method :to_channel_arg_hash, :to_hash - - # Initializes a CompresionOptions instance. - # Starts out with all available compression - # algorithms enabled by default. - # - # Valid algorithms are those supported by the GRPC core - # - # @param default_level [String | Symbol] - # one of 'none', 'low', 'medium', 'high' - # @param default_algorithm [String | Symbol] - # a valid GRPC algorithm - # @param disabled_algorithms [Array<String, Symbol>] - # can contain valid GRPC algorithm names - def initialize(default_algorithm: nil, - default_level: nil, - disabled_algorithms: []) - # Convert possible symbols to strings for comparisons - disabled_algorithms = disabled_algorithms.map(&:to_s) - - if disabled_algorithms.include?(default_algorithm.to_s) - fail ArgumentError("#{default_algorithm} is in disabled_algorithms") - end - - set_default_algorithm(default_algorithm.to_s) unless - default_algorithm.nil? - - set_default_level(default_level.to_s) unless - default_level.nil? - - # *disabled_algorithms spreads array into variadic method parameters - disable_algorithms(*disabled_algorithms) unless - disabled_algorithms.nil? || disabled_algorithms.empty? - end - - def to_s - to_hash.to_s - end - end - end -end diff --git a/src/ruby/spec/compression_options_spec.rb b/src/ruby/spec/compression_options_spec.rb index 32c7a1bc94..0b05701869 100644 --- a/src/ruby/spec/compression_options_spec.rb +++ b/src/ruby/spec/compression_options_spec.rb @@ -59,6 +59,13 @@ describe GRPC::Core::CompressionOptions do high: 3 } + it 'compression level name constants should match expections' do + expect(GRPC::Core::CompressionOptions::COMPRESS_NONE_SYM).to eq(:none) + expect(GRPC::Core::CompressionOptions::COMPRESS_LOW_SYM).to eq(:low) + expect(GRPC::Core::CompressionOptions::COMPRESS_MEDIUM_SYM).to eq(:medium) + expect(GRPC::Core::CompressionOptions::COMPRESS_HIGH_SYM).to eq(:high) + end + it 'implements to_s' do expect { GRPC::Core::CompressionOptions.new.to_s }.to_not raise_error end @@ -79,23 +86,25 @@ describe GRPC::Core::CompressionOptions do it 'gives the correct channel args after everything has been disabled' do options = GRPC::Core::CompressionOptions.new( - default_algorithm: 'identity', - default_level: 'none', - disabled_algorithms: [:gzip, :deflate] + default_algorithm: :identity, + default_level: :none, + disabled_algorithms: ALGORITHMS.keys ) - expect(options.to_hash).to( - eql('grpc.default_compression_algorithm' => 0, - 'grpc.default_compression_level' => 0, - 'grpc.compression_enabled_algorithms_bitset' => 0x1) - ) + channel_arg_hash = options.to_hash + expect(channel_arg_hash['grpc.default_compression_algorithm']).to eq(0) + expect(channel_arg_hash['grpc.default_compression_level']).to eq(0) + + # Don't care if the "identity" algorithm bit is set or unset + bitset = channel_arg_hash['grpc.compression_enabled_algorithms_bitset'] + expect(bitset & ~ALGORITHM_BITS[:identity]).to eq(0) end it 'gives correct channel args with all args set' do options = GRPC::Core::CompressionOptions.new( - default_algorithm: 'gzip', - default_level: 'low', - disabled_algorithms: ['deflate'] + default_algorithm: :gzip, + default_level: :low, + disabled_algorithms: [:deflate] ) expected_bitset = ALL_ENABLED_BITSET & ~ALGORITHM_BITS[:deflate] @@ -109,8 +118,8 @@ describe GRPC::Core::CompressionOptions do it 'gives correct channel args when no algorithms are disabled' do options = GRPC::Core::CompressionOptions.new( - default_algorithm: 'identity', - default_level: 'high' + default_algorithm: :identity, + default_level: :high ) expect(options.to_hash).to( @@ -119,136 +128,181 @@ describe GRPC::Core::CompressionOptions do 'grpc.compression_enabled_algorithms_bitset' => ALL_ENABLED_BITSET) ) end + end - # Raising an error in when attempting to set the default algorithm - # to something that is also requested to be disabled - it 'gives raises an error when the chosen default algorithm is disabled' do - blk = proc do - GRPC::Core::CompressionOptions.new( - default_algorithm: :gzip, - disabled_algorithms: [:gzip]) - end + describe '#new with bad parameters' do + it 'should fail with more than one parameter' do + blk = proc { GRPC::Core::CompressionOptions.new(:gzip, :none) } expect { blk.call }.to raise_error end - end - # Test the private methods in the C extension that interact with - # the wrapped grpc_compression_options. - # - # Using #send to call private methods. - describe 'private internal methods' do - it 'mutating functions and accessors should be private' do - options = GRPC::Core::CompressionOptions.new + it 'should fail with a non-hash parameter' do + blk = proc { GRPC::Core::CompressionOptions.new(:gzip) } + expect { blk.call }.to raise_error + end + end - [:disable_algorithm_internal, - :disable_algorithms, - :set_default_algorithm, - :set_default_level, - :default_algorithm_internal_value, - :default_level_internal_value].each do |method_name| - expect(options.private_methods).to include(method_name) + describe '#level_name_to_value' do + it 'should give the correct internal values from compression level names' do + cls = GRPC::Core::CompressionOptions + COMPRESS_LEVELS.each_pair do |name, internal_value| + expect(cls.level_name_to_value(name)).to eq(internal_value) end end - describe '#disable_algorithms' do - ALGORITHMS.each_pair do |name, internal_value| - it "passes #{internal_value} to internal method for #{name}" do - options = GRPC::Core::CompressionOptions.new - expect(options).to receive(:disable_algorithm_internal) - .with(internal_value) - - options.send(:disable_algorithms, name) + [:gzip, :deflate, :any, Object.new, 'none', 'low', 1].each do |name| + it "should fail for parameter #{name} of class #{name.class}" do + blk = proc do + GRPC::Core::CompressionOptions.level_name_to_value(name) end + expect { blk.call }.to raise_error end + end + end - it 'should work with multiple parameters' do - options = GRPC::Core::CompressionOptions.new + describe '#level_value_to_name' do + it 'should give the correct internal values from compression level names' do + cls = GRPC::Core::CompressionOptions + COMPRESS_LEVELS.each_pair do |name, internal_value| + expect(cls.level_value_to_name(internal_value)).to eq(name) + end + end - ALGORITHMS.values do |internal_value| - expect(options).to receive(:disable_algorithm_internal) - .with(internal_value) + [:gzip, :any, Object.new, '1', :low].each do |name| + it "should fail for parameter #{name} of class #{name.class}" do + blk = proc do + GRPC::Core::CompressionOptions.level_value_to_name(name) end + expect { blk.call }.to raise_error + end + end + end - # disabled_algorithms is a private, variadic method - options.send(:disable_algorithms, *ALGORITHMS.keys) + describe '#algorithm_name_to_value' do + it 'should give the correct internal values from algorithm names' do + cls = GRPC::Core::CompressionOptions + ALGORITHMS.each_pair do |name, internal_value| + expect(cls.algorithm_name_to_value(name)).to eq(internal_value) end end - describe '#new default values' do - it 'should start out with all algorithms enabled' do - options = GRPC::Core::CompressionOptions.new - bitset = options.send(:enabled_algorithms_bitset) - expect(bitset).to eql(ALL_ENABLED_BITSET) + ['gzip', 'deflate', :any, Object.new, :none, :low, 1].each do |name| + it "should fail for parameter #{name} of class #{name.class}" do + blk = proc do + GRPC::Core::CompressionOptions.algorithm_name_to_value(name) + end + expect { blk.call }.to raise_error end + end + end - it 'should start out with no default algorithm' do - options = GRPC::Core::CompressionOptions.new - expect(options.send(:default_algorithm_internal_value)).to be_nil + describe '#algorithm_value_to_name' do + it 'should give the correct internal values from algorithm names' do + cls = GRPC::Core::CompressionOptions + ALGORITHMS.each_pair do |name, internal_value| + expect(cls.algorithm_value_to_name(internal_value)).to eq(name) end + end - it 'should start out with no default level' do - options = GRPC::Core::CompressionOptions.new - expect(options.send(:default_level_internal_value)).to be_nil + ['gzip', :deflate, :any, Object.new, :low, '1'].each do |value| + it "should fail for parameter #{value} of class #{value.class}" do + blk = proc do + GRPC::Core::CompressionOptions.algorithm_value_to_name(value) + end + expect { blk.call }.to raise_error end end + end - describe '#enabled_algoritms_bitset' do - it 'should respond to disabling one algorithm' do - options = GRPC::Core::CompressionOptions.new - options.send(:disable_algorithms, :gzip) - current_bitset = options.send(:enabled_algorithms_bitset) - expect(current_bitset & ALGORITHM_BITS[:gzip]).to be_zero + describe '#default_algorithm and #default_algorithm_internal_value' do + it 'can set the default algorithm and then read it back out' do + ALGORITHMS.each_pair do |name, internal_value| + options = GRPC::Core::CompressionOptions.new(default_algorithm: name) + expect(options.default_algorithm).to eq(name) + expect(options.default_algorithm_internal_value).to eq(internal_value) end + end - it 'should respond to disabling multiple algorithms' do - options = GRPC::Core::CompressionOptions.new + it 'returns nil if unset' do + options = GRPC::Core::CompressionOptions.new + expect(options.default_algorithm).to be_nil + expect(options.default_algorithm_internal_value).to be_nil + end + end - # splitting up algorithms array since #disable_algorithms is variadic - options.send(:disable_algorithms, *ALGORITHMS.keys) - current_bitset = options.send(:enabled_algorithms_bitset) - expect(current_bitset).to eql(ALL_DISABLED_BITSET) + describe '#default_level and #default_level_internal_value' do + it 'can set the default level and read it back out' do + COMPRESS_LEVELS.each_pair do |name, internal_value| + options = GRPC::Core::CompressionOptions.new(default_level: name) + expect(options.default_level).to eq(name) + expect(options.default_level_internal_value).to eq(internal_value) end end - describe 'setting the default algorithm by name' do - it 'should set the internal value of the default algorithm' do - ALGORITHMS.each_pair do |name, expected_internal_value| - options = GRPC::Core::CompressionOptions.new - options.send(:set_default_algorithm, name) - internal_value = options.send(:default_algorithm_internal_value) - expect(internal_value).to eql(expected_internal_value) - end - end + it 'returns nil if unset' do + options = GRPC::Core::CompressionOptions.new + expect(options.default_level).to be_nil + expect(options.default_level_internal_value).to be_nil + end + end - it 'should fail with invalid algorithm names' do - [:none, :low, :huffman, :unkown, Object.new, 1].each do |name| - blk = proc do - options = GRPC::CoreCompressionOptions.new - options.send(:set_default_algorithm, name) - end - expect { blk.call }.to raise_error - end + describe '#disabled_algorithms' do + it 'can set the disabled algorithms and read them back out' do + options = GRPC::Core::CompressionOptions.new( + disabled_algorithms: [:gzip, :deflate]) + + [:gzip, :deflate].each do |name| + expect(options.disabled_algorithms.include?(name)).to eq(true) end + expect(options.disabled_algorithms.size).to eq(2) end - describe 'setting the default level by name' do - it 'should set the internal value of the default compression value' do - COMPRESS_LEVELS.each_pair do |level, expected_internal_value| - options = GRPC::Core::CompressionOptions.new - options.send(:set_default_level, level) - internal_value = options.send(:default_level_internal_value) - expect(internal_value).to eql(expected_internal_value) - end - end + it 'returns an empty list if no algorithms were disabled' do + options = GRPC::Core::CompressionOptions.new + expect(options.disabled_algorithms).to eq([]) + end + end - it 'should fail with invalid names' do - [:identity, :gzip, :unkown, :any, Object.new, 1].each do |name| - blk = proc do - GRPC::Core::CompressionOptions.new.send(:set_default_level, name) - end - expect { blk.call }.to raise_error + describe '#is_algorithm_enabled' do + it 'returns true if the algorithm is valid and not disabled' do + options = GRPC::Core::CompressionOptions.new(disabled_algorithms: [:gzip]) + expect(options.is_algorithm_enabled(:deflate)).to eq(true) + end + + it 'returns false if the algorithm is valid and disabled' do + options = GRPC::Core::CompressionOptions.new(disabled_algorithms: [:gzip]) + expect(options.is_algorithm_enabled(:gzip)).to eq(false) + end + + [:none, :any, 'gzip', Object.new, 1].each do |name| + it "should fail for parameter ${name} of class #{name.class}" do + options = GRPC::Core::CompressionOptions.new( + disabled_algorithms: [:gzip]) + + blk = proc do + options.is_algorithm_enabled(name) end + expect { blk.call }.to raise_error end end end + + describe '#enabled_algoritms_bitset' do + it 'should respond to not disabling any algorithms' do + options = GRPC::Core::CompressionOptions.new + expect(options.enabled_algorithms_bitset).to eq(ALL_ENABLED_BITSET) + end + + it 'should respond to disabling one algorithm' do + options = GRPC::Core::CompressionOptions.new( + disabled_algorithms: [:gzip]) + expect(options.enabled_algorithms_bitset & ALGORITHM_BITS[:gzip]).to eq(0) + end + + it 'should respond to disabling multiple algorithms' do + options = GRPC::Core::CompressionOptions.new( + disabled_algorithms: ALGORITHMS.keys) + expect(options.enabled_algorithms_bitset).to eql(ALL_DISABLED_BITSET) + end + end end -- GitLab