blob: 1b48b41ec8e9a8c03b4dee56f70ca978ebae1311 [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
11 "github.com/golang/protobuf/v2/internal/encoding/wire"
Damien Neil96c229a2019-04-03 12:17:24 -070012 "github.com/golang/protobuf/v2/internal/errors"
Damien Neil99f24c32019-03-13 17:06:42 -070013 "github.com/golang/protobuf/v2/internal/mapsort"
14 "github.com/golang/protobuf/v2/internal/pragma"
15 "github.com/golang/protobuf/v2/reflect/protoreflect"
Damien Neil0d3e8cc2019-04-01 13:31:55 -070016 "github.com/golang/protobuf/v2/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 Neil3016b732019-04-07 12:43:10 -070088 out, err := o.marshalMessageFast(b, m)
Damien Neil4686e232019-04-05 13:31:40 -070089 if err == errInternalNoFast {
Damien Neil3016b732019-04-07 12:43:10 -070090 out, err = o.marshalMessage(b, m.ProtoReflect())
Damien Neil4686e232019-04-05 13:31:40 -070091 }
92 var nerr errors.NonFatal
93 if !nerr.Merge(err) {
Damien Neil3016b732019-04-07 12:43:10 -070094 return out, err
Damien Neil0d3e8cc2019-04-01 13:31:55 -070095 }
Damien Neil4686e232019-04-05 13:31:40 -070096 if !o.AllowPartial {
97 nerr.Merge(IsInitialized(m))
98 }
Damien Neil3016b732019-04-07 12:43:10 -070099 return out, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700100}
101
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700102func (o MarshalOptions) marshalMessageFast(b []byte, m Message) ([]byte, error) {
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700103 methods := protoMethods(m)
104 if methods == nil ||
105 methods.MarshalAppend == nil ||
106 (o.Deterministic && methods.Flags&protoiface.MethodFlagDeterministicMarshal == 0) {
107 return nil, errInternalNoFast
108 }
109 if methods.Size != nil {
110 sz := methods.Size(m)
111 if cap(b) < len(b)+sz {
112 x := make([]byte, len(b), len(b)+sz)
113 copy(x, b)
114 b = x
115 }
Damien Neil03e74862019-04-07 18:18:31 -0700116 o.UseCachedSize = true
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700117 }
118 return methods.MarshalAppend(b, m, protoiface.MarshalOptions(o))
119}
120
Damien Neil99f24c32019-03-13 17:06:42 -0700121func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
122 // There are many choices for what order we visit fields in. The default one here
123 // is chosen for reasonable efficiency and simplicity given the protoreflect API.
124 // It is not deterministic, since KnownFields.Range does not return fields in any
125 // defined order.
126 //
127 // When using deterministic serialization, we sort the known fields by field number.
128 fields := m.Type().Fields()
129 knownFields := m.KnownFields()
130 var err error
Damien Neil96c229a2019-04-03 12:17:24 -0700131 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700132 o.rangeKnown(knownFields, func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
133 field := fields.ByNumber(num)
134 if field == nil {
135 field = knownFields.ExtensionTypes().ByNumber(num)
136 if field == nil {
137 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Type().FullName()))
138 }
139 }
140 b, err = o.marshalField(b, field, value)
Damien Neil96c229a2019-04-03 12:17:24 -0700141 if nerr.Merge(err) {
142 err = nil
143 return true
144 }
145 return false
Damien Neil99f24c32019-03-13 17:06:42 -0700146 })
147 if err != nil {
Damien Neil96c229a2019-04-03 12:17:24 -0700148 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700149 }
150 m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
151 b = append(b, raw...)
152 return true
153 })
Damien Neil96c229a2019-04-03 12:17:24 -0700154 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700155}
156
157// rangeKnown visits known fields in field number order when deterministic
158// serialization is enabled.
159func (o MarshalOptions) rangeKnown(knownFields protoreflect.KnownFields, f func(protoreflect.FieldNumber, protoreflect.Value) bool) {
160 if !o.Deterministic {
161 knownFields.Range(f)
162 return
163 }
164 nums := make([]protoreflect.FieldNumber, 0, knownFields.Len())
165 knownFields.Range(func(num protoreflect.FieldNumber, _ protoreflect.Value) bool {
166 nums = append(nums, num)
167 return true
168 })
169 sort.Slice(nums, func(a, b int) bool {
170 return nums[a] < nums[b]
171 })
172 for _, num := range nums {
173 if !f(num, knownFields.Get(num)) {
174 break
175 }
176 }
177}
178
179func (o MarshalOptions) marshalField(b []byte, field protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) {
180 num := field.Number()
181 kind := field.Kind()
182 switch {
183 case field.Cardinality() != protoreflect.Repeated:
184 b = wire.AppendTag(b, num, wireTypes[kind])
Damien Neilbc310b52019-04-11 11:46:55 -0700185 return o.marshalSingular(b, num, field, value)
Damien Neil99f24c32019-03-13 17:06:42 -0700186 case field.IsMap():
Joe Tsaid24bc722019-04-15 23:39:09 -0700187 return o.marshalMap(b, num, kind, field.Message(), value.Map())
Damien Neil99f24c32019-03-13 17:06:42 -0700188 case field.IsPacked():
Damien Neilbc310b52019-04-11 11:46:55 -0700189 return o.marshalPacked(b, num, field, value.List())
Damien Neil99f24c32019-03-13 17:06:42 -0700190 default:
Damien Neilbc310b52019-04-11 11:46:55 -0700191 return o.marshalList(b, num, field, value.List())
Damien Neil99f24c32019-03-13 17:06:42 -0700192 }
193}
194
195func (o MarshalOptions) marshalMap(b []byte, num wire.Number, kind protoreflect.Kind, mdesc protoreflect.MessageDescriptor, mapv protoreflect.Map) ([]byte, error) {
196 keyf := mdesc.Fields().ByNumber(1)
197 valf := mdesc.Fields().ByNumber(2)
Damien Neil96c229a2019-04-03 12:17:24 -0700198 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700199 var err error
200 o.rangeMap(mapv, keyf.Kind(), func(key protoreflect.MapKey, value protoreflect.Value) bool {
201 b = wire.AppendTag(b, num, wire.BytesType)
202 var pos int
203 b, pos = appendSpeculativeLength(b)
204
205 b, err = o.marshalField(b, keyf, key.Value())
Damien Neil96c229a2019-04-03 12:17:24 -0700206 if !nerr.Merge(err) {
Damien Neil99f24c32019-03-13 17:06:42 -0700207 return false
208 }
209 b, err = o.marshalField(b, valf, value)
Damien Neil96c229a2019-04-03 12:17:24 -0700210 if !nerr.Merge(err) {
Damien Neil99f24c32019-03-13 17:06:42 -0700211 return false
212 }
Damien Neil96c229a2019-04-03 12:17:24 -0700213 err = nil
Damien Neil99f24c32019-03-13 17:06:42 -0700214
215 b = finishSpeculativeLength(b, pos)
216 return true
217 })
218 if err != nil {
Damien Neil96c229a2019-04-03 12:17:24 -0700219 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700220 }
Damien Neil96c229a2019-04-03 12:17:24 -0700221 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700222}
223
224func (o MarshalOptions) rangeMap(mapv protoreflect.Map, kind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
225 if !o.Deterministic {
226 mapv.Range(f)
227 return
228 }
229 mapsort.Range(mapv, kind, f)
230}
231
Damien Neilbc310b52019-04-11 11:46:55 -0700232func (o MarshalOptions) marshalPacked(b []byte, num wire.Number, field protoreflect.FieldDescriptor, list protoreflect.List) ([]byte, error) {
Damien Neil99f24c32019-03-13 17:06:42 -0700233 b = wire.AppendTag(b, num, wire.BytesType)
234 b, pos := appendSpeculativeLength(b)
Damien Neil96c229a2019-04-03 12:17:24 -0700235 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700236 for i, llen := 0, list.Len(); i < llen; i++ {
237 var err error
Damien Neilbc310b52019-04-11 11:46:55 -0700238 b, err = o.marshalSingular(b, num, field, list.Get(i))
Damien Neil96c229a2019-04-03 12:17:24 -0700239 if !nerr.Merge(err) {
240 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700241 }
242 }
243 b = finishSpeculativeLength(b, pos)
Damien Neil96c229a2019-04-03 12:17:24 -0700244 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700245}
246
Damien Neilbc310b52019-04-11 11:46:55 -0700247func (o MarshalOptions) marshalList(b []byte, num wire.Number, field protoreflect.FieldDescriptor, list protoreflect.List) ([]byte, error) {
248 kind := field.Kind()
Damien Neil96c229a2019-04-03 12:17:24 -0700249 var nerr errors.NonFatal
Damien Neil99f24c32019-03-13 17:06:42 -0700250 for i, llen := 0, list.Len(); i < llen; i++ {
251 var err error
252 b = wire.AppendTag(b, num, wireTypes[kind])
Damien Neilbc310b52019-04-11 11:46:55 -0700253 b, err = o.marshalSingular(b, num, field, list.Get(i))
Damien Neil96c229a2019-04-03 12:17:24 -0700254 if !nerr.Merge(err) {
255 return b, err
Damien Neil99f24c32019-03-13 17:06:42 -0700256 }
257 }
Damien Neil96c229a2019-04-03 12:17:24 -0700258 return b, nerr.E
Damien Neil99f24c32019-03-13 17:06:42 -0700259}
260
261// When encoding length-prefixed fields, we speculatively set aside some number of bytes
262// for the length, encode the data, and then encode the length (shifting the data if necessary
263// to make room).
264const speculativeLength = 1
265
266func appendSpeculativeLength(b []byte) ([]byte, int) {
267 pos := len(b)
268 b = append(b, "\x00\x00\x00\x00"[:speculativeLength]...)
269 return b, pos
270}
271
272func finishSpeculativeLength(b []byte, pos int) []byte {
273 mlen := len(b) - pos - speculativeLength
274 msiz := wire.SizeVarint(uint64(mlen))
275 if msiz != speculativeLength {
276 for i := 0; i < msiz-speculativeLength; i++ {
277 b = append(b, 0)
278 }
279 copy(b[pos+msiz:], b[pos+speculativeLength:])
280 b = b[:pos+msiz+mlen]
281 }
282 wire.AppendVarint(b[:pos], uint64(mlen))
283 return b
284}