blob: 68a344878646240c9dce4104cc9f10d94453b679 [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"
12 "github.com/golang/protobuf/v2/internal/mapsort"
13 "github.com/golang/protobuf/v2/internal/pragma"
14 "github.com/golang/protobuf/v2/reflect/protoreflect"
15)
16
17// MarshalOptions configures the marshaler.
18//
19// Example usage:
20// b, err := MarshalOptions{Deterministic: true}.Marshal(m)
21type MarshalOptions struct {
22 // Deterministic controls whether the same message will always be
23 // serialized to the same bytes within the same binary.
24 //
25 // Setting this option guarantees that repeated serialization of
26 // the same message will return the same bytes, and that different
27 // processes of the same binary (which may be executing on different
28 // machines) will serialize equal messages to the same bytes.
29 //
30 // Note that the deterministic serialization is NOT canonical across
31 // languages. It is not guaranteed to remain stable over time. It is
32 // unstable across different builds with schema changes due to unknown
33 // fields. Users who need canonical serialization (e.g., persistent
34 // storage in a canonical form, fingerprinting, etc.) must define
35 // their own canonicalization specification and implement their own
36 // serializer rather than relying on this API.
37 //
38 // If deterministic serialization is requested, map entries will be
39 // sorted by keys in lexographical order. This is an implementation
40 // detail and subject to change.
41 Deterministic bool
42
43 pragma.NoUnkeyedLiterals
44}
45
46// Marshal returns the wire-format encoding of m.
47func Marshal(m Message) ([]byte, error) {
48 return MarshalOptions{}.MarshalAppend(nil, m)
49}
50
51// Marshal returns the wire-format encoding of m.
52func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
53 return o.marshalMessage(nil, m.ProtoReflect())
54}
55
56// MarshalAppend appends the wire-format encoding of m to b,
57// returning the result.
58func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
59 return o.marshalMessage(b, m.ProtoReflect())
60}
61
62func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
63 // There are many choices for what order we visit fields in. The default one here
64 // is chosen for reasonable efficiency and simplicity given the protoreflect API.
65 // It is not deterministic, since KnownFields.Range does not return fields in any
66 // defined order.
67 //
68 // When using deterministic serialization, we sort the known fields by field number.
69 fields := m.Type().Fields()
70 knownFields := m.KnownFields()
71 var err error
72 o.rangeKnown(knownFields, func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
73 field := fields.ByNumber(num)
74 if field == nil {
75 field = knownFields.ExtensionTypes().ByNumber(num)
76 if field == nil {
77 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Type().FullName()))
78 }
79 }
80 b, err = o.marshalField(b, field, value)
81 return err == nil
82 })
83 if err != nil {
84 return nil, err
85 }
86 m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
87 b = append(b, raw...)
88 return true
89 })
90 // TODO: required field checks
91 return b, nil
92}
93
94// rangeKnown visits known fields in field number order when deterministic
95// serialization is enabled.
96func (o MarshalOptions) rangeKnown(knownFields protoreflect.KnownFields, f func(protoreflect.FieldNumber, protoreflect.Value) bool) {
97 if !o.Deterministic {
98 knownFields.Range(f)
99 return
100 }
101 nums := make([]protoreflect.FieldNumber, 0, knownFields.Len())
102 knownFields.Range(func(num protoreflect.FieldNumber, _ protoreflect.Value) bool {
103 nums = append(nums, num)
104 return true
105 })
106 sort.Slice(nums, func(a, b int) bool {
107 return nums[a] < nums[b]
108 })
109 for _, num := range nums {
110 if !f(num, knownFields.Get(num)) {
111 break
112 }
113 }
114}
115
116func (o MarshalOptions) marshalField(b []byte, field protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) {
117 num := field.Number()
118 kind := field.Kind()
119 switch {
120 case field.Cardinality() != protoreflect.Repeated:
121 b = wire.AppendTag(b, num, wireTypes[kind])
122 return o.marshalSingular(b, num, kind, value)
123 case field.IsMap():
124 return o.marshalMap(b, num, kind, field.MessageType(), value.Map())
125 case field.IsPacked():
126 return o.marshalPacked(b, num, kind, value.List())
127 default:
128 return o.marshalList(b, num, kind, value.List())
129 }
130}
131
132func (o MarshalOptions) marshalMap(b []byte, num wire.Number, kind protoreflect.Kind, mdesc protoreflect.MessageDescriptor, mapv protoreflect.Map) ([]byte, error) {
133 keyf := mdesc.Fields().ByNumber(1)
134 valf := mdesc.Fields().ByNumber(2)
135 var err error
136 o.rangeMap(mapv, keyf.Kind(), func(key protoreflect.MapKey, value protoreflect.Value) bool {
137 b = wire.AppendTag(b, num, wire.BytesType)
138 var pos int
139 b, pos = appendSpeculativeLength(b)
140
141 b, err = o.marshalField(b, keyf, key.Value())
142 if err != nil {
143 return false
144 }
145 b, err = o.marshalField(b, valf, value)
146 if err != nil {
147 return false
148 }
149
150 b = finishSpeculativeLength(b, pos)
151 return true
152 })
153 if err != nil {
154 return nil, err
155 }
156 return b, nil
157}
158
159func (o MarshalOptions) rangeMap(mapv protoreflect.Map, kind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
160 if !o.Deterministic {
161 mapv.Range(f)
162 return
163 }
164 mapsort.Range(mapv, kind, f)
165}
166
167func (o MarshalOptions) marshalPacked(b []byte, num wire.Number, kind protoreflect.Kind, list protoreflect.List) ([]byte, error) {
168 b = wire.AppendTag(b, num, wire.BytesType)
169 b, pos := appendSpeculativeLength(b)
170 for i, llen := 0, list.Len(); i < llen; i++ {
171 var err error
172 b, err = o.marshalSingular(b, num, kind, list.Get(i))
173 if err != nil {
174 return nil, err
175 }
176 }
177 b = finishSpeculativeLength(b, pos)
178 return b, nil
179}
180
181func (o MarshalOptions) marshalList(b []byte, num wire.Number, kind protoreflect.Kind, list protoreflect.List) ([]byte, error) {
182 for i, llen := 0, list.Len(); i < llen; i++ {
183 var err error
184 b = wire.AppendTag(b, num, wireTypes[kind])
185 b, err = o.marshalSingular(b, num, kind, list.Get(i))
186 if err != nil {
187 return nil, err
188 }
189 }
190 return b, nil
191}
192
193// When encoding length-prefixed fields, we speculatively set aside some number of bytes
194// for the length, encode the data, and then encode the length (shifting the data if necessary
195// to make room).
196const speculativeLength = 1
197
198func appendSpeculativeLength(b []byte) ([]byte, int) {
199 pos := len(b)
200 b = append(b, "\x00\x00\x00\x00"[:speculativeLength]...)
201 return b, pos
202}
203
204func finishSpeculativeLength(b []byte, pos int) []byte {
205 mlen := len(b) - pos - speculativeLength
206 msiz := wire.SizeVarint(uint64(mlen))
207 if msiz != speculativeLength {
208 for i := 0; i < msiz-speculativeLength; i++ {
209 b = append(b, 0)
210 }
211 copy(b[pos+msiz:], b[pos+speculativeLength:])
212 b = b[:pos+msiz+mlen]
213 }
214 wire.AppendVarint(b[:pos], uint64(mlen))
215 return b
216}