256
|
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" |
260
|
18 |
"google.golang.org/protobuf/internal/errors" |
|
19 |
"google.golang.org/protobuf/reflect/protoreflect" |
256
|
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. |
260
|
38 |
func Unmarshal(s string, k protoreflect.Kind, evs protoreflect.EnumValueDescriptors, f Format) (protoreflect.Value, protoreflect.EnumValueDescriptor, error) { |
256
|
39 |
switch k { |
260
|
40 |
case protoreflect.BoolKind: |
256
|
41 |
if f == GoTag { |
|
42 |
switch s { |
|
43 |
case "1": |
260
|
44 |
return protoreflect.ValueOfBool(true), nil, nil |
256
|
45 |
case "0": |
260
|
46 |
return protoreflect.ValueOfBool(false), nil, nil |
256
|
47 |
} |
|
48 |
} else { |
|
49 |
switch s { |
|
50 |
case "true": |
260
|
51 |
return protoreflect.ValueOfBool(true), nil, nil |
256
|
52 |
case "false": |
260
|
53 |
return protoreflect.ValueOfBool(false), nil, nil |
256
|
54 |
} |
|
55 |
} |
260
|
56 |
case protoreflect.EnumKind: |
256
|
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 { |
260
|
60 |
if ev := evs.ByNumber(protoreflect.EnumNumber(n)); ev != nil { |
|
61 |
return protoreflect.ValueOfEnum(ev.Number()), ev, nil |
256
|
62 |
} |
|
63 |
} |
|
64 |
} else { |
|
65 |
// Descriptor default_value use the enum identifier. |
260
|
66 |
ev := evs.ByName(protoreflect.Name(s)) |
256
|
67 |
if ev != nil { |
260
|
68 |
return protoreflect.ValueOfEnum(ev.Number()), ev, nil |
256
|
69 |
} |
|
70 |
} |
260
|
71 |
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: |
256
|
72 |
if v, err := strconv.ParseInt(s, 10, 32); err == nil { |
260
|
73 |
return protoreflect.ValueOfInt32(int32(v)), nil, nil |
256
|
74 |
} |
260
|
75 |
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
256
|
76 |
if v, err := strconv.ParseInt(s, 10, 64); err == nil { |
260
|
77 |
return protoreflect.ValueOfInt64(int64(v)), nil, nil |
256
|
78 |
} |
260
|
79 |
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: |
256
|
80 |
if v, err := strconv.ParseUint(s, 10, 32); err == nil { |
260
|
81 |
return protoreflect.ValueOfUint32(uint32(v)), nil, nil |
256
|
82 |
} |
260
|
83 |
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
256
|
84 |
if v, err := strconv.ParseUint(s, 10, 64); err == nil { |
260
|
85 |
return protoreflect.ValueOfUint64(uint64(v)), nil, nil |
256
|
86 |
} |
260
|
87 |
case protoreflect.FloatKind, protoreflect.DoubleKind: |
256
|
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 { |
260
|
101 |
if k == protoreflect.FloatKind { |
|
102 |
return protoreflect.ValueOfFloat32(float32(v)), nil, nil |
256
|
103 |
} else { |
260
|
104 |
return protoreflect.ValueOfFloat64(float64(v)), nil, nil |
256
|
105 |
} |
|
106 |
} |
260
|
107 |
case protoreflect.StringKind: |
256
|
108 |
// String values are already unescaped and can be used as is. |
260
|
109 |
return protoreflect.ValueOfString(s), nil, nil |
|
110 |
case protoreflect.BytesKind: |
256
|
111 |
if b, ok := unmarshalBytes(s); ok { |
260
|
112 |
return protoreflect.ValueOfBytes(b), nil, nil |
256
|
113 |
} |
|
114 |
} |
260
|
115 |
return protoreflect.Value{}, nil, errors.New("could not parse value for %v: %q", k, s) |
256
|
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. |
260
|
121 |
func Marshal(v protoreflect.Value, ev protoreflect.EnumValueDescriptor, k protoreflect.Kind, f Format) (string, error) { |
256
|
122 |
switch k { |
260
|
123 |
case protoreflect.BoolKind: |
256
|
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 |
} |
260
|
137 |
case protoreflect.EnumKind: |
256
|
138 |
if f == GoTag { |
|
139 |
return strconv.FormatInt(int64(v.Enum()), 10), nil |
|
140 |
} else { |
|
141 |
return string(ev.Name()), nil |
|
142 |
} |
260
|
143 |
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
256
|
144 |
return strconv.FormatInt(v.Int(), 10), nil |
260
|
145 |
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
256
|
146 |
return strconv.FormatUint(v.Uint(), 10), nil |
260
|
147 |
case protoreflect.FloatKind, protoreflect.DoubleKind: |
256
|
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: |
260
|
157 |
if k == protoreflect.FloatKind { |
256
|
158 |
return strconv.FormatFloat(f, 'g', -1, 32), nil |
|
159 |
} else { |
|
160 |
return strconv.FormatFloat(f, 'g', -1, 64), nil |
|
161 |
} |
|
162 |
} |
260
|
163 |
case protoreflect.StringKind: |
256
|
164 |
// String values are serialized as is without any escaping. |
|
165 |
return v.String(), nil |
260
|
166 |
case protoreflect.BytesKind: |
256
|
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 |
} |