gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encode.go (11227B)


      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.
    152 func (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.
    192 func (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.
    206 func (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.
    258 func (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.
    270 func (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.
    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.
    336 func (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 }