|
|
@@ -0,0 +1,681 @@
|
|
|
+// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+// Package pack enables manual encoding and decoding of protobuf wire data.
|
|
|
+//
|
|
|
+// This package is intended for use in debugging and/or creation of test data.
|
|
|
+// Proper usage of this package requires knowledge of the wire format.
|
|
|
+//
|
|
|
+// See https://developers.google.com/protocol-buffers/docs/encoding.
|
|
|
+package pack
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "math"
|
|
|
+ "path"
|
|
|
+ "reflect"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "unicode"
|
|
|
+ "unicode/utf8"
|
|
|
+
|
|
|
+ "google.golang.org/proto/internal/encoding/wire"
|
|
|
+ "google.golang.org/proto/reflect/protoreflect"
|
|
|
+)
|
|
|
+
|
|
|
+// Number is the field number; aliased from the wire package for convenience.
|
|
|
+type Number = wire.Number
|
|
|
+
|
|
|
+// Number type constants; copied from the wire package for convenience.
|
|
|
+const (
|
|
|
+ MinValidNumber Number = wire.MinValidNumber
|
|
|
+ FirstReservedNumber Number = wire.FirstReservedNumber
|
|
|
+ LastReservedNumber Number = wire.LastReservedNumber
|
|
|
+ MaxValidNumber Number = wire.MaxValidNumber
|
|
|
+)
|
|
|
+
|
|
|
+// Type is the wire type; aliased from the wire package for convenience.
|
|
|
+type Type = wire.Type
|
|
|
+
|
|
|
+// Wire type constants; copied from the wire package for convenience.
|
|
|
+const (
|
|
|
+ VarintType Type = wire.VarintType
|
|
|
+ Fixed32Type Type = wire.Fixed32Type
|
|
|
+ Fixed64Type Type = wire.Fixed64Type
|
|
|
+ BytesType Type = wire.BytesType
|
|
|
+ StartGroupType Type = wire.StartGroupType
|
|
|
+ EndGroupType Type = wire.EndGroupType
|
|
|
+)
|
|
|
+
|
|
|
+type (
|
|
|
+ // Token is any other type (e.g., Message, Tag, Varint, Float32, etc).
|
|
|
+ Token token
|
|
|
+ // Message is a ordered sequence Tokens.
|
|
|
+ Message []Token
|
|
|
+
|
|
|
+ // Tag is a tuple of the field number and the wire type.
|
|
|
+ Tag struct {
|
|
|
+ Number Number
|
|
|
+ Type Type
|
|
|
+ }
|
|
|
+ // Bool is a boolean.
|
|
|
+ Bool bool
|
|
|
+ // Varint is a signed varint using 64-bit two's complement encoding.
|
|
|
+ Varint int64
|
|
|
+ // Svarint is a signed varint using zig-zag encoding.
|
|
|
+ Svarint int64
|
|
|
+ // Uvarint is a unsigned varint.
|
|
|
+ Uvarint uint64
|
|
|
+
|
|
|
+ // Int32 is a signed 32-bit fixed-width integer.
|
|
|
+ Int32 int32
|
|
|
+ // Uint32 is an unsigned 32-bit fixed-width integer.
|
|
|
+ Uint32 uint32
|
|
|
+ // Float32 is a 32-bit fixed-width floating point number.
|
|
|
+ Float32 float32
|
|
|
+
|
|
|
+ // Int64 is a signed 64-bit fixed-width integer.
|
|
|
+ Int64 int64
|
|
|
+ // Uint64 is an unsigned 64-bit fixed-width integer.
|
|
|
+ Uint64 uint64
|
|
|
+ // Float64 is a 64-bit fixed-width floating point number.
|
|
|
+ Float64 float64
|
|
|
+
|
|
|
+ // String is a length-prefixed string.
|
|
|
+ String string
|
|
|
+ // Bytes is a length-prefixed bytes.
|
|
|
+ Bytes []byte
|
|
|
+ // LengthPrefix is a length-prefixed message.
|
|
|
+ LengthPrefix Message
|
|
|
+
|
|
|
+ // Denormalized is a denormalized varint value, where a varint is encoded
|
|
|
+ // using more bytes than is strictly necessary. The number of extra bytes
|
|
|
+ // alone is sufficient to losslessly represent the denormalized varint.
|
|
|
+ //
|
|
|
+ // The value may be one of Tag, Bool, Varint, Svarint, or Uvarint,
|
|
|
+ // where the varint representation of each token is denormalized.
|
|
|
+ //
|
|
|
+ // Alternatively, the value may be one of String, Bytes, or LengthPrefix,
|
|
|
+ // where the varint representation of the length-prefix is denormalized.
|
|
|
+ Denormalized struct {
|
|
|
+ Count uint // number of extra bytes
|
|
|
+ Value Token
|
|
|
+ }
|
|
|
+
|
|
|
+ // Raw are bytes directly appended to output.
|
|
|
+ Raw []byte
|
|
|
+)
|
|
|
+
|
|
|
+type token interface {
|
|
|
+ isToken()
|
|
|
+}
|
|
|
+
|
|
|
+func (Message) isToken() {}
|
|
|
+func (Tag) isToken() {}
|
|
|
+func (Bool) isToken() {}
|
|
|
+func (Varint) isToken() {}
|
|
|
+func (Svarint) isToken() {}
|
|
|
+func (Uvarint) isToken() {}
|
|
|
+func (Int32) isToken() {}
|
|
|
+func (Uint32) isToken() {}
|
|
|
+func (Float32) isToken() {}
|
|
|
+func (Int64) isToken() {}
|
|
|
+func (Uint64) isToken() {}
|
|
|
+func (Float64) isToken() {}
|
|
|
+func (String) isToken() {}
|
|
|
+func (Bytes) isToken() {}
|
|
|
+func (LengthPrefix) isToken() {}
|
|
|
+func (Denormalized) isToken() {}
|
|
|
+func (Raw) isToken() {}
|
|
|
+
|
|
|
+// Size reports the size in bytes of the marshaled message.
|
|
|
+func (m Message) Size() int {
|
|
|
+ var n int
|
|
|
+ for _, v := range m {
|
|
|
+ switch v := v.(type) {
|
|
|
+ case Message:
|
|
|
+ n += v.Size()
|
|
|
+ case Tag:
|
|
|
+ n += wire.SizeTag(v.Number)
|
|
|
+ case Bool:
|
|
|
+ n += wire.SizeVarint(wire.EncodeBool(false))
|
|
|
+ case Varint:
|
|
|
+ n += wire.SizeVarint(uint64(v))
|
|
|
+ case Svarint:
|
|
|
+ n += wire.SizeVarint(wire.EncodeZigZag(int64(v)))
|
|
|
+ case Uvarint:
|
|
|
+ n += wire.SizeVarint(uint64(v))
|
|
|
+ case Int32, Uint32, Float32:
|
|
|
+ n += wire.SizeFixed32()
|
|
|
+ case Int64, Uint64, Float64:
|
|
|
+ n += wire.SizeFixed64()
|
|
|
+ case String:
|
|
|
+ n += wire.SizeBytes(len(v))
|
|
|
+ case Bytes:
|
|
|
+ n += wire.SizeBytes(len(v))
|
|
|
+ case LengthPrefix:
|
|
|
+ n += wire.SizeBytes(Message(v).Size())
|
|
|
+ case Denormalized:
|
|
|
+ n += int(v.Count) + Message{v.Value}.Size()
|
|
|
+ case Raw:
|
|
|
+ n += len(v)
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("unknown type: %T", v))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return n
|
|
|
+}
|
|
|
+
|
|
|
+// Message encodes an AST into the protobuf wire format.
|
|
|
+//
|
|
|
+// Example message definition:
|
|
|
+// message MyMessage {
|
|
|
+// string field1 = 1;
|
|
|
+// int64 field2 = 2;
|
|
|
+// repeated float32 field3 = 3;
|
|
|
+// }
|
|
|
+//
|
|
|
+// Example encoded message:
|
|
|
+// b := Message{
|
|
|
+// Tag{1, BytesType}, String("Hello, world!"),
|
|
|
+// Tag{2, VarintType}, Varint(-10),
|
|
|
+// Tag{3, BytesType}, LengthPrefix{
|
|
|
+// Float32(1.1), Float32(2.2), Float32(3.3),
|
|
|
+// },
|
|
|
+// }.Marshal()
|
|
|
+//
|
|
|
+// Resulting wire data:
|
|
|
+// 0x0000 0a 0d 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 10 |..Hello, world!.|
|
|
|
+// 0x0010 f6 ff ff ff ff ff ff ff ff 01 1a 0c cd cc 8c 3f |...............?|
|
|
|
+// 0x0020 cd cc 0c 40 33 33 53 40 |...@33S@|
|
|
|
+func (m Message) Marshal() []byte {
|
|
|
+ var out []byte
|
|
|
+ for _, v := range m {
|
|
|
+ switch v := v.(type) {
|
|
|
+ case Message:
|
|
|
+ out = append(out, v.Marshal()...)
|
|
|
+ case Tag:
|
|
|
+ out = wire.AppendTag(out, v.Number, v.Type)
|
|
|
+ case Bool:
|
|
|
+ out = wire.AppendVarint(out, wire.EncodeBool(bool(v)))
|
|
|
+ case Varint:
|
|
|
+ out = wire.AppendVarint(out, uint64(v))
|
|
|
+ case Svarint:
|
|
|
+ out = wire.AppendVarint(out, wire.EncodeZigZag(int64(v)))
|
|
|
+ case Uvarint:
|
|
|
+ out = wire.AppendVarint(out, uint64(v))
|
|
|
+ case Int32:
|
|
|
+ out = wire.AppendFixed32(out, uint32(v))
|
|
|
+ case Uint32:
|
|
|
+ out = wire.AppendFixed32(out, uint32(v))
|
|
|
+ case Float32:
|
|
|
+ out = wire.AppendFixed32(out, math.Float32bits(float32(v)))
|
|
|
+ case Int64:
|
|
|
+ out = wire.AppendFixed64(out, uint64(v))
|
|
|
+ case Uint64:
|
|
|
+ out = wire.AppendFixed64(out, uint64(v))
|
|
|
+ case Float64:
|
|
|
+ out = wire.AppendFixed64(out, math.Float64bits(float64(v)))
|
|
|
+ case String:
|
|
|
+ out = wire.AppendBytes(out, []byte(v))
|
|
|
+ case Bytes:
|
|
|
+ out = wire.AppendBytes(out, []byte(v))
|
|
|
+ case LengthPrefix:
|
|
|
+ out = wire.AppendBytes(out, Message(v).Marshal())
|
|
|
+ case Denormalized:
|
|
|
+ b := Message{v.Value}.Marshal()
|
|
|
+ _, n := wire.ConsumeVarint(b)
|
|
|
+ out = append(out, b[:n]...)
|
|
|
+ for i := uint(0); i < v.Count; i++ {
|
|
|
+ out[len(out)-1] |= 0x80 // set continuation bit on previous
|
|
|
+ out = append(out, 0)
|
|
|
+ }
|
|
|
+ out = append(out, b[n:]...)
|
|
|
+ case Raw:
|
|
|
+ return append(out, v...)
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("unknown type: %T", v))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return out
|
|
|
+}
|
|
|
+
|
|
|
+// Unmarshal parses the input protobuf wire data as a Message AST.
|
|
|
+// Any parsing error results in the remainder of the input being
|
|
|
+// concatenated to the message as a Raw type.
|
|
|
+//
|
|
|
+// Each tag (a tuple of the field number and wire type) encountered is
|
|
|
+// appended to the AST as a Tag.
|
|
|
+//
|
|
|
+// The contents of each wire type is mapped to the following Go types:
|
|
|
+// VarintType => Uvarint
|
|
|
+// Fixed32Type => Uint32
|
|
|
+// Fixed64Type => Uint64
|
|
|
+// BytesType => Bytes
|
|
|
+// GroupType => Message
|
|
|
+//
|
|
|
+// Since the wire format is not self-describing, this function cannot parse
|
|
|
+// sub-messages and will leave them as the Bytes type. Further manual parsing
|
|
|
+// can be performed as such:
|
|
|
+// var m, m1, m2 Message
|
|
|
+// m.Unmarshal(b)
|
|
|
+// m1.Unmarshal(m[3].(Bytes))
|
|
|
+// m[3] = LengthPrefix(m1)
|
|
|
+// m2.Unmarshal(m[3].(LengthPrefix)[1].(Bytes))
|
|
|
+// m[3].(LengthPrefix)[1] = LengthPrefix(m2)
|
|
|
+//
|
|
|
+// Unmarshal is useful for debugging the protobuf wire format.
|
|
|
+func (m *Message) Unmarshal(in []byte) {
|
|
|
+ m.UnmarshalDescriptor(in, nil)
|
|
|
+}
|
|
|
+
|
|
|
+// UnmarshalDescriptor parses the input protobuf wire data as a Message AST
|
|
|
+// using the provided message descriptor for more accurate parsing of fields.
|
|
|
+// It operates like Unmarshal, but may use a wider range of Go types to
|
|
|
+// represent the wire data.
|
|
|
+//
|
|
|
+// The contents of each wire type is mapped to one of the following Go types:
|
|
|
+// VarintType => Bool, Varint, Svarint, Uvarint
|
|
|
+// Fixed32Type => Int32, Uint32, Float32
|
|
|
+// Fixed64Type => Uint32, Uint64, Float64
|
|
|
+// BytesType => String, Bytes, LengthPrefix
|
|
|
+// GroupType => Message
|
|
|
+//
|
|
|
+// If the field is unknown, it uses the same mapping as Unmarshal.
|
|
|
+// Known sub-messages are parsed as a Message and packed repeated fields are
|
|
|
+// parsed as a LengthPrefix.
|
|
|
+func (m *Message) UnmarshalDescriptor(in []byte, desc protoreflect.MessageDescriptor) {
|
|
|
+ p := parser{in: in, out: *m}
|
|
|
+ p.parseMessage(desc, false)
|
|
|
+ *m = p.out
|
|
|
+}
|
|
|
+
|
|
|
+type parser struct {
|
|
|
+ in []byte
|
|
|
+ out []Token
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseMessage(msgDesc protoreflect.MessageDescriptor, group bool) {
|
|
|
+ for len(p.in) > 0 {
|
|
|
+ v, n := wire.ConsumeVarint(p.in)
|
|
|
+ num, typ := wire.DecodeTag(v)
|
|
|
+ if n < 0 || num < 0 || v > math.MaxUint32 {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if typ == EndGroupType && group {
|
|
|
+ return // if inside a group, then stop
|
|
|
+ }
|
|
|
+ p.out, p.in = append(p.out, Tag{num, typ}), p.in[n:]
|
|
|
+ if m := n - wire.SizeVarint(v); m > 0 {
|
|
|
+ p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
|
|
|
+ }
|
|
|
+
|
|
|
+ // If descriptor is available, use it for more accurate parsing.
|
|
|
+ var isPacked bool
|
|
|
+ var kind protoreflect.Kind
|
|
|
+ var subDesc protoreflect.MessageDescriptor
|
|
|
+ if msgDesc != nil && !msgDesc.IsPlaceholder() {
|
|
|
+ if fieldDesc := msgDesc.Fields().ByNumber(num); fieldDesc != nil {
|
|
|
+ isPacked = fieldDesc.IsPacked()
|
|
|
+ kind = fieldDesc.Kind()
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ subDesc = fieldDesc.MessageType()
|
|
|
+ if subDesc == nil || subDesc.IsPlaceholder() {
|
|
|
+ kind = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ switch typ {
|
|
|
+ case VarintType:
|
|
|
+ p.parseVarint(kind)
|
|
|
+ case Fixed32Type:
|
|
|
+ p.parseFixed32(kind)
|
|
|
+ case Fixed64Type:
|
|
|
+ p.parseFixed64(kind)
|
|
|
+ case BytesType:
|
|
|
+ p.parseBytes(isPacked, kind, subDesc)
|
|
|
+ case StartGroupType:
|
|
|
+ p.parseGroup(subDesc)
|
|
|
+ case EndGroupType:
|
|
|
+ // Handled above.
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseVarint(kind protoreflect.Kind) {
|
|
|
+ v, n := wire.ConsumeVarint(p.in)
|
|
|
+ if n < 0 {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.BoolKind:
|
|
|
+ switch v {
|
|
|
+ case 0:
|
|
|
+ p.out, p.in = append(p.out, Bool(false)), p.in[n:]
|
|
|
+ case 1:
|
|
|
+ p.out, p.in = append(p.out, Bool(true)), p.in[n:]
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
|
|
|
+ }
|
|
|
+ case protoreflect.Int32Kind, protoreflect.Int64Kind:
|
|
|
+ p.out, p.in = append(p.out, Varint(v)), p.in[n:]
|
|
|
+ case protoreflect.Sint32Kind, protoreflect.Sint64Kind:
|
|
|
+ p.out, p.in = append(p.out, Svarint(wire.DecodeZigZag(v))), p.in[n:]
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
|
|
|
+ }
|
|
|
+ if m := n - wire.SizeVarint(v); m > 0 {
|
|
|
+ p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseFixed32(kind protoreflect.Kind) {
|
|
|
+ v, n := wire.ConsumeFixed32(p.in)
|
|
|
+ if n < 0 {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.FloatKind:
|
|
|
+ p.out, p.in = append(p.out, Float32(math.Float32frombits(v))), p.in[n:]
|
|
|
+ case protoreflect.Sfixed32Kind:
|
|
|
+ p.out, p.in = append(p.out, Int32(v)), p.in[n:]
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Uint32(v)), p.in[n:]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseFixed64(kind protoreflect.Kind) {
|
|
|
+ v, n := wire.ConsumeFixed64(p.in)
|
|
|
+ if n < 0 {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.DoubleKind:
|
|
|
+ p.out, p.in = append(p.out, Float64(math.Float64frombits(v))), p.in[n:]
|
|
|
+ case protoreflect.Sfixed64Kind:
|
|
|
+ p.out, p.in = append(p.out, Int64(v)), p.in[n:]
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Uint64(v)), p.in[n:]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseBytes(isPacked bool, kind protoreflect.Kind, desc protoreflect.MessageDescriptor) {
|
|
|
+ v, n := wire.ConsumeVarint(p.in)
|
|
|
+ if n < 0 {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
|
|
|
+ if m := n - wire.SizeVarint(v); m > 0 {
|
|
|
+ p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
|
|
|
+ }
|
|
|
+ if v > uint64(len(p.in)) {
|
|
|
+ p.out, p.in = append(p.out, Raw(p.in)), nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ p.out = p.out[:len(p.out)-1] // subsequent tokens contain prefix-length
|
|
|
+
|
|
|
+ if isPacked {
|
|
|
+ p.parsePacked(int(v), kind)
|
|
|
+ } else {
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.MessageKind:
|
|
|
+ p2 := parser{in: p.in[:v]}
|
|
|
+ p2.parseMessage(desc, false)
|
|
|
+ p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[v:]
|
|
|
+ case protoreflect.StringKind:
|
|
|
+ p.out, p.in = append(p.out, String(p.in[:v])), p.in[v:]
|
|
|
+ default:
|
|
|
+ p.out, p.in = append(p.out, Bytes(p.in[:v])), p.in[v:]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if m := n - wire.SizeVarint(v); m > 0 {
|
|
|
+ p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parsePacked(n int, kind protoreflect.Kind) {
|
|
|
+ p2 := parser{in: p.in[:n]}
|
|
|
+ for len(p2.in) > 0 {
|
|
|
+ switch kind {
|
|
|
+ case protoreflect.BoolKind, protoreflect.EnumKind,
|
|
|
+ protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
|
|
|
+ protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
|
|
|
+ p2.parseVarint(kind)
|
|
|
+ case protoreflect.Fixed32Kind, protoreflect.Sfixed32Kind, protoreflect.FloatKind:
|
|
|
+ p2.parseFixed32(kind)
|
|
|
+ case protoreflect.Fixed64Kind, protoreflect.Sfixed64Kind, protoreflect.DoubleKind:
|
|
|
+ p2.parseFixed64(kind)
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("invalid packed kind: %v", kind))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[n:]
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseGroup(desc protoreflect.MessageDescriptor) {
|
|
|
+ p2 := parser{in: p.in}
|
|
|
+ p2.parseMessage(desc, true)
|
|
|
+ if len(p2.out) > 0 {
|
|
|
+ p.out = append(p.out, Message(p2.out))
|
|
|
+ }
|
|
|
+ p.in = p2.in
|
|
|
+
|
|
|
+ // Append the trailing end group.
|
|
|
+ v, n := wire.ConsumeVarint(p.in)
|
|
|
+ if num, typ := wire.DecodeTag(v); typ == EndGroupType {
|
|
|
+ p.out, p.in = append(p.out, Tag{num, typ}), p.in[n:]
|
|
|
+ if m := n - wire.SizeVarint(v); m > 0 {
|
|
|
+ p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Format implements a custom formatter to visualize the Message AST.
|
|
|
+// Using "%#v" formats the Message in Go source code.
|
|
|
+func (m Message) Format(s fmt.State, r rune) {
|
|
|
+ switch r {
|
|
|
+ case 'x':
|
|
|
+ io.WriteString(s, fmt.Sprintf("%x", m.Marshal()))
|
|
|
+ case 'X':
|
|
|
+ io.WriteString(s, fmt.Sprintf("%X", m.Marshal()))
|
|
|
+ case 'v':
|
|
|
+ switch {
|
|
|
+ case s.Flag('#'):
|
|
|
+ io.WriteString(s, m.format(true, true))
|
|
|
+ case s.Flag('+'):
|
|
|
+ io.WriteString(s, m.format(false, true))
|
|
|
+ default:
|
|
|
+ io.WriteString(s, m.format(false, false))
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ panic("invalid verb: " + string(r))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// format formats the message.
|
|
|
+// If source is enabled, this emits valid Go source.
|
|
|
+// If multi is enabled, the output may span multiple lines.
|
|
|
+func (m Message) format(source, multi bool) string {
|
|
|
+ var ss []string
|
|
|
+ var prefix, nextPrefix string
|
|
|
+ for _, v := range m {
|
|
|
+ // Ensure certain tokens have preceding or succeeding newlines.
|
|
|
+ prefix, nextPrefix = nextPrefix, " "
|
|
|
+ if multi {
|
|
|
+ switch v := v.(type) {
|
|
|
+ case Tag: // only has preceding newline
|
|
|
+ prefix = "\n"
|
|
|
+ case Denormalized: // only has preceding newline
|
|
|
+ if _, ok := v.Value.(Tag); ok {
|
|
|
+ prefix = "\n"
|
|
|
+ }
|
|
|
+ case Message, Raw: // has preceding and succeeding newlines
|
|
|
+ prefix, nextPrefix = "\n", "\n"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ s := formatToken(v, source, multi)
|
|
|
+ ss = append(ss, prefix+s+",")
|
|
|
+ }
|
|
|
+
|
|
|
+ var s string
|
|
|
+ if len(ss) > 0 {
|
|
|
+ s = strings.TrimSpace(strings.Join(ss, ""))
|
|
|
+ if multi {
|
|
|
+ s = "\n\t" + strings.Join(strings.Split(s, "\n"), "\n\t") + "\n"
|
|
|
+ } else {
|
|
|
+ s = strings.TrimSuffix(s, ",")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ s = fmt.Sprintf("%T{%s}", m, s)
|
|
|
+ if !source {
|
|
|
+ s = trimPackage(s)
|
|
|
+ }
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
+// formatToken formats a single token.
|
|
|
+func formatToken(t Token, source, multi bool) (s string) {
|
|
|
+ switch v := t.(type) {
|
|
|
+ case Message:
|
|
|
+ s = v.format(source, multi)
|
|
|
+ case LengthPrefix:
|
|
|
+ s = formatPacked(v, source, multi)
|
|
|
+ if s == "" {
|
|
|
+ ms := Message(v).format(source, multi)
|
|
|
+ s = fmt.Sprintf("%T(%s)", v, ms)
|
|
|
+ }
|
|
|
+ case Tag:
|
|
|
+ s = fmt.Sprintf("%T{%d, %s}", v, v.Number, formatType(v.Type, source))
|
|
|
+ case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64:
|
|
|
+ if source {
|
|
|
+ // Print floats in a way that preserves exact precision.
|
|
|
+ if f, _ := v.(Float32); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
|
|
|
+ switch {
|
|
|
+ case f > 0:
|
|
|
+ s = fmt.Sprintf("%T(math.Inf(+1))", v)
|
|
|
+ case f < 0:
|
|
|
+ s = fmt.Sprintf("%T(math.Inf(-1))", v)
|
|
|
+ case math.Float32bits(float32(math.NaN())) == math.Float32bits(float32(f)):
|
|
|
+ s = fmt.Sprintf("%T(math.NaN())", v)
|
|
|
+ default:
|
|
|
+ s = fmt.Sprintf("%T(math.Float32frombits(0x%08x))", v, math.Float32bits(float32(f)))
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if f, _ := v.(Float64); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
|
|
|
+ switch {
|
|
|
+ case f > 0:
|
|
|
+ s = fmt.Sprintf("%T(math.Inf(+1))", v)
|
|
|
+ case f < 0:
|
|
|
+ s = fmt.Sprintf("%T(math.Inf(-1))", v)
|
|
|
+ case math.Float64bits(float64(math.NaN())) == math.Float64bits(float64(f)):
|
|
|
+ s = fmt.Sprintf("%T(math.NaN())", v)
|
|
|
+ default:
|
|
|
+ s = fmt.Sprintf("%T(math.Float64frombits(0x%08x))", v, math.Float64bits(float64(f)))
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ s = fmt.Sprintf("%T(%v)", v, v)
|
|
|
+ case String, Bytes, Raw:
|
|
|
+ s = fmt.Sprintf("%s", v)
|
|
|
+ s = fmt.Sprintf("%T(%s)", v, formatString(s))
|
|
|
+ case Denormalized:
|
|
|
+ s = fmt.Sprintf("%T{+%d, %v}", v, v.Count, formatToken(v.Value, source, multi))
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("unknown type: %T", v))
|
|
|
+ }
|
|
|
+ if !source {
|
|
|
+ s = trimPackage(s)
|
|
|
+ }
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
+// formatPacked returns a non-empty string if LengthPrefix looks like a packed
|
|
|
+// repeated field of primitives.
|
|
|
+func formatPacked(v LengthPrefix, source, multi bool) string {
|
|
|
+ var ss []string
|
|
|
+ for _, v := range v {
|
|
|
+ switch v.(type) {
|
|
|
+ case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64, Denormalized, Raw:
|
|
|
+ if v, ok := v.(Denormalized); ok {
|
|
|
+ switch v.Value.(type) {
|
|
|
+ case Bool, Varint, Svarint, Uvarint:
|
|
|
+ default:
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ss = append(ss, formatToken(v, source, multi))
|
|
|
+ default:
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+ s := fmt.Sprintf("%T{%s}", v, strings.Join(ss, ", "))
|
|
|
+ if !source {
|
|
|
+ s = trimPackage(s)
|
|
|
+ }
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
+// formatType returns the name for Type.
|
|
|
+func formatType(t Type, source bool) (s string) {
|
|
|
+ switch t {
|
|
|
+ case VarintType:
|
|
|
+ s = pkg + ".VarintType"
|
|
|
+ case Fixed32Type:
|
|
|
+ s = pkg + ".Fixed32Type"
|
|
|
+ case Fixed64Type:
|
|
|
+ s = pkg + ".Fixed64Type"
|
|
|
+ case BytesType:
|
|
|
+ s = pkg + ".BytesType"
|
|
|
+ case StartGroupType:
|
|
|
+ s = pkg + ".StartGroupType"
|
|
|
+ case EndGroupType:
|
|
|
+ s = pkg + ".EndGroupType"
|
|
|
+ default:
|
|
|
+ s = fmt.Sprintf("Type(%d)", t)
|
|
|
+ }
|
|
|
+ if !source {
|
|
|
+ s = strings.TrimSuffix(trimPackage(s), "Type")
|
|
|
+ }
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
+// formatString returns a quoted string for s.
|
|
|
+func formatString(s string) string {
|
|
|
+ // Use quoted string if it the same length as a raw string literal.
|
|
|
+ // Otherwise, attempt to use the raw string form.
|
|
|
+ qs := strconv.Quote(s)
|
|
|
+ if len(qs) == 1+len(s)+1 {
|
|
|
+ return qs
|
|
|
+ }
|
|
|
+
|
|
|
+ // Disallow newlines to ensure output is a single line.
|
|
|
+ // Disallow non-printable runes for readability purposes.
|
|
|
+ rawInvalid := func(r rune) bool {
|
|
|
+ return r == '`' || r == '\n' || r == utf8.RuneError || !unicode.IsPrint(r)
|
|
|
+ }
|
|
|
+ if strings.IndexFunc(s, rawInvalid) < 0 {
|
|
|
+ return "`" + s + "`"
|
|
|
+ }
|
|
|
+ return qs
|
|
|
+}
|
|
|
+
|
|
|
+var pkg = path.Base(reflect.TypeOf(Tag{}).PkgPath())
|
|
|
+
|
|
|
+func trimPackage(s string) string {
|
|
|
+ return strings.TrimPrefix(strings.TrimPrefix(s, pkg), ".")
|
|
|
+}
|