| khenaidoo | a46458b | 2021-12-15 16:50:44 -0500 | [diff] [blame] | 1 | package dynamic |
| 2 | |
| 3 | // Binary serialization and de-serialization for dynamic messages |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| khenaidoo | a46458b | 2021-12-15 16:50:44 -0500 | [diff] [blame] | 7 | "io" |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 8 | |
| 9 | "github.com/golang/protobuf/proto" |
| 10 | |
| 11 | "github.com/jhump/protoreflect/codec" |
| khenaidoo | a46458b | 2021-12-15 16:50:44 -0500 | [diff] [blame] | 12 | ) |
| 13 | |
| 14 | // defaultDeterminism, if true, will mean that calls to Marshal will produce |
| 15 | // deterministic output. This is used to make the output of proto.Marshal(...) |
| 16 | // deterministic (since there is no way to have that convey determinism intent). |
| 17 | // **This is only used from tests.** |
| 18 | var defaultDeterminism = false |
| 19 | |
| 20 | // Marshal serializes this message to bytes, returning an error if the operation |
| 21 | // fails. The resulting bytes are in the standard protocol buffer binary format. |
| 22 | func (m *Message) Marshal() ([]byte, error) { |
| 23 | var b codec.Buffer |
| 24 | b.SetDeterministic(defaultDeterminism) |
| 25 | if err := m.marshal(&b); err != nil { |
| 26 | return nil, err |
| 27 | } |
| 28 | return b.Bytes(), nil |
| 29 | } |
| 30 | |
| 31 | // MarshalAppend behaves exactly the same as Marshal, except instead of allocating a |
| 32 | // new byte slice to marshal into, it uses the provided byte slice. The backing array |
| 33 | // for the returned byte slice *may* be the same as the one that was passed in, but |
| 34 | // it's not guaranteed as a new backing array will automatically be allocated if |
| 35 | // more bytes need to be written than the provided buffer has capacity for. |
| 36 | func (m *Message) MarshalAppend(b []byte) ([]byte, error) { |
| 37 | codedBuf := codec.NewBuffer(b) |
| 38 | codedBuf.SetDeterministic(defaultDeterminism) |
| 39 | if err := m.marshal(codedBuf); err != nil { |
| 40 | return nil, err |
| 41 | } |
| 42 | return codedBuf.Bytes(), nil |
| 43 | } |
| 44 | |
| 45 | // MarshalDeterministic serializes this message to bytes in a deterministic way, |
| 46 | // returning an error if the operation fails. This differs from Marshal in that |
| 47 | // map keys will be sorted before serializing to bytes. The protobuf spec does |
| 48 | // not define ordering for map entries, so Marshal will use standard Go map |
| 49 | // iteration order (which will be random). But for cases where determinism is |
| 50 | // more important than performance, use this method instead. |
| 51 | func (m *Message) MarshalDeterministic() ([]byte, error) { |
| 52 | var b codec.Buffer |
| 53 | b.SetDeterministic(true) |
| 54 | if err := m.marshal(&b); err != nil { |
| 55 | return nil, err |
| 56 | } |
| 57 | return b.Bytes(), nil |
| 58 | } |
| 59 | |
| 60 | // MarshalAppendDeterministic behaves exactly the same as MarshalDeterministic, |
| 61 | // except instead of allocating a new byte slice to marshal into, it uses the |
| 62 | // provided byte slice. The backing array for the returned byte slice *may* be |
| 63 | // the same as the one that was passed in, but it's not guaranteed as a new |
| 64 | // backing array will automatically be allocated if more bytes need to be written |
| 65 | // than the provided buffer has capacity for. |
| 66 | func (m *Message) MarshalAppendDeterministic(b []byte) ([]byte, error) { |
| 67 | codedBuf := codec.NewBuffer(b) |
| 68 | codedBuf.SetDeterministic(true) |
| 69 | if err := m.marshal(codedBuf); err != nil { |
| 70 | return nil, err |
| 71 | } |
| 72 | return codedBuf.Bytes(), nil |
| 73 | } |
| 74 | |
| 75 | func (m *Message) marshal(b *codec.Buffer) error { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 76 | if m.GetMessageDescriptor().GetMessageOptions().GetMessageSetWireFormat() { |
| 77 | return fmt.Errorf("%s is a message set; marshaling message sets is not implemented", m.GetMessageDescriptor().GetFullyQualifiedName()) |
| 78 | } |
| khenaidoo | a46458b | 2021-12-15 16:50:44 -0500 | [diff] [blame] | 79 | if err := m.marshalKnownFields(b); err != nil { |
| 80 | return err |
| 81 | } |
| 82 | return m.marshalUnknownFields(b) |
| 83 | } |
| 84 | |
| 85 | func (m *Message) marshalKnownFields(b *codec.Buffer) error { |
| 86 | for _, tag := range m.knownFieldTags() { |
| 87 | itag := int32(tag) |
| 88 | val := m.values[itag] |
| 89 | fd := m.FindFieldDescriptor(itag) |
| 90 | if fd == nil { |
| 91 | panic(fmt.Sprintf("Couldn't find field for tag %d", itag)) |
| 92 | } |
| 93 | if err := b.EncodeFieldValue(fd, val); err != nil { |
| 94 | return err |
| 95 | } |
| 96 | } |
| 97 | return nil |
| 98 | } |
| 99 | |
| 100 | func (m *Message) marshalUnknownFields(b *codec.Buffer) error { |
| 101 | for _, tag := range m.unknownFieldTags() { |
| 102 | itag := int32(tag) |
| 103 | sl := m.unknownFields[itag] |
| 104 | for _, u := range sl { |
| 105 | if err := b.EncodeTagAndWireType(itag, u.Encoding); err != nil { |
| 106 | return err |
| 107 | } |
| 108 | switch u.Encoding { |
| 109 | case proto.WireBytes: |
| 110 | if err := b.EncodeRawBytes(u.Contents); err != nil { |
| 111 | return err |
| 112 | } |
| 113 | case proto.WireStartGroup: |
| 114 | _, _ = b.Write(u.Contents) |
| 115 | if err := b.EncodeTagAndWireType(itag, proto.WireEndGroup); err != nil { |
| 116 | return err |
| 117 | } |
| 118 | case proto.WireFixed32: |
| 119 | if err := b.EncodeFixed32(u.Value); err != nil { |
| 120 | return err |
| 121 | } |
| 122 | case proto.WireFixed64: |
| 123 | if err := b.EncodeFixed64(u.Value); err != nil { |
| 124 | return err |
| 125 | } |
| 126 | case proto.WireVarint: |
| 127 | if err := b.EncodeVarint(u.Value); err != nil { |
| 128 | return err |
| 129 | } |
| 130 | default: |
| 131 | return codec.ErrBadWireType |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | return nil |
| 136 | } |
| 137 | |
| 138 | // Unmarshal de-serializes the message that is present in the given bytes into |
| 139 | // this message. It first resets the current message. It returns an error if the |
| 140 | // given bytes do not contain a valid encoding of this message type. |
| 141 | func (m *Message) Unmarshal(b []byte) error { |
| 142 | m.Reset() |
| 143 | if err := m.UnmarshalMerge(b); err != nil { |
| 144 | return err |
| 145 | } |
| 146 | return m.Validate() |
| 147 | } |
| 148 | |
| 149 | // UnmarshalMerge de-serializes the message that is present in the given bytes |
| 150 | // into this message. Unlike Unmarshal, it does not first reset the message, |
| 151 | // instead merging the data in the given bytes into the existing data in this |
| 152 | // message. |
| 153 | func (m *Message) UnmarshalMerge(b []byte) error { |
| 154 | return m.unmarshal(codec.NewBuffer(b), false) |
| 155 | } |
| 156 | |
| 157 | func (m *Message) unmarshal(buf *codec.Buffer, isGroup bool) error { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 158 | if m.GetMessageDescriptor().GetMessageOptions().GetMessageSetWireFormat() { |
| 159 | return fmt.Errorf("%s is a message set; unmarshaling message sets is not implemented", m.GetMessageDescriptor().GetFullyQualifiedName()) |
| 160 | } |
| khenaidoo | a46458b | 2021-12-15 16:50:44 -0500 | [diff] [blame] | 161 | for !buf.EOF() { |
| 162 | fd, val, err := buf.DecodeFieldValue(m.FindFieldDescriptor, m.mf) |
| 163 | if err != nil { |
| 164 | if err == codec.ErrWireTypeEndGroup { |
| 165 | if isGroup { |
| 166 | // finished parsing group |
| 167 | return nil |
| 168 | } |
| 169 | return codec.ErrBadWireType |
| 170 | } |
| 171 | return err |
| 172 | } |
| 173 | |
| 174 | if fd == nil { |
| 175 | if m.unknownFields == nil { |
| 176 | m.unknownFields = map[int32][]UnknownField{} |
| 177 | } |
| 178 | uv := val.(codec.UnknownField) |
| 179 | u := UnknownField{ |
| 180 | Encoding: uv.Encoding, |
| 181 | Value: uv.Value, |
| 182 | Contents: uv.Contents, |
| 183 | } |
| 184 | m.unknownFields[uv.Tag] = append(m.unknownFields[uv.Tag], u) |
| 185 | } else if err := mergeField(m, fd, val); err != nil { |
| 186 | return err |
| 187 | } |
| 188 | } |
| 189 | if isGroup { |
| 190 | return io.ErrUnexpectedEOF |
| 191 | } |
| 192 | return nil |
| 193 | } |