gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }