xref: /aosp_15_r20/external/golang-protobuf/encoding/prototext/encode.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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	"google.golang.org/protobuf/reflect/protoregistry"
24)
25
26const 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.
32func 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.
39func Marshal(m proto.Message) ([]byte, error) {
40	return MarshalOptions{}.Marshal(m)
41}
42
43// MarshalOptions is a configurable text format marshaler.
44type 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.
89func (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.
103func (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.
110func (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
146type encoder struct {
147	*text.Encoder
148	opts MarshalOptions
149}
150
151// marshalMessage marshals the given protoreflect.Message.
152func (e encoder) marshalMessage(m protoreflect.Message, inclDelims bool) error {
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.
192func (e encoder) marshalField(name string, val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
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.
206func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
207	kind := fd.Kind()
208	switch kind {
209	case protoreflect.BoolKind:
210		e.WriteBool(val.Bool())
211
212	case protoreflect.StringKind:
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
219	case protoreflect.Int32Kind, protoreflect.Int64Kind,
220		protoreflect.Sint32Kind, protoreflect.Sint64Kind,
221		protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind:
222		e.WriteInt(val.Int())
223
224	case protoreflect.Uint32Kind, protoreflect.Uint64Kind,
225		protoreflect.Fixed32Kind, protoreflect.Fixed64Kind:
226		e.WriteUint(val.Uint())
227
228	case protoreflect.FloatKind:
229		// Encoder.WriteFloat handles the special numbers NaN and infinites.
230		e.WriteFloat(val.Float(), 32)
231
232	case protoreflect.DoubleKind:
233		// Encoder.WriteFloat handles the special numbers NaN and infinites.
234		e.WriteFloat(val.Float(), 64)
235
236	case protoreflect.BytesKind:
237		e.WriteString(string(val.Bytes()))
238
239	case protoreflect.EnumKind:
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
248	case protoreflect.MessageKind, protoreflect.GroupKind:
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.
258func (e encoder) marshalList(name string, list protoreflect.List, fd protoreflect.FieldDescriptor) error {
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.
270func (e encoder) marshalMap(name string, mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error {
271	var err error
272	order.RangeEntries(mmap, order.GenericKeyOrder, func(key protoreflect.MapKey, val protoreflect.Value) bool {
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.
295func (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.
336func (e encoder) marshalAny(any protoreflect.Message) bool {
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}
371