podman
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 prototext6
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 {33return 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) {40return MarshalOptions{}.Marshal(m)41}
42
43// MarshalOptions is a configurable text format marshaler.
44type MarshalOptions struct {45pragma.NoUnkeyedLiterals46
47// Multiline specifies whether the marshaler should format the output in48// indented-form with every textual element on a new line.49// If Indent is an empty string, then an arbitrary indent is chosen.50Multiline bool51
52// Indent specifies the set of indentation characters to use in a multiline53// formatted output such that every entry is preceded by Indent and54// terminated by a newline. If non-empty, then Multiline is treated as true.55// Indent can only be composed of space or tab characters.56Indent string57
58// EmitASCII specifies whether to format strings and bytes as ASCII only59// as opposed to using UTF-8 encoding when possible.60EmitASCII bool61
62// allowInvalidUTF8 specifies whether to permit the encoding of strings63// with invalid UTF-8. This is unexported as it is intended to only64// be specified by the Format method.65allowInvalidUTF8 bool66
67// AllowPartial allows messages that have missing required fields to marshal68// without returning an error. If AllowPartial is false (the default),69// Marshal will return error if there are any missing required fields.70AllowPartial bool71
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.75EmitUnknown bool76
77// Resolver is used for looking up types when expanding google.protobuf.Any78// messages. If nil, this defaults to using protoregistry.GlobalTypes.79Resolver interface {80protoregistry.ExtensionTypeResolver81protoregistry.MessageTypeResolver82}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 {90if m == nil || !m.ProtoReflect().IsValid() {91return "<nil>" // invalid syntax, but okay since this is for debugging92}93o.allowInvalidUTF8 = true94o.AllowPartial = true95o.EmitUnknown = true96b, _ := o.Marshal(m)97return 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) {104return o.marshal(nil, m)105}
106
107// MarshalAppend appends the textproto format encoding of m to b,
108// returning the result.
109func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) {110return o.marshal(b, m)111}
112
113// marshal is a centralized function that all marshal operations go through.
114// For profiling purposes, avoid changing the name of this function or
115// introducing other code paths for marshal that do not go through this.
116func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) {117var delims = [2]byte{'{', '}'}118
119if o.Multiline && o.Indent == "" {120o.Indent = defaultIndent121}122if o.Resolver == nil {123o.Resolver = protoregistry.GlobalTypes124}125
126internalEnc, err := text.NewEncoder(b, o.Indent, delims, o.EmitASCII)127if err != nil {128return nil, err129}130
131// Treat nil message interface as an empty message,132// in which case there is nothing to output.133if m == nil {134return b, nil135}136
137enc := encoder{internalEnc, o}138err = enc.marshalMessage(m.ProtoReflect(), false)139if err != nil {140return nil, err141}142out := enc.Bytes()143if len(o.Indent) > 0 && len(out) > 0 {144out = append(out, '\n')145}146if o.AllowPartial {147return out, nil148}149return out, proto.CheckInitialized(m)150}
151
152type encoder struct {153*text.Encoder154opts MarshalOptions
155}
156
157// marshalMessage marshals the given protoreflect.Message.
158func (e encoder) marshalMessage(m protoreflect.Message, inclDelims bool) error {159messageDesc := m.Descriptor()160if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {161return errors.New("no support for proto1 MessageSets")162}163
164if inclDelims {165e.StartMessage()166defer e.EndMessage()167}168
169// Handle Any expansion.170if messageDesc.FullName() == genid.Any_message_fullname {171if e.marshalAny(m) {172return nil173}174// If unable to expand, continue on to marshal Any as a regular message.175}176
177// Marshal fields.178var err error179order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {180if err = e.marshalField(fd.TextName(), v, fd); err != nil {181return false182}183return true184})185if err != nil {186return err187}188
189// Marshal unknown fields.190if e.opts.EmitUnknown {191e.marshalUnknown(m.GetUnknown())192}193
194return nil195}
196
197// marshalField marshals the given field with protoreflect.Value.
198func (e encoder) marshalField(name string, val protoreflect.Value, fd protoreflect.FieldDescriptor) error {199switch {200case fd.IsList():201return e.marshalList(name, val.List(), fd)202case fd.IsMap():203return e.marshalMap(name, val.Map(), fd)204default:205e.WriteName(name)206return e.marshalSingular(val, fd)207}208}
209
210// marshalSingular marshals the given non-repeated field value. This includes
211// all scalar types, enums, messages, and groups.
212func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {213kind := fd.Kind()214switch kind {215case protoreflect.BoolKind:216e.WriteBool(val.Bool())217
218case protoreflect.StringKind:219s := val.String()220if !e.opts.allowInvalidUTF8 && strs.EnforceUTF8(fd) && !utf8.ValidString(s) {221return errors.InvalidUTF8(string(fd.FullName()))222}223e.WriteString(s)224
225case protoreflect.Int32Kind, protoreflect.Int64Kind,226protoreflect.Sint32Kind, protoreflect.Sint64Kind,227protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind:228e.WriteInt(val.Int())229
230case protoreflect.Uint32Kind, protoreflect.Uint64Kind,231protoreflect.Fixed32Kind, protoreflect.Fixed64Kind:232e.WriteUint(val.Uint())233
234case protoreflect.FloatKind:235// Encoder.WriteFloat handles the special numbers NaN and infinites.236e.WriteFloat(val.Float(), 32)237
238case protoreflect.DoubleKind:239// Encoder.WriteFloat handles the special numbers NaN and infinites.240e.WriteFloat(val.Float(), 64)241
242case protoreflect.BytesKind:243e.WriteString(string(val.Bytes()))244
245case protoreflect.EnumKind:246num := val.Enum()247if desc := fd.Enum().Values().ByNumber(num); desc != nil {248e.WriteLiteral(string(desc.Name()))249} else {250// Use numeric value if there is no enum description.251e.WriteInt(int64(num))252}253
254case protoreflect.MessageKind, protoreflect.GroupKind:255return e.marshalMessage(val.Message(), true)256
257default:258panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))259}260return nil261}
262
263// marshalList marshals the given protoreflect.List as multiple name-value fields.
264func (e encoder) marshalList(name string, list protoreflect.List, fd protoreflect.FieldDescriptor) error {265size := list.Len()266for i := 0; i < size; i++ {267e.WriteName(name)268if err := e.marshalSingular(list.Get(i), fd); err != nil {269return err270}271}272return nil273}
274
275// marshalMap marshals the given protoreflect.Map as multiple name-value fields.
276func (e encoder) marshalMap(name string, mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error {277var err error278order.RangeEntries(mmap, order.GenericKeyOrder, func(key protoreflect.MapKey, val protoreflect.Value) bool {279e.WriteName(name)280e.StartMessage()281defer e.EndMessage()282
283e.WriteName(string(genid.MapEntry_Key_field_name))284err = e.marshalSingular(key.Value(), fd.MapKey())285if err != nil {286return false287}288
289e.WriteName(string(genid.MapEntry_Value_field_name))290err = e.marshalSingular(val, fd.MapValue())291if err != nil {292return false293}294return true295})296return err297}
298
299// marshalUnknown parses the given []byte and marshals fields out.
300// This function assumes proper encoding in the given []byte.
301func (e encoder) marshalUnknown(b []byte) {302const dec = 10303const hex = 16304for len(b) > 0 {305num, wtype, n := protowire.ConsumeTag(b)306b = b[n:]307e.WriteName(strconv.FormatInt(int64(num), dec))308
309switch wtype {310case protowire.VarintType:311var v uint64312v, n = protowire.ConsumeVarint(b)313e.WriteUint(v)314case protowire.Fixed32Type:315var v uint32316v, n = protowire.ConsumeFixed32(b)317e.WriteLiteral("0x" + strconv.FormatUint(uint64(v), hex))318case protowire.Fixed64Type:319var v uint64320v, n = protowire.ConsumeFixed64(b)321e.WriteLiteral("0x" + strconv.FormatUint(v, hex))322case protowire.BytesType:323var v []byte324v, n = protowire.ConsumeBytes(b)325e.WriteString(string(v))326case protowire.StartGroupType:327e.StartMessage()328var v []byte329v, n = protowire.ConsumeGroup(num, b)330e.marshalUnknown(v)331e.EndMessage()332default:333panic(fmt.Sprintf("prototext: error parsing unknown field wire type: %v", wtype))334}335
336b = b[n:]337}338}
339
340// marshalAny marshals the given google.protobuf.Any message in expanded form.
341// It returns true if it was able to marshal, else false.
342func (e encoder) marshalAny(any protoreflect.Message) bool {343// Construct the embedded message.344fds := any.Descriptor().Fields()345fdType := fds.ByNumber(genid.Any_TypeUrl_field_number)346typeURL := any.Get(fdType).String()347mt, err := e.opts.Resolver.FindMessageByURL(typeURL)348if err != nil {349return false350}351m := mt.New().Interface()352
353// Unmarshal bytes into embedded message.354fdValue := fds.ByNumber(genid.Any_Value_field_number)355value := any.Get(fdValue)356err = proto.UnmarshalOptions{357AllowPartial: true,358Resolver: e.opts.Resolver,359}.Unmarshal(value.Bytes(), m)360if err != nil {361return false362}363
364// Get current encoder position. If marshaling fails, reset encoder output365// back to this position.366pos := e.Snapshot()367
368// Field name is the proto field name enclosed in [].369e.WriteName("[" + typeURL + "]")370err = e.marshalMessage(m.ProtoReflect(), true)371if err != nil {372e.Reset(pos)373return false374}375return true376}
377