default.go raw

   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  }
 214