Index: third_party/protobuf/ruby/ext/google/protobuf_c/storage.c |
diff --git a/third_party/protobuf/ruby/ext/google/protobuf_c/storage.c b/third_party/protobuf/ruby/ext/google/protobuf_c/storage.c |
index b1f65f413b53d3b46047c83c46a55103d9134596..3ff2bda6bbc146b0efbd178be6326f8ad9b51ddb 100644 |
--- a/third_party/protobuf/ruby/ext/google/protobuf_c/storage.c |
+++ b/third_party/protobuf/ruby/ext/google/protobuf_c/storage.c |
@@ -57,6 +57,37 @@ size_t native_slot_size(upb_fieldtype_t type) { |
} |
} |
+static VALUE value_from_default(const upb_fielddef *field) { |
+ switch (upb_fielddef_type(field)) { |
+ case UPB_TYPE_FLOAT: return DBL2NUM(upb_fielddef_defaultfloat(field)); |
+ case UPB_TYPE_DOUBLE: return DBL2NUM(upb_fielddef_defaultdouble(field)); |
+ case UPB_TYPE_BOOL: |
+ return upb_fielddef_defaultbool(field) ? Qtrue : Qfalse; |
+ case UPB_TYPE_MESSAGE: return Qnil; |
+ case UPB_TYPE_ENUM: { |
+ const upb_enumdef *enumdef = upb_fielddef_enumsubdef(field); |
+ int32_t num = upb_fielddef_defaultint32(field); |
+ const char *label = upb_enumdef_iton(enumdef, num); |
+ if (label) { |
+ return ID2SYM(rb_intern(label)); |
+ } else { |
+ return INT2NUM(num); |
+ } |
+ } |
+ case UPB_TYPE_INT32: return INT2NUM(upb_fielddef_defaultint32(field)); |
+ case UPB_TYPE_INT64: return LL2NUM(upb_fielddef_defaultint64(field));; |
+ case UPB_TYPE_UINT32: return UINT2NUM(upb_fielddef_defaultuint32(field)); |
+ case UPB_TYPE_UINT64: return ULL2NUM(upb_fielddef_defaultuint64(field)); |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: { |
+ size_t size; |
+ const char *str = upb_fielddef_defaultstr(field, &size); |
+ return rb_str_new(str, size); |
+ } |
+ default: return Qnil; |
+ } |
+} |
+ |
static bool is_ruby_num(VALUE value) { |
return (TYPE(value) == T_FLOAT || |
TYPE(value) == T_FIXNUM || |
@@ -86,25 +117,24 @@ void native_slot_check_int_range_precision(upb_fieldtype_t type, VALUE val) { |
} |
} |
-void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value) { |
- bool bad_encoding = false; |
- rb_encoding* string_encoding = rb_enc_from_index(ENCODING_GET(value)); |
- if (type == UPB_TYPE_STRING) { |
- bad_encoding = |
- string_encoding != kRubyStringUtf8Encoding && |
- string_encoding != kRubyStringASCIIEncoding; |
- } else { |
- bad_encoding = |
- string_encoding != kRubyString8bitEncoding; |
- } |
- // Check that encoding is UTF-8 or ASCII (for string fields) or ASCII-8BIT |
- // (for bytes fields). |
- if (bad_encoding) { |
- rb_raise(rb_eTypeError, "Encoding for '%s' fields must be %s (was %s)", |
- (type == UPB_TYPE_STRING) ? "string" : "bytes", |
- (type == UPB_TYPE_STRING) ? "UTF-8 or ASCII" : "ASCII-8BIT", |
- rb_enc_name(string_encoding)); |
+VALUE native_slot_encode_and_freeze_string(upb_fieldtype_t type, VALUE value) { |
+ rb_encoding* desired_encoding = (type == UPB_TYPE_STRING) ? |
+ kRubyStringUtf8Encoding : kRubyString8bitEncoding; |
+ VALUE desired_encoding_value = rb_enc_from_encoding(desired_encoding); |
+ |
+ // Note: this will not duplicate underlying string data unless necessary. |
+ value = rb_str_encode(value, desired_encoding_value, 0, Qnil); |
+ |
+ if (type == UPB_TYPE_STRING && |
+ rb_enc_str_coderange(value) == ENC_CODERANGE_BROKEN) { |
+ rb_raise(rb_eEncodingError, "String is invalid UTF-8"); |
} |
+ |
+ // Ensure the data remains valid. Since we called #encode a moment ago, |
+ // this does not freeze the string the user assigned. |
+ rb_obj_freeze(value); |
+ |
+ return value; |
} |
void native_slot_set(upb_fieldtype_t type, VALUE type_class, |
@@ -150,8 +180,8 @@ void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, |
if (CLASS_OF(value) != rb_cString) { |
rb_raise(rb_eTypeError, "Invalid argument for string field."); |
} |
- native_slot_validate_string_encoding(type, value); |
- DEREF(memory, VALUE) = value; |
+ |
+ DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); |
break; |
} |
case UPB_TYPE_MESSAGE: { |
@@ -537,7 +567,7 @@ VALUE layout_get(MessageLayout* layout, |
if (upb_fielddef_containingoneof(field)) { |
if (*oneof_case != upb_fielddef_number(field)) { |
- return Qnil; |
+ return value_from_default(field); |
} |
return native_slot_get(upb_fielddef_type(field), |
field_type_class(field), |