• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5package prototext
6
7import (
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
27const 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.
33func 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.
40func Marshal(m proto.Message) ([]byte, error) {
41	return MarshalOptions{}.Marshal(m)
42}
43
44// MarshalOptions is a configurable text format marshaler.
45type 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.
90func (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.
104func (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.
111func (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
147type encoder struct {
148	*text.Encoder
149	opts MarshalOptions
150}
151
152// marshalMessage marshals the given protoreflect.Message.
153func (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.
193func (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.
207func (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.
259func (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.
271func (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.
296func (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.
337func (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}
372