blob: fa738a15da9b950d31b0bd94cbf23471eb0e7448 [file] [log] [blame]
kesavand2cde6582020-06-22 04:56:23 -04001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package proto
6
7import (
8 "sort"
9
10 "google.golang.org/protobuf/encoding/protowire"
11 "google.golang.org/protobuf/internal/encoding/messageset"
12 "google.golang.org/protobuf/internal/fieldsort"
13 "google.golang.org/protobuf/internal/mapsort"
14 "google.golang.org/protobuf/internal/pragma"
15 "google.golang.org/protobuf/reflect/protoreflect"
16 "google.golang.org/protobuf/runtime/protoiface"
17)
18
19// MarshalOptions configures the marshaler.
20//
21// Example usage:
22// b, err := MarshalOptions{Deterministic: true}.Marshal(m)
23type MarshalOptions struct {
24 pragma.NoUnkeyedLiterals
25
26 // AllowPartial allows messages that have missing required fields to marshal
27 // without returning an error. If AllowPartial is false (the default),
28 // Marshal will return an error if there are any missing required fields.
29 AllowPartial bool
30
31 // Deterministic controls whether the same message will always be
32 // serialized to the same bytes within the same binary.
33 //
34 // Setting this option guarantees that repeated serialization of
35 // the same message will return the same bytes, and that different
36 // processes of the same binary (which may be executing on different
37 // machines) will serialize equal messages to the same bytes.
38 // It has no effect on the resulting size of the encoded message compared
39 // to a non-deterministic marshal.
40 //
41 // Note that the deterministic serialization is NOT canonical across
42 // languages. It is not guaranteed to remain stable over time. It is
43 // unstable across different builds with schema changes due to unknown
44 // fields. Users who need canonical serialization (e.g., persistent
45 // storage in a canonical form, fingerprinting, etc.) must define
46 // their own canonicalization specification and implement their own
47 // serializer rather than relying on this API.
48 //
49 // If deterministic serialization is requested, map entries will be
50 // sorted by keys in lexographical order. This is an implementation
51 // detail and subject to change.
52 Deterministic bool
53
54 // UseCachedSize indicates that the result of a previous Size call
55 // may be reused.
56 //
57 // Setting this option asserts that:
58 //
59 // 1. Size has previously been called on this message with identical
60 // options (except for UseCachedSize itself).
61 //
62 // 2. The message and all its submessages have not changed in any
63 // way since the Size call.
64 //
65 // If either of these invariants is violated,
66 // the results are undefined and may include panics or corrupted output.
67 //
68 // Implementations MAY take this option into account to provide
69 // better performance, but there is no guarantee that they will do so.
70 // There is absolutely no guarantee that Size followed by Marshal with
71 // UseCachedSize set will perform equivalently to Marshal alone.
72 UseCachedSize bool
73}
74
75// Marshal returns the wire-format encoding of m.
76func Marshal(m Message) ([]byte, error) {
77 out, err := MarshalOptions{}.marshal(nil, m.ProtoReflect())
78 return out.Buf, err
79}
80
81// Marshal returns the wire-format encoding of m.
82func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
83 out, err := o.marshal(nil, m.ProtoReflect())
84 return out.Buf, err
85}
86
87// MarshalAppend appends the wire-format encoding of m to b,
88// returning the result.
89func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
90 out, err := o.marshal(b, m.ProtoReflect())
91 return out.Buf, err
92}
93
94// MarshalState returns the wire-format encoding of a message.
95//
96// This method permits fine-grained control over the marshaler.
97// Most users should use Marshal instead.
98func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error) {
99 return o.marshal(in.Buf, in.Message)
100}
101
102func (o MarshalOptions) marshal(b []byte, m protoreflect.Message) (out protoiface.MarshalOutput, err error) {
103 allowPartial := o.AllowPartial
104 o.AllowPartial = true
105 if methods := protoMethods(m); methods != nil && methods.Marshal != nil &&
106 !(o.Deterministic && methods.Flags&protoiface.SupportMarshalDeterministic == 0) {
107 in := protoiface.MarshalInput{
108 Message: m,
109 Buf: b,
110 }
111 if o.Deterministic {
112 in.Flags |= protoiface.MarshalDeterministic
113 }
114 if o.UseCachedSize {
115 in.Flags |= protoiface.MarshalUseCachedSize
116 }
117 if methods.Size != nil {
118 sout := methods.Size(protoiface.SizeInput{
119 Message: m,
120 Flags: in.Flags,
121 })
122 if cap(b) < len(b)+sout.Size {
123 in.Buf = make([]byte, len(b), growcap(cap(b), len(b)+sout.Size))
124 copy(in.Buf, b)
125 }
126 in.Flags |= protoiface.MarshalUseCachedSize
127 }
128 out, err = methods.Marshal(in)
129 } else {
130 out.Buf, err = o.marshalMessageSlow(b, m)
131 }
132 if err != nil {
133 return out, err
134 }
135 if allowPartial {
136 return out, nil
137 }
138 return out, checkInitialized(m)
139}
140
141func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
142 out, err := o.marshal(b, m)
143 return out.Buf, err
144}
145
146// growcap scales up the capacity of a slice.
147//
148// Given a slice with a current capacity of oldcap and a desired
149// capacity of wantcap, growcap returns a new capacity >= wantcap.
150//
151// The algorithm is mostly identical to the one used by append as of Go 1.14.
152func growcap(oldcap, wantcap int) (newcap int) {
153 if wantcap > oldcap*2 {
154 newcap = wantcap
155 } else if oldcap < 1024 {
156 // The Go 1.14 runtime takes this case when len(s) < 1024,
157 // not when cap(s) < 1024. The difference doesn't seem
158 // significant here.
159 newcap = oldcap * 2
160 } else {
161 newcap = oldcap
162 for 0 < newcap && newcap < wantcap {
163 newcap += newcap / 4
164 }
165 if newcap <= 0 {
166 newcap = wantcap
167 }
168 }
169 return newcap
170}
171
172func (o MarshalOptions) marshalMessageSlow(b []byte, m protoreflect.Message) ([]byte, error) {
173 if messageset.IsMessageSet(m.Descriptor()) {
174 return marshalMessageSet(b, m, o)
175 }
176 // There are many choices for what order we visit fields in. The default one here
177 // is chosen for reasonable efficiency and simplicity given the protoreflect API.
178 // It is not deterministic, since Message.Range does not return fields in any
179 // defined order.
180 //
181 // When using deterministic serialization, we sort the known fields.
182 var err error
183 o.rangeFields(m, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
184 b, err = o.marshalField(b, fd, v)
185 return err == nil
186 })
187 if err != nil {
188 return b, err
189 }
190 b = append(b, m.GetUnknown()...)
191 return b, nil
192}
193
194// rangeFields visits fields in a defined order when deterministic serialization is enabled.
195func (o MarshalOptions) rangeFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
196 if !o.Deterministic {
197 m.Range(f)
198 return
199 }
200 var fds []protoreflect.FieldDescriptor
201 m.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
202 fds = append(fds, fd)
203 return true
204 })
205 sort.Slice(fds, func(a, b int) bool {
206 return fieldsort.Less(fds[a], fds[b])
207 })
208 for _, fd := range fds {
209 if !f(fd, m.Get(fd)) {
210 break
211 }
212 }
213}
214
215func (o MarshalOptions) marshalField(b []byte, fd protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) {
216 switch {
217 case fd.IsList():
218 return o.marshalList(b, fd, value.List())
219 case fd.IsMap():
220 return o.marshalMap(b, fd, value.Map())
221 default:
222 b = protowire.AppendTag(b, fd.Number(), wireTypes[fd.Kind()])
223 return o.marshalSingular(b, fd, value)
224 }
225}
226
227func (o MarshalOptions) marshalList(b []byte, fd protoreflect.FieldDescriptor, list protoreflect.List) ([]byte, error) {
228 if fd.IsPacked() && list.Len() > 0 {
229 b = protowire.AppendTag(b, fd.Number(), protowire.BytesType)
230 b, pos := appendSpeculativeLength(b)
231 for i, llen := 0, list.Len(); i < llen; i++ {
232 var err error
233 b, err = o.marshalSingular(b, fd, list.Get(i))
234 if err != nil {
235 return b, err
236 }
237 }
238 b = finishSpeculativeLength(b, pos)
239 return b, nil
240 }
241
242 kind := fd.Kind()
243 for i, llen := 0, list.Len(); i < llen; i++ {
244 var err error
245 b = protowire.AppendTag(b, fd.Number(), wireTypes[kind])
246 b, err = o.marshalSingular(b, fd, list.Get(i))
247 if err != nil {
248 return b, err
249 }
250 }
251 return b, nil
252}
253
254func (o MarshalOptions) marshalMap(b []byte, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) ([]byte, error) {
255 keyf := fd.MapKey()
256 valf := fd.MapValue()
257 var err error
258 o.rangeMap(mapv, keyf.Kind(), func(key protoreflect.MapKey, value protoreflect.Value) bool {
259 b = protowire.AppendTag(b, fd.Number(), protowire.BytesType)
260 var pos int
261 b, pos = appendSpeculativeLength(b)
262
263 b, err = o.marshalField(b, keyf, key.Value())
264 if err != nil {
265 return false
266 }
267 b, err = o.marshalField(b, valf, value)
268 if err != nil {
269 return false
270 }
271 b = finishSpeculativeLength(b, pos)
272 return true
273 })
274 return b, err
275}
276
277func (o MarshalOptions) rangeMap(mapv protoreflect.Map, kind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
278 if !o.Deterministic {
279 mapv.Range(f)
280 return
281 }
282 mapsort.Range(mapv, kind, f)
283}
284
285// When encoding length-prefixed fields, we speculatively set aside some number of bytes
286// for the length, encode the data, and then encode the length (shifting the data if necessary
287// to make room).
288const speculativeLength = 1
289
290func appendSpeculativeLength(b []byte) ([]byte, int) {
291 pos := len(b)
292 b = append(b, "\x00\x00\x00\x00"[:speculativeLength]...)
293 return b, pos
294}
295
296func finishSpeculativeLength(b []byte, pos int) []byte {
297 mlen := len(b) - pos - speculativeLength
298 msiz := protowire.SizeVarint(uint64(mlen))
299 if msiz != speculativeLength {
300 for i := 0; i < msiz-speculativeLength; i++ {
301 b = append(b, 0)
302 }
303 copy(b[pos+msiz:], b[pos+speculativeLength:])
304 b = b[:pos+msiz+mlen]
305 }
306 protowire.AppendVarint(b[:pos], uint64(mlen))
307 return b
308}