blob: b55389f8aa4418aeb07da287f782e362f81be458 [file] [log] [blame]
Damien Neil99f24c32019-03-13 17:06:42 -07001// 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 "fmt"
9 "sort"
10
Damien Neile89e6242019-05-13 23:55:40 -070011 "google.golang.org/protobuf/internal/encoding/wire"
12 "google.golang.org/protobuf/internal/errors"
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"
Damien Neil99f24c32019-03-13 17:06:42 -070017)
18
19// MarshalOptions configures the marshaler.
20//
21// Example usage:
22// b, err := MarshalOptions{Deterministic: true}.Marshal(m)
23type MarshalOptions struct {
Damien Neil96c229a2019-04-03 12:17:24 -070024 // AllowPartial allows messages that have missing required fields to marshal
25 // without returning an error. If AllowPartial is false (the default),
26 // Marshal will return an error if there are any missing required fields.
27 AllowPartial bool
28
Damien Neil99f24c32019-03-13 17:06:42 -070029 // Deterministic controls whether the same message will always be
30 // serialized to the same bytes within the same binary.
31 //
32 // Setting this option guarantees that repeated serialization of
33 // the same message will return the same bytes, and that different
34 // processes of the same binary (which may be executing on different
35 // machines) will serialize equal messages to the same bytes.
36 //
37 // Note that the deterministic serialization is NOT canonical across
38 // languages. It is not guaranteed to remain stable over time. It is
39 // unstable across different builds with schema changes due to unknown
40 // fields. Users who need canonical serialization (e.g., persistent
41 // storage in a canonical form, fingerprinting, etc.) must define
42 // their own canonicalization specification and implement their own
43 // serializer rather than relying on this API.
44 //
45 // If deterministic serialization is requested, map entries will be
46 // sorted by keys in lexographical order. This is an implementation
47 // detail and subject to change.
48 Deterministic bool
49
Damien Neil03e74862019-04-07 18:18:31 -070050 // UseCachedSize indicates that the result of a previous Size call
51 // may be reused.
52 //
53 // Setting this option asserts that:
54 //
55 // 1. Size has previously been called on this message with identical
56 // options (except for UseCachedSize itself).
57 //
58 // 2. The message and all its submessages have not changed in any
59 // way since the Size call.
60 //
61 // If either of these invariants is broken, the results are undefined
62 // but may include panics or invalid output.
63 //
64 // Implementations MAY take this option into account to provide
65 // better performance, but there is no guarantee that they will do so.
66 // There is absolutely no guarantee that Size followed by Marshal with
67 // UseCachedSize set will perform equivalently to Marshal alone.
68 UseCachedSize bool
69
Damien Neil99f24c32019-03-13 17:06:42 -070070 pragma.NoUnkeyedLiterals
71}
72
Damien Neil0d3e8cc2019-04-01 13:31:55 -070073var _ = protoiface.MarshalOptions(MarshalOptions{})
74
Damien Neil99f24c32019-03-13 17:06:42 -070075// Marshal returns the wire-format encoding of m.
76func Marshal(m Message) ([]byte, error) {
77 return MarshalOptions{}.MarshalAppend(nil, m)
78}
79
80// Marshal returns the wire-format encoding of m.
81func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
Damien Neil0d3e8cc2019-04-01 13:31:55 -070082 return o.MarshalAppend(nil, m)
Damien Neil99f24c32019-03-13 17:06:42 -070083}
84
85// MarshalAppend appends the wire-format encoding of m to b,
86// returning the result.
87func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
Damien Neilc37adef2019-04-01 13:49:56 -070088 // Set AllowPartial in recursive calls to marshal to avoid duplicating
89 // effort with the single initialization check below.
90 allowPartial := o.AllowPartial
91 o.AllowPartial = true
Damien Neil3016b732019-04-07 12:43:10 -070092 out, err := o.marshalMessageFast(b, m)
Damien Neil4686e232019-04-05 13:31:40 -070093 if err == errInternalNoFast {
Damien Neil3016b732019-04-07 12:43:10 -070094 out, err = o.marshalMessage(b, m.ProtoReflect())
Damien Neil4686e232019-04-05 13:31:40 -070095 }
96 var nerr errors.NonFatal
97 if !nerr.Merge(err) {
Damien Neil3016b732019-04-07 12:43:10 -070098 return out, err
Damien Neil0d3e8cc2019-04-01 13:31:55 -070099 }
Damien Neilc37adef2019-04-01 13:49:56 -0700100 if !allowPartial {
Damien Neil4686e232019-04-05 13:31:40 -0700101 nerr.Merge(IsInitialized(m))
102 }
Damien Neil3016b732019-04-07 12:43:10 -0700103 return out, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700104}
105
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700106func (o MarshalOptions) marshalMessageFast(b []byte, m Message) ([]byte, error) {
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700107 methods := protoMethods(m)
108 if methods == nil ||
109 methods.MarshalAppend == nil ||
110 (o.Deterministic && methods.Flags&protoiface.MethodFlagDeterministicMarshal == 0) {
111 return nil, errInternalNoFast
112 }
113 if methods.Size != nil {
114 sz := methods.Size(m)
115 if cap(b) < len(b)+sz {
116 x := make([]byte, len(b), len(b)+sz)
117 copy(x, b)
118 b = x
119 }
Damien Neil03e74862019-04-07 18:18:31 -0700120 o.UseCachedSize = true
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700121 }
122 return methods.MarshalAppend(b, m, protoiface.MarshalOptions(o))
123}
124
Damien Neil99f24c32019-03-13 17:06:42 -0700125func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
126 // There are many choices for what order we visit fields in. The default one here
127 // is chosen for reasonable efficiency and simplicity given the protoreflect API.
128 // It is not deterministic, since KnownFields.Range does not return fields in any
129 // defined order.
130 //
131 // When using deterministic serialization, we sort the known fields by field number.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700132 fieldDescs := m.Descriptor().Fields()
Damien Neil99f24c32019-03-13 17:06:42 -0700133 knownFields := m.KnownFields()
134 var err error
Damien Neil96c229a2019-04-03 12:17:24 -0700135 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700136 o.rangeKnown(knownFields, func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700137 field := fieldDescs.ByNumber(num)
Damien Neil99f24c32019-03-13 17:06:42 -0700138 if field == nil {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700139 field = knownFields.ExtensionTypes().ByNumber(num).Descriptor()
Damien Neil99f24c32019-03-13 17:06:42 -0700140 if field == nil {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700141 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Descriptor().FullName()))
Damien Neil99f24c32019-03-13 17:06:42 -0700142 }
143 }
144 b, err = o.marshalField(b, field, value)
Damien Neil96c229a2019-04-03 12:17:24 -0700145 if nerr.Merge(err) {
146 err = nil
147 return true
148 }
149 return false
Damien Neil99f24c32019-03-13 17:06:42 -0700150 })
151 if err != nil {
Damien Neil96c229a2019-04-03 12:17:24 -0700152 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700153 }
154 m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
155 b = append(b, raw...)
156 return true
157 })
Damien Neil96c229a2019-04-03 12:17:24 -0700158 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700159}
160
161// rangeKnown visits known fields in field number order when deterministic
162// serialization is enabled.
163func (o MarshalOptions) rangeKnown(knownFields protoreflect.KnownFields, f func(protoreflect.FieldNumber, protoreflect.Value) bool) {
164 if !o.Deterministic {
165 knownFields.Range(f)
166 return
167 }
168 nums := make([]protoreflect.FieldNumber, 0, knownFields.Len())
169 knownFields.Range(func(num protoreflect.FieldNumber, _ protoreflect.Value) bool {
170 nums = append(nums, num)
171 return true
172 })
173 sort.Slice(nums, func(a, b int) bool {
174 return nums[a] < nums[b]
175 })
176 for _, num := range nums {
177 if !f(num, knownFields.Get(num)) {
178 break
179 }
180 }
181}
182
Joe Tsaiac31a352019-05-13 14:32:56 -0700183func (o MarshalOptions) marshalField(b []byte, fd protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) {
184 num := fd.Number()
185 kind := fd.Kind()
Damien Neil99f24c32019-03-13 17:06:42 -0700186 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700187 case fd.IsList():
188 return o.marshalList(b, num, fd, value.List())
189 case fd.IsMap():
190 return o.marshalMap(b, num, fd, value.Map())
Damien Neil99f24c32019-03-13 17:06:42 -0700191 default:
Joe Tsaiac31a352019-05-13 14:32:56 -0700192 b = wire.AppendTag(b, num, wireTypes[kind])
193 return o.marshalSingular(b, num, fd, value)
Damien Neil99f24c32019-03-13 17:06:42 -0700194 }
195}
196
Joe Tsaiac31a352019-05-13 14:32:56 -0700197func (o MarshalOptions) marshalList(b []byte, num wire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) ([]byte, error) {
198 if fd.IsPacked() {
199 b = wire.AppendTag(b, num, wire.BytesType)
200 b, pos := appendSpeculativeLength(b)
201 var nerr errors.NonFatal
202 for i, llen := 0, list.Len(); i < llen; i++ {
203 var err error
204 b, err = o.marshalSingular(b, num, fd, list.Get(i))
205 if !nerr.Merge(err) {
206 return b, err
207 }
208 }
209 b = finishSpeculativeLength(b, pos)
210 return b, nerr.E
211 }
212
213 kind := fd.Kind()
214 var nerr errors.NonFatal
215 for i, llen := 0, list.Len(); i < llen; i++ {
216 var err error
217 b = wire.AppendTag(b, num, wireTypes[kind])
218 b, err = o.marshalSingular(b, num, fd, list.Get(i))
219 if !nerr.Merge(err) {
220 return b, err
221 }
222 }
223 return b, nerr.E
224}
225
226func (o MarshalOptions) marshalMap(b []byte, num wire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) ([]byte, error) {
227 keyf := fd.MapKey()
228 valf := fd.MapValue()
Damien Neil96c229a2019-04-03 12:17:24 -0700229 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700230 var err error
231 o.rangeMap(mapv, keyf.Kind(), func(key protoreflect.MapKey, value protoreflect.Value) bool {
232 b = wire.AppendTag(b, num, wire.BytesType)
233 var pos int
234 b, pos = appendSpeculativeLength(b)
235
236 b, err = o.marshalField(b, keyf, key.Value())
Damien Neil96c229a2019-04-03 12:17:24 -0700237 if !nerr.Merge(err) {
Damien Neil99f24c32019-03-13 17:06:42 -0700238 return false
239 }
240 b, err = o.marshalField(b, valf, value)
Damien Neil96c229a2019-04-03 12:17:24 -0700241 if !nerr.Merge(err) {
Damien Neil99f24c32019-03-13 17:06:42 -0700242 return false
243 }
Damien Neil96c229a2019-04-03 12:17:24 -0700244 err = nil
Damien Neil99f24c32019-03-13 17:06:42 -0700245
246 b = finishSpeculativeLength(b, pos)
247 return true
248 })
249 if err != nil {
Damien Neil96c229a2019-04-03 12:17:24 -0700250 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700251 }
Damien Neil96c229a2019-04-03 12:17:24 -0700252 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700253}
254
255func (o MarshalOptions) rangeMap(mapv protoreflect.Map, kind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
256 if !o.Deterministic {
257 mapv.Range(f)
258 return
259 }
260 mapsort.Range(mapv, kind, f)
261}
262
Damien Neil99f24c32019-03-13 17:06:42 -0700263// When encoding length-prefixed fields, we speculatively set aside some number of bytes
264// for the length, encode the data, and then encode the length (shifting the data if necessary
265// to make room).
266const speculativeLength = 1
267
268func appendSpeculativeLength(b []byte) ([]byte, int) {
269 pos := len(b)
270 b = append(b, "\x00\x00\x00\x00"[:speculativeLength]...)
271 return b, pos
272}
273
274func finishSpeculativeLength(b []byte, pos int) []byte {
275 mlen := len(b) - pos - speculativeLength
276 msiz := wire.SizeVarint(uint64(mlen))
277 if msiz != speculativeLength {
278 for i := 0; i < msiz-speculativeLength; i++ {
279 b = append(b, 0)
280 }
281 copy(b[pos+msiz:], b[pos+speculativeLength:])
282 b = b[:pos+msiz+mlen]
283 }
284 wire.AppendVarint(b[:pos], uint64(mlen))
285 return b
286}