static VALUE table_key(Map* self, VALUE key, char* buf, const char** out_key, size_t* out_length) { switch (self->key_type) { case UPB_TYPE_BYTES: case UPB_TYPE_STRING: // Strings: use string content directly. Check_Type(key, T_STRING); key = native_slot_encode_and_freeze_string(self->key_type, key); *out_key = RSTRING_PTR(key); *out_length = RSTRING_LEN(key); break; case UPB_TYPE_BOOL: case UPB_TYPE_INT32: case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: native_slot_set(self->key_type, Qnil, buf, key); *out_key = buf; *out_length = native_slot_size(self->key_type); break; default: // Map constructor should not allow a Map with another key type to be // constructed. assert(false); break; } return key; }
void native_slot_set_value_and_case(const char* name, upb_fieldtype_t type, VALUE type_class, void* memory, VALUE value, uint32_t* case_memory, uint32_t case_number) { // Note that in order to atomically change the value in memory and the case // value (w.r.t. Ruby VM calls), we must set the value at |memory| only after // all Ruby VM calls are complete. The case is then set at the bottom of this // function. switch (type) { case UPB_TYPE_FLOAT: if (!is_ruby_num(value)) { rb_raise(cTypeError, "Expected number type for float field '%s' (given %s).", name, rb_class2name(CLASS_OF(value))); } DEREF(memory, float) = NUM2DBL(value); break; case UPB_TYPE_DOUBLE: if (!is_ruby_num(value)) { rb_raise(cTypeError, "Expected number type for double field '%s' (given %s).", name, rb_class2name(CLASS_OF(value))); } DEREF(memory, double) = NUM2DBL(value); break; case UPB_TYPE_BOOL: { int8_t val = -1; if (value == Qtrue) { val = 1; } else if (value == Qfalse) { val = 0; } else { rb_raise(cTypeError, "Invalid argument for boolean field '%s' (given %s).", name, rb_class2name(CLASS_OF(value))); } DEREF(memory, int8_t) = val; break; } case UPB_TYPE_STRING: if (CLASS_OF(value) == rb_cSymbol) { value = rb_funcall(value, rb_intern("to_s"), 0); } else if (CLASS_OF(value) != rb_cString) { rb_raise(cTypeError, "Invalid argument for string field '%s' (given %s).", name, rb_class2name(CLASS_OF(value))); } DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); break; case UPB_TYPE_BYTES: { if (CLASS_OF(value) != rb_cString) { rb_raise(cTypeError, "Invalid argument for bytes field '%s' (given %s).", name, rb_class2name(CLASS_OF(value))); } DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); break; } case UPB_TYPE_MESSAGE: { if (CLASS_OF(value) == CLASS_OF(Qnil)) { value = Qnil; } else if (CLASS_OF(value) != type_class) { // check for possible implicit conversions VALUE converted_value = NULL; char* field_type_name = rb_class2name(type_class); if (strcmp(field_type_name, "Google::Protobuf::Timestamp") == 0 && rb_obj_is_kind_of(value, rb_cTime)) { // Time -> Google::Protobuf::Timestamp VALUE hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("seconds"), rb_funcall(value, rb_intern("to_i"), 0)); rb_hash_aset(hash, rb_str_new2("nanos"), rb_funcall(value, rb_intern("nsec"), 0)); VALUE args[1] = { hash }; converted_value = rb_class_new_instance(1, args, type_class); } else if (strcmp(field_type_name, "Google::Protobuf::Duration") == 0 && rb_obj_is_kind_of(value, rb_cNumeric)) { // Numeric -> Google::Protobuf::Duration VALUE hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("seconds"), rb_funcall(value, rb_intern("to_i"), 0)); VALUE n_value = rb_funcall(value, rb_intern("remainder"), 1, INT2NUM(1)); n_value = rb_funcall(n_value, rb_intern("*"), 1, INT2NUM(1000000000)); n_value = rb_funcall(n_value, rb_intern("round"), 0); rb_hash_aset(hash, rb_str_new2("nanos"), n_value); VALUE args[1] = { hash }; converted_value = rb_class_new_instance(1, args, type_class); } // raise if no suitable conversaion could be found if (converted_value == NULL) { rb_raise(cTypeError, "Invalid type %s to assign to submessage field '%s'.", rb_class2name(CLASS_OF(value)), name); } else { value = converted_value; } } DEREF(memory, VALUE) = value; break; } case UPB_TYPE_ENUM: { int32_t int_val = 0; if (TYPE(value) == T_STRING) { value = rb_funcall(value, rb_intern("to_sym"), 0); } else if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) { rb_raise(cTypeError, "Expected number or symbol type for enum field '%s'.", name); } if (TYPE(value) == T_SYMBOL) { // Ensure that the given symbol exists in the enum module. VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value); if (lookup == Qnil) { rb_raise(rb_eRangeError, "Unknown symbol value for enum field '%s'.", name); } else { int_val = NUM2INT(lookup); } } else { native_slot_check_int_range_precision(name, UPB_TYPE_INT32, value); int_val = NUM2INT(value); } DEREF(memory, int32_t) = int_val; break; } case UPB_TYPE_INT32: case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: native_slot_check_int_range_precision(name, type, value); switch (type) { case UPB_TYPE_INT32: DEREF(memory, int32_t) = NUM2INT(value); break; case UPB_TYPE_INT64: DEREF(memory, int64_t) = NUM2LL(value); break; case UPB_TYPE_UINT32: DEREF(memory, uint32_t) = NUM2UINT(value); break; case UPB_TYPE_UINT64: DEREF(memory, uint64_t) = NUM2ULL(value); break; default: break; } break; default: break; } if (case_memory != NULL) { *case_memory = case_number; } }
void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, void* memory, VALUE value, uint32_t* case_memory, uint32_t case_number) { // Note that in order to atomically change the value in memory and the case // value (w.r.t. Ruby VM calls), we must set the value at |memory| only after // all Ruby VM calls are complete. The case is then set at the bottom of this // function. switch (type) { case UPB_TYPE_FLOAT: if (!is_ruby_num(value)) { rb_raise(cTypeError, "Expected number type for float field."); } DEREF(memory, float) = NUM2DBL(value); break; case UPB_TYPE_DOUBLE: if (!is_ruby_num(value)) { rb_raise(cTypeError, "Expected number type for double field."); } DEREF(memory, double) = NUM2DBL(value); break; case UPB_TYPE_BOOL: { int8_t val = -1; if (value == Qtrue) { val = 1; } else if (value == Qfalse) { val = 0; } else { rb_raise(cTypeError, "Invalid argument for boolean field."); } DEREF(memory, int8_t) = val; break; } case UPB_TYPE_STRING: if (CLASS_OF(value) == rb_cSymbol) { value = rb_funcall(value, rb_intern("to_s"), 0); } else if (CLASS_OF(value) != rb_cString) { rb_raise(cTypeError, "Invalid argument for string field."); } DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); break; case UPB_TYPE_BYTES: { if (CLASS_OF(value) != rb_cString) { rb_raise(cTypeError, "Invalid argument for string field."); } DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); break; } case UPB_TYPE_MESSAGE: { if (CLASS_OF(value) == CLASS_OF(Qnil)) { value = Qnil; } else if (CLASS_OF(value) != type_class) { rb_raise(cTypeError, "Invalid type %s to assign to submessage field.", rb_class2name(CLASS_OF(value))); } DEREF(memory, VALUE) = value; break; } case UPB_TYPE_ENUM: { int32_t int_val = 0; if (TYPE(value) == T_STRING) { value = rb_funcall(value, rb_intern("to_sym"), 0); } else if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) { rb_raise(cTypeError, "Expected number or symbol type for enum field."); } if (TYPE(value) == T_SYMBOL) { // Ensure that the given symbol exists in the enum module. VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value); if (lookup == Qnil) { rb_raise(rb_eRangeError, "Unknown symbol value for enum field."); } else { int_val = NUM2INT(lookup); } } else { native_slot_check_int_range_precision(UPB_TYPE_INT32, value); int_val = NUM2INT(value); } DEREF(memory, int32_t) = int_val; break; } case UPB_TYPE_INT32: case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: native_slot_check_int_range_precision(type, value); switch (type) { case UPB_TYPE_INT32: DEREF(memory, int32_t) = NUM2INT(value); break; case UPB_TYPE_INT64: DEREF(memory, int64_t) = NUM2LL(value); break; case UPB_TYPE_UINT32: DEREF(memory, uint32_t) = NUM2UINT(value); break; case UPB_TYPE_UINT64: DEREF(memory, uint64_t) = NUM2ULL(value); break; default: break; } break; default: break; } if (case_memory != NULL) { *case_memory = case_number; } }