256
|
1 |
// Copyright 2018 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 |
|
|
5 |
package prototext |
|
6 |
|
|
7 |
import ( |
|
8 |
"fmt" |
|
9 |
"strconv" |
|
10 |
"unicode/utf8" |
|
11 |
|
|
12 |
"google.golang.org/protobuf/encoding/protowire" |
|
13 |
"google.golang.org/protobuf/internal/encoding/messageset" |
|
14 |
"google.golang.org/protobuf/internal/encoding/text" |
|
15 |
"google.golang.org/protobuf/internal/errors" |
|
16 |
"google.golang.org/protobuf/internal/flags" |
|
17 |
"google.golang.org/protobuf/internal/genid" |
|
18 |
"google.golang.org/protobuf/internal/order" |
|
19 |
"google.golang.org/protobuf/internal/pragma" |
|
20 |
"google.golang.org/protobuf/internal/strs" |
|
21 |
"google.golang.org/protobuf/proto" |
|
22 |
"google.golang.org/protobuf/reflect/protoreflect" |
|
23 |
"google.golang.org/protobuf/reflect/protoregistry" |
|
24 |
) |
|
25 |
|
|
26 |
const defaultIndent = " " |
|
27 |
|
|
28 |
// Format formats the message as a multiline string. |
|
29 |
// This function is only intended for human consumption and ignores errors. |
|
30 |
// Do not depend on the output being stable. It may change over time across |
|
31 |
// different versions of the program. |
|
32 |
func Format(m proto.Message) string { |
|
33 |
return MarshalOptions{Multiline: true}.Format(m) |
|
34 |
} |
|
35 |
|
|
36 |
// Marshal writes the given proto.Message in textproto format using default |
|
37 |
// options. Do not depend on the output being stable. It may change over time |
|
38 |
// across different versions of the program. |
|
39 |
func Marshal(m proto.Message) ([]byte, error) { |
|
40 |
return MarshalOptions{}.Marshal(m) |
|
41 |
} |
|
42 |
|
|
43 |
// MarshalOptions is a configurable text format marshaler. |
|
44 |
type MarshalOptions struct { |
|
45 |
pragma.NoUnkeyedLiterals |
|
46 |
|
|
47 |
// Multiline specifies whether the marshaler should format the output in |
|
48 |
// indented-form with every textual element on a new line. |
|
49 |
// If Indent is an empty string, then an arbitrary indent is chosen. |
|
50 |
Multiline bool |
|
51 |
|
|
52 |
// Indent specifies the set of indentation characters to use in a multiline |
|
53 |
// formatted output such that every entry is preceded by Indent and |
|
54 |
// terminated by a newline. If non-empty, then Multiline is treated as true. |
|
55 |
// Indent can only be composed of space or tab characters. |
|
56 |
Indent string |
|
57 |
|
|
58 |
// EmitASCII specifies whether to format strings and bytes as ASCII only |
|
59 |
// as opposed to using UTF-8 encoding when possible. |
|
60 |
EmitASCII bool |
|
61 |
|
|
62 |
// allowInvalidUTF8 specifies whether to permit the encoding of strings |
|
63 |
// with invalid UTF-8. This is unexported as it is intended to only |
|
64 |
// be specified by the Format method. |
|
65 |
allowInvalidUTF8 bool |
|
66 |
|
|
67 |
// AllowPartial allows messages that have missing required fields to marshal |
|
68 |
// without returning an error. If AllowPartial is false (the default), |
|
69 |
// Marshal will return error if there are any missing required fields. |
|
70 |
AllowPartial bool |
|
71 |
|
|
72 |
// EmitUnknown specifies whether to emit unknown fields in the output. |
|
73 |
// If specified, the unmarshaler may be unable to parse the output. |
|
74 |
// The default is to exclude unknown fields. |
|
75 |
EmitUnknown bool |
|
76 |
|
|
77 |
// Resolver is used for looking up types when expanding google.protobuf.Any |
|
78 |
// messages. If nil, this defaults to using protoregistry.GlobalTypes. |
|
79 |
Resolver interface { |
|
80 |
protoregistry.ExtensionTypeResolver |
|
81 |
protoregistry.MessageTypeResolver |
|
82 |
} |
|
83 |
} |
|
84 |
|
|
85 |
// Format formats the message as a string. |
|
86 |
// This method is only intended for human consumption and ignores errors. |
|
87 |
// Do not depend on the output being stable. It may change over time across |
|
88 |
// different versions of the program. |
|
89 |
func (o MarshalOptions) Format(m proto.Message) string { |
|
90 |
if m == nil || !m.ProtoReflect().IsValid() { |
|
91 |
return "<nil>" // invalid syntax, but okay since this is for debugging |
|
92 |
} |
|
93 |
o.allowInvalidUTF8 = true |
|
94 |
o.AllowPartial = true |
|
95 |
o.EmitUnknown = true |
|
96 |
b, _ := o.Marshal(m) |
|
97 |
return string(b) |
|
98 |
} |
|
99 |
|
|
100 |
// Marshal writes the given proto.Message in textproto format using options in |
|
101 |
// MarshalOptions object. Do not depend on the output being stable. It may |
|
102 |
// change over time across different versions of the program. |
|
103 |
func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { |
|
104 |
return o.marshal(m) |
|
105 |
} |
|
106 |
|
|
107 |
// marshal is a centralized function that all marshal operations go through. |
|
108 |
// For profiling purposes, avoid changing the name of this function or |
|
109 |
// introducing other code paths for marshal that do not go through this. |
|
110 |
func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { |
|
111 |
var delims = [2]byte{'{', '}'} |
|
112 |
|
|
113 |
if o.Multiline && o.Indent == "" { |
|
114 |
o.Indent = defaultIndent |
|
115 |
} |
|
116 |
if o.Resolver == nil { |
|
117 |
o.Resolver = protoregistry.GlobalTypes |
|
118 |
} |
|
119 |
|
|
120 |
internalEnc, err := text.NewEncoder(o.Indent, delims, o.EmitASCII) |
|
121 |
if err != nil { |
|
122 |
return nil, err |
|
123 |
} |
|
124 |
|
|
125 |
// Treat nil message interface as an empty message, |
|
126 |
// in which case there is nothing to output. |
|
127 |
if m == nil { |
|
128 |
return []byte{}, nil |
|
129 |
} |
|
130 |
|
|
131 |
enc := encoder{internalEnc, o} |
|
132 |
err = enc.marshalMessage(m.ProtoReflect(), false) |
|
133 |
if err != nil { |
|
134 |
return nil, err |
|
135 |
} |
|
136 |
out := enc.Bytes() |
|
137 |
if len(o.Indent) > 0 && len(out) > 0 { |
|
138 |
out = append(out, '\n') |
|
139 |
} |
|
140 |
if o.AllowPartial { |
|
141 |
return out, nil |
|
142 |
} |
|
143 |
return out, proto.CheckInitialized(m) |
|
144 |
} |
|
145 |
|
|
146 |
type encoder struct { |
|
147 |
*text.Encoder |
|
148 |
opts MarshalOptions |
|
149 |
} |
|
150 |
|
|
151 |
// marshalMessage marshals the given protoreflect.Message. |
260
|
152 |
func (e encoder) marshalMessage(m protoreflect.Message, inclDelims bool) error { |
256
|
153 |
messageDesc := m.Descriptor() |
|
154 |
if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) { |
|
155 |
return errors.New("no support for proto1 MessageSets") |
|
156 |
} |
|
157 |
|
|
158 |
if inclDelims { |
|
159 |
e.StartMessage() |
|
160 |
defer e.EndMessage() |
|
161 |
} |
|
162 |
|
|
163 |
// Handle Any expansion. |
|
164 |
if messageDesc.FullName() == genid.Any_message_fullname { |
|
165 |
if e.marshalAny(m) { |
|
166 |
return nil |
|
167 |
} |
|
168 |
// If unable to expand, continue on to marshal Any as a regular message. |
|
169 |
} |
|
170 |
|
|
171 |
// Marshal fields. |
|
172 |
var err error |
|
173 |
order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
|
174 |
if err = e.marshalField(fd.TextName(), v, fd); err != nil { |
|
175 |
return false |
|
176 |
} |
|
177 |
return true |
|
178 |
}) |
|
179 |
if err != nil { |
|
180 |
return err |
|
181 |
} |
|
182 |
|
|
183 |
// Marshal unknown fields. |
|
184 |
if e.opts.EmitUnknown { |
|
185 |
e.marshalUnknown(m.GetUnknown()) |
|
186 |
} |
|
187 |
|
|
188 |
return nil |
|
189 |
} |
|
190 |
|
|
191 |
// marshalField marshals the given field with protoreflect.Value. |
260
|
192 |
func (e encoder) marshalField(name string, val protoreflect.Value, fd protoreflect.FieldDescriptor) error { |
256
|
193 |
switch { |
|
194 |
case fd.IsList(): |
|
195 |
return e.marshalList(name, val.List(), fd) |
|
196 |
case fd.IsMap(): |
|
197 |
return e.marshalMap(name, val.Map(), fd) |
|
198 |
default: |
|
199 |
e.WriteName(name) |
|
200 |
return e.marshalSingular(val, fd) |
|
201 |
} |
|
202 |
} |
|
203 |
|
|
204 |
// marshalSingular marshals the given non-repeated field value. This includes |
|
205 |
// all scalar types, enums, messages, and groups. |
260
|
206 |
func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error { |
256
|
207 |
kind := fd.Kind() |
|
208 |
switch kind { |
260
|
209 |
case protoreflect.BoolKind: |
256
|
210 |
e.WriteBool(val.Bool()) |
|
211 |
|
260
|
212 |
case protoreflect.StringKind: |
256
|
213 |
s := val.String() |
|
214 |
if !e.opts.allowInvalidUTF8 && strs.EnforceUTF8(fd) && !utf8.ValidString(s) { |
|
215 |
return errors.InvalidUTF8(string(fd.FullName())) |
|
216 |
} |
|
217 |
e.WriteString(s) |
|
218 |
|
260
|
219 |
case protoreflect.Int32Kind, protoreflect.Int64Kind, |
|
220 |
protoreflect.Sint32Kind, protoreflect.Sint64Kind, |
|
221 |
protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind: |
256
|
222 |
e.WriteInt(val.Int()) |
|
223 |
|
260
|
224 |
case protoreflect.Uint32Kind, protoreflect.Uint64Kind, |
|
225 |
protoreflect.Fixed32Kind, protoreflect.Fixed64Kind: |
256
|
226 |
e.WriteUint(val.Uint()) |
|
227 |
|
260
|
228 |
case protoreflect.FloatKind: |
256
|
229 |
// Encoder.WriteFloat handles the special numbers NaN and infinites. |
|
230 |
e.WriteFloat(val.Float(), 32) |
|
231 |
|
260
|
232 |
case protoreflect.DoubleKind: |
256
|
233 |
// Encoder.WriteFloat handles the special numbers NaN and infinites. |
|
234 |
e.WriteFloat(val.Float(), 64) |
|
235 |
|
260
|
236 |
case protoreflect.BytesKind: |
256
|
237 |
e.WriteString(string(val.Bytes())) |
|
238 |
|
260
|
239 |
case protoreflect.EnumKind: |
256
|
240 |
num := val.Enum() |
|
241 |
if desc := fd.Enum().Values().ByNumber(num); desc != nil { |
|
242 |
e.WriteLiteral(string(desc.Name())) |
|
243 |
} else { |
|
244 |
// Use numeric value if there is no enum description. |
|
245 |
e.WriteInt(int64(num)) |
|
246 |
} |
|
247 |
|
260
|
248 |
case protoreflect.MessageKind, protoreflect.GroupKind: |
256
|
249 |
return e.marshalMessage(val.Message(), true) |
|
250 |
|
|
251 |
default: |
|
252 |
panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind)) |
|
253 |
} |
|
254 |
return nil |
|
255 |
} |
|
256 |
|
|
257 |
// marshalList marshals the given protoreflect.List as multiple name-value fields. |
260
|
258 |
func (e encoder) marshalList(name string, list protoreflect.List, fd protoreflect.FieldDescriptor) error { |
256
|
259 |
size := list.Len() |
|
260 |
for i := 0; i < size; i++ { |
|
261 |
e.WriteName(name) |
|
262 |
if err := e.marshalSingular(list.Get(i), fd); err != nil { |
|
263 |
return err |
|
264 |
} |
|
265 |
} |
|
266 |
return nil |
|
267 |
} |
|
268 |
|
|
269 |
// marshalMap marshals the given protoreflect.Map as multiple name-value fields. |
260
|
270 |
func (e encoder) marshalMap(name string, mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error { |
256
|
271 |
var err error |
260
|
272 |
order.RangeEntries(mmap, order.GenericKeyOrder, func(key protoreflect.MapKey, val protoreflect.Value) bool { |
256
|
273 |
e.WriteName(name) |
|
274 |
e.StartMessage() |
|
275 |
defer e.EndMessage() |
|
276 |
|
|
277 |
e.WriteName(string(genid.MapEntry_Key_field_name)) |
|
278 |
err = e.marshalSingular(key.Value(), fd.MapKey()) |
|
279 |
if err != nil { |
|
280 |
return false |
|
281 |
} |
|
282 |
|
|
283 |
e.WriteName(string(genid.MapEntry_Value_field_name)) |
|
284 |
err = e.marshalSingular(val, fd.MapValue()) |
|
285 |
if err != nil { |
|
286 |
return false |
|
287 |
} |
|
288 |
return true |
|
289 |
}) |
|
290 |
return err |
|
291 |
} |
|
292 |
|
|
293 |
// marshalUnknown parses the given []byte and marshals fields out. |
|
294 |
// This function assumes proper encoding in the given []byte. |
|
295 |
func (e encoder) marshalUnknown(b []byte) { |
|
296 |
const dec = 10 |
|
297 |
const hex = 16 |
|
298 |
for len(b) > 0 { |
|
299 |
num, wtype, n := protowire.ConsumeTag(b) |
|
300 |
b = b[n:] |
|
301 |
e.WriteName(strconv.FormatInt(int64(num), dec)) |
|
302 |
|
|
303 |
switch wtype { |
|
304 |
case protowire.VarintType: |
|
305 |
var v uint64 |
|
306 |
v, n = protowire.ConsumeVarint(b) |
|
307 |
e.WriteUint(v) |
|
308 |
case protowire.Fixed32Type: |
|
309 |
var v uint32 |
|
310 |
v, n = protowire.ConsumeFixed32(b) |
|
311 |
e.WriteLiteral("0x" + strconv.FormatUint(uint64(v), hex)) |
|
312 |
case protowire.Fixed64Type: |
|
313 |
var v uint64 |
|
314 |
v, n = protowire.ConsumeFixed64(b) |
|
315 |
e.WriteLiteral("0x" + strconv.FormatUint(v, hex)) |
|
316 |
case protowire.BytesType: |
|
317 |
var v []byte |
|
318 |
v, n = protowire.ConsumeBytes(b) |
|
319 |
e.WriteString(string(v)) |
|
320 |
case protowire.StartGroupType: |
|
321 |
e.StartMessage() |
|
322 |
var v []byte |
|
323 |
v, n = protowire.ConsumeGroup(num, b) |
|
324 |
e.marshalUnknown(v) |
|
325 |
e.EndMessage() |
|
326 |
default: |
|
327 |
panic(fmt.Sprintf("prototext: error parsing unknown field wire type: %v", wtype)) |
|
328 |
} |
|
329 |
|
|
330 |
b = b[n:] |
|
331 |
} |
|
332 |
} |
|
333 |
|
|
334 |
// marshalAny marshals the given google.protobuf.Any message in expanded form. |
|
335 |
// It returns true if it was able to marshal, else false. |
260
|
336 |
func (e encoder) marshalAny(any protoreflect.Message) bool { |
256
|
337 |
// Construct the embedded message. |
|
338 |
fds := any.Descriptor().Fields() |
|
339 |
fdType := fds.ByNumber(genid.Any_TypeUrl_field_number) |
|
340 |
typeURL := any.Get(fdType).String() |
|
341 |
mt, err := e.opts.Resolver.FindMessageByURL(typeURL) |
|
342 |
if err != nil { |
|
343 |
return false |
|
344 |
} |
|
345 |
m := mt.New().Interface() |
|
346 |
|
|
347 |
// Unmarshal bytes into embedded message. |
|
348 |
fdValue := fds.ByNumber(genid.Any_Value_field_number) |
|
349 |
value := any.Get(fdValue) |
|
350 |
err = proto.UnmarshalOptions{ |
|
351 |
AllowPartial: true, |
|
352 |
Resolver: e.opts.Resolver, |
|
353 |
}.Unmarshal(value.Bytes(), m) |
|
354 |
if err != nil { |
|
355 |
return false |
|
356 |
} |
|
357 |
|
|
358 |
// Get current encoder position. If marshaling fails, reset encoder output |
|
359 |
// back to this position. |
|
360 |
pos := e.Snapshot() |
|
361 |
|
|
362 |
// Field name is the proto field name enclosed in []. |
|
363 |
e.WriteName("[" + typeURL + "]") |
|
364 |
err = e.marshalMessage(m.ProtoReflect(), true) |
|
365 |
if err != nil { |
|
366 |
e.Reset(pos) |
|
367 |
return false |
|
368 |
} |
|
369 |
return true |
|
370 |
} |