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