186 lines
5.6 KiB
Go
186 lines
5.6 KiB
Go
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
|
|
}
|