214 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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.
 | |
| 	v, err := ptext.UnmarshalString(`"` + s + `"`)
 | |
| 	if err != nil {
 | |
| 		return nil, false
 | |
| 	}
 | |
| 	return []byte(v), true
 | |
| }
 | |
| 
 | |
| // 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
 | |
| }
 |