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