blob: 91fd6723ab8a3542c3cf896cacb074e0069b8ae6 [file] [log] [blame]
package dynamic
// Binary serialization and de-serialization for dynamic messages
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/jhump/protoreflect/codec"
"io"
)
// defaultDeterminism, if true, will mean that calls to Marshal will produce
// deterministic output. This is used to make the output of proto.Marshal(...)
// deterministic (since there is no way to have that convey determinism intent).
// **This is only used from tests.**
var defaultDeterminism = false
// Marshal serializes this message to bytes, returning an error if the operation
// fails. The resulting bytes are in the standard protocol buffer binary format.
func (m *Message) Marshal() ([]byte, error) {
var b codec.Buffer
b.SetDeterministic(defaultDeterminism)
if err := m.marshal(&b); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// MarshalAppend behaves exactly the same as Marshal, except instead of allocating a
// new byte slice to marshal into, it uses the provided byte slice. The backing array
// for the returned byte slice *may* be the same as the one that was passed in, but
// it's not guaranteed as a new backing array will automatically be allocated if
// more bytes need to be written than the provided buffer has capacity for.
func (m *Message) MarshalAppend(b []byte) ([]byte, error) {
codedBuf := codec.NewBuffer(b)
codedBuf.SetDeterministic(defaultDeterminism)
if err := m.marshal(codedBuf); err != nil {
return nil, err
}
return codedBuf.Bytes(), nil
}
// MarshalDeterministic serializes this message to bytes in a deterministic way,
// returning an error if the operation fails. This differs from Marshal in that
// map keys will be sorted before serializing to bytes. The protobuf spec does
// not define ordering for map entries, so Marshal will use standard Go map
// iteration order (which will be random). But for cases where determinism is
// more important than performance, use this method instead.
func (m *Message) MarshalDeterministic() ([]byte, error) {
var b codec.Buffer
b.SetDeterministic(true)
if err := m.marshal(&b); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// MarshalAppendDeterministic behaves exactly the same as MarshalDeterministic,
// except instead of allocating a new byte slice to marshal into, it uses the
// provided byte slice. The backing array for the returned byte slice *may* be
// the same as the one that was passed in, but it's not guaranteed as a new
// backing array will automatically be allocated if more bytes need to be written
// than the provided buffer has capacity for.
func (m *Message) MarshalAppendDeterministic(b []byte) ([]byte, error) {
codedBuf := codec.NewBuffer(b)
codedBuf.SetDeterministic(true)
if err := m.marshal(codedBuf); err != nil {
return nil, err
}
return codedBuf.Bytes(), nil
}
func (m *Message) marshal(b *codec.Buffer) error {
if err := m.marshalKnownFields(b); err != nil {
return err
}
return m.marshalUnknownFields(b)
}
func (m *Message) marshalKnownFields(b *codec.Buffer) error {
for _, tag := range m.knownFieldTags() {
itag := int32(tag)
val := m.values[itag]
fd := m.FindFieldDescriptor(itag)
if fd == nil {
panic(fmt.Sprintf("Couldn't find field for tag %d", itag))
}
if err := b.EncodeFieldValue(fd, val); err != nil {
return err
}
}
return nil
}
func (m *Message) marshalUnknownFields(b *codec.Buffer) error {
for _, tag := range m.unknownFieldTags() {
itag := int32(tag)
sl := m.unknownFields[itag]
for _, u := range sl {
if err := b.EncodeTagAndWireType(itag, u.Encoding); err != nil {
return err
}
switch u.Encoding {
case proto.WireBytes:
if err := b.EncodeRawBytes(u.Contents); err != nil {
return err
}
case proto.WireStartGroup:
_, _ = b.Write(u.Contents)
if err := b.EncodeTagAndWireType(itag, proto.WireEndGroup); err != nil {
return err
}
case proto.WireFixed32:
if err := b.EncodeFixed32(u.Value); err != nil {
return err
}
case proto.WireFixed64:
if err := b.EncodeFixed64(u.Value); err != nil {
return err
}
case proto.WireVarint:
if err := b.EncodeVarint(u.Value); err != nil {
return err
}
default:
return codec.ErrBadWireType
}
}
}
return nil
}
// Unmarshal de-serializes the message that is present in the given bytes into
// this message. It first resets the current message. It returns an error if the
// given bytes do not contain a valid encoding of this message type.
func (m *Message) Unmarshal(b []byte) error {
m.Reset()
if err := m.UnmarshalMerge(b); err != nil {
return err
}
return m.Validate()
}
// UnmarshalMerge de-serializes the message that is present in the given bytes
// into this message. Unlike Unmarshal, it does not first reset the message,
// instead merging the data in the given bytes into the existing data in this
// message.
func (m *Message) UnmarshalMerge(b []byte) error {
return m.unmarshal(codec.NewBuffer(b), false)
}
func (m *Message) unmarshal(buf *codec.Buffer, isGroup bool) error {
for !buf.EOF() {
fd, val, err := buf.DecodeFieldValue(m.FindFieldDescriptor, m.mf)
if err != nil {
if err == codec.ErrWireTypeEndGroup {
if isGroup {
// finished parsing group
return nil
}
return codec.ErrBadWireType
}
return err
}
if fd == nil {
if m.unknownFields == nil {
m.unknownFields = map[int32][]UnknownField{}
}
uv := val.(codec.UnknownField)
u := UnknownField{
Encoding: uv.Encoding,
Value: uv.Value,
Contents: uv.Contents,
}
m.unknownFields[uv.Tag] = append(m.unknownFields[uv.Tag], u)
} else if err := mergeField(m, fd, val); err != nil {
return err
}
}
if isGroup {
return io.ErrUnexpectedEOF
}
return nil
}