v1.38.0 is used by Kubernetes since https://github.com/kubernetes/kubernetes/pull/100488. Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
		
			
				
	
	
		
			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
 | 
						|
}
 |