proto: add IsInitialized
Move all checks for required fields into a proto.IsInitialized function.
Initial testing makes me confident that we can provide a fast-path
implementation of IsInitialized which will perform more than
acceptably. (In the degenerate-but-common case where a message
transitively contains no required fields, this check can be nearly
zero cost.)
Unifying checks into a single function provides consistent behavior
between the wire, text, and json codecs.
Performing the check after decoding eliminates the wire decoder bug
where a split message is incorrectly seen as missing required fields.
Performing the check after decoding also provides consistent and
arguably more correct behavior when the target message was partially
prepopulated.
Change-Id: I9478b7bebb263af00c0d9f66a1f26e31ff553522
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170787
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/proto/encode.go b/proto/encode.go
index adf3de4..fc3ce92 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -69,10 +69,18 @@
// MarshalAppend appends the wire-format encoding of m to b,
// returning the result.
func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
- if b, err := o.marshalMessageFast(b, m); err != errInternalNoFast {
+ b, err := o.marshalMessageFast(b, m)
+ if err == errInternalNoFast {
+ b, err = o.marshalMessage(b, m.ProtoReflect())
+ }
+ var nerr errors.NonFatal
+ if !nerr.Merge(err) {
return b, err
}
- return o.marshalMessage(b, m.ProtoReflect())
+ if !o.AllowPartial {
+ nerr.Merge(IsInitialized(m))
+ }
+ return b, nerr.E
}
func (o MarshalOptions) marshalMessageFast(b []byte, m Message) ([]byte, error) {
@@ -129,9 +137,6 @@
b = append(b, raw...)
return true
})
- if !o.AllowPartial {
- checkRequiredFields(m, &nerr)
- }
return b, nerr.E
}