xref: /aosp_15_r20/external/golang-protobuf/internal/encoding/defval/default.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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}
214