default.go (6511B)
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 defval marshals and unmarshals textual forms of default values. 6 // 7 // This package handles both the form historically used in Go struct field tags 8 // and also the form used by google.protobuf.FieldDescriptorProto.default_value 9 // since they differ in superficial ways. 10 package defval 11 12 import ( 13 "fmt" 14 "math" 15 "strconv" 16 17 ptext "google.golang.org/protobuf/internal/encoding/text" 18 "google.golang.org/protobuf/internal/errors" 19 "google.golang.org/protobuf/reflect/protoreflect" 20 ) 21 22 // Format is the serialization format used to represent the default value. 23 type Format int 24 25 const ( 26 _ Format = iota 27 28 // Descriptor uses the serialization format that protoc uses with the 29 // google.protobuf.FieldDescriptorProto.default_value field. 30 Descriptor 31 32 // GoTag uses the historical serialization format in Go struct field tags. 33 GoTag 34 ) 35 36 // Unmarshal deserializes the default string s according to the given kind k. 37 // When k is an enum, a list of enum value descriptors must be provided. 38 func Unmarshal(s string, k protoreflect.Kind, evs protoreflect.EnumValueDescriptors, f Format) (protoreflect.Value, protoreflect.EnumValueDescriptor, error) { 39 switch k { 40 case protoreflect.BoolKind: 41 if f == GoTag { 42 switch s { 43 case "1": 44 return protoreflect.ValueOfBool(true), nil, nil 45 case "0": 46 return protoreflect.ValueOfBool(false), nil, nil 47 } 48 } else { 49 switch s { 50 case "true": 51 return protoreflect.ValueOfBool(true), nil, nil 52 case "false": 53 return protoreflect.ValueOfBool(false), nil, nil 54 } 55 } 56 case protoreflect.EnumKind: 57 if f == GoTag { 58 // Go tags use the numeric form of the enum value. 59 if n, err := strconv.ParseInt(s, 10, 32); err == nil { 60 if ev := evs.ByNumber(protoreflect.EnumNumber(n)); ev != nil { 61 return protoreflect.ValueOfEnum(ev.Number()), ev, nil 62 } 63 } 64 } else { 65 // Descriptor default_value use the enum identifier. 66 ev := evs.ByName(protoreflect.Name(s)) 67 if ev != nil { 68 return protoreflect.ValueOfEnum(ev.Number()), ev, nil 69 } 70 } 71 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: 72 if v, err := strconv.ParseInt(s, 10, 32); err == nil { 73 return protoreflect.ValueOfInt32(int32(v)), nil, nil 74 } 75 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: 76 if v, err := strconv.ParseInt(s, 10, 64); err == nil { 77 return protoreflect.ValueOfInt64(int64(v)), nil, nil 78 } 79 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: 80 if v, err := strconv.ParseUint(s, 10, 32); err == nil { 81 return protoreflect.ValueOfUint32(uint32(v)), nil, nil 82 } 83 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: 84 if v, err := strconv.ParseUint(s, 10, 64); err == nil { 85 return protoreflect.ValueOfUint64(uint64(v)), nil, nil 86 } 87 case protoreflect.FloatKind, protoreflect.DoubleKind: 88 var v float64 89 var err error 90 switch s { 91 case "-inf": 92 v = math.Inf(-1) 93 case "inf": 94 v = math.Inf(+1) 95 case "nan": 96 v = math.NaN() 97 default: 98 v, err = strconv.ParseFloat(s, 64) 99 } 100 if err == nil { 101 if k == protoreflect.FloatKind { 102 return protoreflect.ValueOfFloat32(float32(v)), nil, nil 103 } else { 104 return protoreflect.ValueOfFloat64(float64(v)), nil, nil 105 } 106 } 107 case protoreflect.StringKind: 108 // String values are already unescaped and can be used as is. 109 return protoreflect.ValueOfString(s), nil, nil 110 case protoreflect.BytesKind: 111 if b, ok := unmarshalBytes(s); ok { 112 return protoreflect.ValueOfBytes(b), nil, nil 113 } 114 } 115 return protoreflect.Value{}, nil, errors.New("could not parse value for %v: %q", k, s) 116 } 117 118 // Marshal serializes v as the default string according to the given kind k. 119 // When specifying the Descriptor format for an enum kind, the associated 120 // enum value descriptor must be provided. 121 func Marshal(v protoreflect.Value, ev protoreflect.EnumValueDescriptor, k protoreflect.Kind, f Format) (string, error) { 122 switch k { 123 case protoreflect.BoolKind: 124 if f == GoTag { 125 if v.Bool() { 126 return "1", nil 127 } else { 128 return "0", nil 129 } 130 } else { 131 if v.Bool() { 132 return "true", nil 133 } else { 134 return "false", nil 135 } 136 } 137 case protoreflect.EnumKind: 138 if f == GoTag { 139 return strconv.FormatInt(int64(v.Enum()), 10), nil 140 } else { 141 return string(ev.Name()), nil 142 } 143 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: 144 return strconv.FormatInt(v.Int(), 10), nil 145 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: 146 return strconv.FormatUint(v.Uint(), 10), nil 147 case protoreflect.FloatKind, protoreflect.DoubleKind: 148 f := v.Float() 149 switch { 150 case math.IsInf(f, -1): 151 return "-inf", nil 152 case math.IsInf(f, +1): 153 return "inf", nil 154 case math.IsNaN(f): 155 return "nan", nil 156 default: 157 if k == protoreflect.FloatKind { 158 return strconv.FormatFloat(f, 'g', -1, 32), nil 159 } else { 160 return strconv.FormatFloat(f, 'g', -1, 64), nil 161 } 162 } 163 case protoreflect.StringKind: 164 // String values are serialized as is without any escaping. 165 return v.String(), nil 166 case protoreflect.BytesKind: 167 if s, ok := marshalBytes(v.Bytes()); ok { 168 return s, nil 169 } 170 } 171 return "", errors.New("could not format value for %v: %v", k, v) 172 } 173 174 // unmarshalBytes deserializes bytes by applying C unescaping. 175 func unmarshalBytes(s string) ([]byte, bool) { 176 // Bytes values use the same escaping as the text format, 177 // however they lack the surrounding double quotes. 178 v, err := ptext.UnmarshalString(`"` + s + `"`) 179 if err != nil { 180 return nil, false 181 } 182 return []byte(v), true 183 } 184 185 // marshalBytes serializes bytes by using C escaping. 186 // To match the exact output of protoc, this is identical to the 187 // CEscape function in strutil.cc of the protoc source code. 188 func marshalBytes(b []byte) (string, bool) { 189 var s []byte 190 for _, c := range b { 191 switch c { 192 case '\n': 193 s = append(s, `\n`...) 194 case '\r': 195 s = append(s, `\r`...) 196 case '\t': 197 s = append(s, `\t`...) 198 case '"': 199 s = append(s, `\"`...) 200 case '\'': 201 s = append(s, `\'`...) 202 case '\\': 203 s = append(s, `\\`...) 204 default: 205 if printableASCII := c >= 0x20 && c <= 0x7e; printableASCII { 206 s = append(s, c) 207 } else { 208 s = append(s, fmt.Sprintf(`\%03o`, c)...) 209 } 210 } 211 } 212 return string(s), true 213 }