123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- // 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 text implements the text format for protocol buffers.
- // This package has no semantic understanding for protocol buffers and is only
- // a parser and composer for the format.
- //
- // There is no formal specification for the protobuf text format, as such the
- // C++ implementation (see google::protobuf::TextFormat) is the reference
- // implementation of the text format.
- //
- // This package is neither a superset nor a subset of the C++ implementation.
- // This implementation permits a more liberal grammar in some cases to be
- // backwards compatible with the historical Go implementation.
- // Future parsings unique to Go should not be added.
- // Some grammars allowed by the C++ implementation are deliberately
- // not implemented here because they are considered a bug by the protobuf team
- // and should not be replicated.
- //
- // The Go implementation should implement a sufficient amount of the C++
- // grammar such that the default text serialization by C++ can be parsed by Go.
- // However, just because the C++ parser accepts some input does not mean that
- // the Go implementation should as well.
- //
- // The text format is almost a superset of JSON except:
- // * message keys are not quoted strings, but identifiers
- // * the top-level value must be a message without the delimiters
- package text
- import (
- "fmt"
- "math"
- "strings"
- "google.golang.org/protobuf/internal/flags"
- "google.golang.org/protobuf/reflect/protoreflect"
- )
- // Type represents a type expressible in the text format.
- type Type uint8
- const (
- _ Type = iota
- // Bool is a boolean (e.g., "true" or "false").
- Bool
- // Int is a signed integer (e.g., "-1423").
- Int
- // Uint is an unsigned integer (e.g., "0xdeadbeef").
- Uint
- // Float32 is a 32-bit floating-point number (e.g., "1.234" or "1e38").
- // This allows encoding to differentiate the bitsize used for formatting.
- Float32
- // Float64 is a 64-bit floating-point number.
- Float64
- // String is a quoted string (e.g., `"the quick brown fox"`).
- String
- // Name is a protocol buffer identifier (e.g., `field_name`).
- Name
- // List is an ordered list of values (e.g., `[0, "one", true]`).
- List
- // Message is an ordered map of values (e.g., `{"key": null}`).
- Message
- )
- func (t Type) String() string {
- switch t {
- case Bool:
- return "bool"
- case Int:
- return "int"
- case Uint:
- return "uint"
- case Float32:
- return "float32"
- case Float64:
- return "float64"
- case String:
- return "string"
- case Name:
- return "name"
- case List:
- return "list"
- case Message:
- return "message"
- default:
- return "<invalid>"
- }
- }
- // Value contains a value of a given Type.
- type Value struct {
- typ Type
- raw []byte // raw bytes of the serialized data
- str string // only for String or Name
- num uint64 // only for Bool, Int, Uint, Float32, or Float64
- arr []Value // only for List
- obj [][2]Value // only for Message
- }
- // ValueOf returns a Value for a given Go value:
- // bool => Bool
- // int32, int64 => Int
- // uint32, uint64 => Uint
- // float32 => Float32
- // float64 => Float64
- // string, []byte => String
- // protoreflect.Name => Name
- // []Value => List
- // [][2]Value => Message
- //
- // ValueOf panics if the Go type is not one of the above.
- func ValueOf(v interface{}) Value {
- switch v := v.(type) {
- case bool:
- if v {
- return Value{typ: Bool, num: 1}
- } else {
- return Value{typ: Bool, num: 0}
- }
- case int32:
- return Value{typ: Int, num: uint64(v)}
- case int64:
- return Value{typ: Int, num: uint64(v)}
- case uint32:
- return Value{typ: Uint, num: uint64(v)}
- case uint64:
- return Value{typ: Uint, num: uint64(v)}
- case float32:
- // Store as float64 bits.
- return Value{typ: Float32, num: math.Float64bits(float64(v))}
- case float64:
- return Value{typ: Float64, num: math.Float64bits(float64(v))}
- case string:
- return Value{typ: String, str: string(v)}
- case []byte:
- return Value{typ: String, str: string(v)}
- case protoreflect.Name:
- return Value{typ: Name, str: string(v)}
- case []Value:
- return Value{typ: List, arr: v}
- case [][2]Value:
- return Value{typ: Message, obj: v}
- default:
- panic(fmt.Sprintf("invalid type %T", v))
- }
- }
- func rawValueOf(v interface{}, raw []byte) Value {
- v2 := ValueOf(v)
- v2.raw = raw
- return v2
- }
- // Type is the type of the value. When parsing, this is a best-effort guess
- // at the resulting type. However, there are ambiguities as to the exact type
- // of the value (e.g., "false" is either a bool or a name).
- // Thus, some of the types are convertible with each other.
- // The Bool, Int, Uint, Float32, Float64, and Name methods return a boolean to
- // report whether the conversion was successful.
- func (v Value) Type() Type {
- return v.typ
- }
- // Bool returns v as a bool and reports whether the conversion succeeded.
- func (v Value) Bool() (x bool, ok bool) {
- switch v.typ {
- case Bool:
- return v.num > 0, true
- case Uint, Int:
- // C++ allows a 1-bit unsigned integer (e.g., "0", "1", or "0x1").
- if len(v.raw) > 0 && v.raw[0] != '-' && v.num < 2 {
- return v.num > 0, true
- }
- }
- return false, false
- }
- // Int returns v as an int64 of the specified precision and reports whether
- // the conversion succeeded.
- func (v Value) Int(b64 bool) (x int64, ok bool) {
- switch v.typ {
- case Int:
- n := int64(v.num)
- if b64 || (math.MinInt32 <= n && n <= math.MaxInt32) {
- return int64(n), true
- }
- case Uint:
- n := uint64(v.num)
- if (!b64 && n <= math.MaxInt32) || (b64 && n <= math.MaxInt64) {
- return int64(n), true
- }
- // C++ accepts large positive hex numbers as negative values.
- // This feature is here for proto1 backwards compatibility purposes.
- if flags.ProtoLegacy && len(v.raw) > 1 && v.raw[0] == '0' && v.raw[1] == 'x' {
- if !b64 {
- return int64(int32(n)), n <= math.MaxUint32
- }
- // if !b64 && n <= math.MaxUint32 {
- // return int64(int32(n)), true
- // }
- return int64(n), true
- }
- }
- return 0, false
- }
- // Uint returns v as an uint64 of the specified precision and reports whether
- // the conversion succeeded.
- func (v Value) Uint(b64 bool) (x uint64, ok bool) {
- switch v.typ {
- case Int:
- n := int64(v.num)
- if len(v.raw) > 0 && v.raw[0] != '-' && (b64 || n <= math.MaxUint32) {
- return uint64(n), true
- }
- case Uint:
- n := uint64(v.num)
- if b64 || n <= math.MaxUint32 {
- return uint64(n), true
- }
- }
- return 0, false
- }
- // Float returns v as a float64 of the specified precision and reports whether
- // the conversion succeeded.
- func (v Value) Float(b64 bool) (x float64, ok bool) {
- switch v.typ {
- case Int:
- return float64(int64(v.num)), true // possibly lossy, but allowed
- case Uint:
- return float64(uint64(v.num)), true // possibly lossy, but allowed
- case Float32, Float64:
- n := math.Float64frombits(v.num)
- if math.IsNaN(n) || math.IsInf(n, 0) {
- return float64(n), true
- }
- if b64 || math.Abs(n) <= math.MaxFloat32 {
- return float64(n), true
- }
- }
- return 0, false
- }
- // String returns v as a string if the Type is String.
- // Otherwise, this returns a formatted string of v for debugging purposes.
- //
- // Since String is used to represent both text and binary, it is not validated
- // to contain valid UTF-8. When using this value with the string type in proto,
- // it is the user's responsibility perform additional UTF-8 validation.
- func (v Value) String() string {
- if v.typ != String {
- return v.stringValue()
- }
- return v.str
- }
- func (v Value) stringValue() string {
- switch v.typ {
- case Bool, Int, Uint, Float32, Float64, Name:
- return string(v.Raw())
- case List:
- var ss []string
- for _, v := range v.List() {
- ss = append(ss, v.String())
- }
- return "[" + strings.Join(ss, ",") + "]"
- case Message:
- var ss []string
- for _, v := range v.Message() {
- k := v[0].String()
- if v[0].Type() == String {
- k = "[" + k + "]"
- }
- ss = append(ss, k+":"+v[1].String())
- }
- return "{" + strings.Join(ss, ",") + "}"
- default:
- return "<invalid>"
- }
- }
- // Name returns the field name or enum value name and reports whether the value
- // can be treated as an identifier.
- func (v Value) Name() (protoreflect.Name, bool) {
- switch v.typ {
- case Bool, Float32, Float64:
- // Ambiguity arises in unmarshalValue since "nan" may interpreted as
- // either a Name type (for enum values) or a Float32/Float64 type.
- // Similarly, "true" may be interpreted as either a Name or Bool type.
- n := protoreflect.Name(v.raw)
- if n.IsValid() {
- return n, true
- }
- case Name:
- return protoreflect.Name(v.str), true
- }
- return "", false
- }
- // List returns the elements of v and panics if the Type is not List.
- // Mutations on the return value may not be observable from the Raw method.
- func (v Value) List() []Value {
- if v.typ != List {
- panic("value is not a list")
- }
- return v.arr
- }
- // Message returns the items of v and panics if the Type is not Message.
- // The [2]Value represents a key and value pair, where the key is either
- // a Name (representing a field name), a String (representing extension field
- // names or the Any type URL), or an Uint for unknown fields.
- //
- // Mutations on the return value may not be observable from the Raw method.
- func (v Value) Message() [][2]Value {
- if v.typ != Message {
- panic("value is not a message")
- }
- return v.obj
- }
- // Raw returns the raw representation of the value.
- // The returned value may alias the input given to Unmarshal.
- func (v Value) Raw() []byte {
- if len(v.raw) > 0 {
- return v.raw
- }
- p := encoder{}
- if err := p.marshalValue(v); err != nil {
- return []byte("<invalid>")
- }
- return p.out
- }
|