// 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 "" } } // 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 "" } } // 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("") } return p.out }