internal/impl: avoid preserving presence on proto3 bytes field
When setting a proto3 bytes field to an empty, non-nil bytes slice,
just store a nil slice in the underderlying storage.
This is done to avoid presenting the illusion to the user that
presence is preserved for proto3.
Updates golang/protobuf#896
Change-Id: I1b97bedd547d336863c65d9418d8f07edf69ccd6
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185577
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index f3b4f8a..7cba71d 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -204,11 +204,15 @@
}
}
-var emptyBytes = reflect.ValueOf([]byte{})
+var (
+ nilBytes = reflect.ValueOf([]byte(nil))
+ emptyBytes = reflect.ValueOf([]byte{})
+)
func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
nullable := fd.Syntax() == pref.Proto2
+ isBytes := ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8
if nullable {
if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid type: got %v, want pointer", ft))
@@ -274,8 +278,12 @@
rv = rv.Elem()
}
rv.Set(conv.GoValueOf(v))
- if nullable && rv.Kind() == reflect.Slice && rv.IsNil() {
- rv.Set(emptyBytes)
+ if isBytes && rv.Len() == 0 {
+ if nullable {
+ rv.Set(emptyBytes) // preserve presence in proto2
+ } else {
+ rv.Set(nilBytes) // do not preserve presence in proto3
+ }
}
},
}