123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- // 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 defval marshals and unmarshals textual forms of default values.
- //
- // This package handles both the form historically used in Go struct field tags
- // and also the form used by google.protobuf.FieldDescriptorProto.default_value
- // since they differ in superficial ways.
- package defval
- import (
- "fmt"
- "math"
- "strconv"
- ptext "google.golang.org/protobuf/internal/encoding/text"
- errors "google.golang.org/protobuf/internal/errors"
- pref "google.golang.org/protobuf/reflect/protoreflect"
- )
- // Format is the serialization format used to represent the default value.
- type Format int
- const (
- _ Format = iota
- // Descriptor uses the serialization format that protoc uses with the
- // google.protobuf.FieldDescriptorProto.default_value field.
- Descriptor
- // GoTag uses the historical serialization format in Go struct field tags.
- GoTag
- )
- // Unmarshal deserializes the default string s according to the given kind k.
- // When k is an enum, a list of enum value descriptors must be provided.
- func Unmarshal(s string, k pref.Kind, evs pref.EnumValueDescriptors, f Format) (pref.Value, pref.EnumValueDescriptor, error) {
- switch k {
- case pref.BoolKind:
- if f == GoTag {
- switch s {
- case "1":
- return pref.ValueOfBool(true), nil, nil
- case "0":
- return pref.ValueOfBool(false), nil, nil
- }
- } else {
- switch s {
- case "true":
- return pref.ValueOfBool(true), nil, nil
- case "false":
- return pref.ValueOfBool(false), nil, nil
- }
- }
- case pref.EnumKind:
- if f == GoTag {
- // Go tags use the numeric form of the enum value.
- if n, err := strconv.ParseInt(s, 10, 32); err == nil {
- if ev := evs.ByNumber(pref.EnumNumber(n)); ev != nil {
- return pref.ValueOfEnum(ev.Number()), ev, nil
- }
- }
- } else {
- // Descriptor default_value use the enum identifier.
- ev := evs.ByName(pref.Name(s))
- if ev != nil {
- return pref.ValueOfEnum(ev.Number()), ev, nil
- }
- }
- case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
- if v, err := strconv.ParseInt(s, 10, 32); err == nil {
- return pref.ValueOfInt32(int32(v)), nil, nil
- }
- case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
- if v, err := strconv.ParseInt(s, 10, 64); err == nil {
- return pref.ValueOfInt64(int64(v)), nil, nil
- }
- case pref.Uint32Kind, pref.Fixed32Kind:
- if v, err := strconv.ParseUint(s, 10, 32); err == nil {
- return pref.ValueOfUint32(uint32(v)), nil, nil
- }
- case pref.Uint64Kind, pref.Fixed64Kind:
- if v, err := strconv.ParseUint(s, 10, 64); err == nil {
- return pref.ValueOfUint64(uint64(v)), nil, nil
- }
- case pref.FloatKind, pref.DoubleKind:
- var v float64
- var err error
- switch s {
- case "-inf":
- v = math.Inf(-1)
- case "inf":
- v = math.Inf(+1)
- case "nan":
- v = math.NaN()
- default:
- v, err = strconv.ParseFloat(s, 64)
- }
- if err == nil {
- if k == pref.FloatKind {
- return pref.ValueOfFloat32(float32(v)), nil, nil
- } else {
- return pref.ValueOfFloat64(float64(v)), nil, nil
- }
- }
- case pref.StringKind:
- // String values are already unescaped and can be used as is.
- return pref.ValueOfString(s), nil, nil
- case pref.BytesKind:
- if b, ok := unmarshalBytes(s); ok {
- return pref.ValueOfBytes(b), nil, nil
- }
- }
- return pref.Value{}, nil, errors.New("could not parse value for %v: %q", k, s)
- }
- // Marshal serializes v as the default string according to the given kind k.
- // When specifying the Descriptor format for an enum kind, the associated
- // enum value descriptor must be provided.
- func Marshal(v pref.Value, ev pref.EnumValueDescriptor, k pref.Kind, f Format) (string, error) {
- switch k {
- case pref.BoolKind:
- if f == GoTag {
- if v.Bool() {
- return "1", nil
- } else {
- return "0", nil
- }
- } else {
- if v.Bool() {
- return "true", nil
- } else {
- return "false", nil
- }
- }
- case pref.EnumKind:
- if f == GoTag {
- return strconv.FormatInt(int64(v.Enum()), 10), nil
- } else {
- return string(ev.Name()), nil
- }
- case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind, pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
- return strconv.FormatInt(v.Int(), 10), nil
- case pref.Uint32Kind, pref.Fixed32Kind, pref.Uint64Kind, pref.Fixed64Kind:
- return strconv.FormatUint(v.Uint(), 10), nil
- case pref.FloatKind, pref.DoubleKind:
- f := v.Float()
- switch {
- case math.IsInf(f, -1):
- return "-inf", nil
- case math.IsInf(f, +1):
- return "inf", nil
- case math.IsNaN(f):
- return "nan", nil
- default:
- if k == pref.FloatKind {
- return strconv.FormatFloat(f, 'g', -1, 32), nil
- } else {
- return strconv.FormatFloat(f, 'g', -1, 64), nil
- }
- }
- case pref.StringKind:
- // String values are serialized as is without any escaping.
- return v.String(), nil
- case pref.BytesKind:
- if s, ok := marshalBytes(v.Bytes()); ok {
- return s, nil
- }
- }
- return "", errors.New("could not format value for %v: %v", k, v)
- }
- // unmarshalBytes deserializes bytes by applying C unescaping.
- func unmarshalBytes(s string) ([]byte, bool) {
- // Bytes values use the same escaping as the text format,
- // however they lack the surrounding double quotes.
- // TODO: Export unmarshalString in the text package to avoid this hack.
- v, err := ptext.Unmarshal([]byte(`["` + s + `"]:0`))
- if err == nil && len(v.Message()) == 1 {
- s := v.Message()[0][0].String()
- return []byte(s), true
- }
- return nil, false
- }
- // marshalBytes serializes bytes by using C escaping.
- // To match the exact output of protoc, this is identical to the
- // CEscape function in strutil.cc of the protoc source code.
- func marshalBytes(b []byte) (string, bool) {
- var s []byte
- for _, c := range b {
- switch c {
- case '\n':
- s = append(s, `\n`...)
- case '\r':
- s = append(s, `\r`...)
- case '\t':
- s = append(s, `\t`...)
- case '"':
- s = append(s, `\"`...)
- case '\'':
- s = append(s, `\'`...)
- case '\\':
- s = append(s, `\\`...)
- default:
- if printableASCII := c >= 0x20 && c <= 0x7e; printableASCII {
- s = append(s, c)
- } else {
- s = append(s, fmt.Sprintf(`\%03o`, c)...)
- }
- }
- }
- return string(s), true
- }
|