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