default.go

  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.
 10package defval
 11
 12import (
 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.
 23type Format int
 24
 25const (
 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.
 38func 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.
121func 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.
175func 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.
188func 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}