|
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 protoreflect |
|
6 |
|
7 import ( |
|
8 "fmt" |
|
9 "math" |
|
10 ) |
|
11 |
|
12 // Value is a union where only one Go type may be set at a time. |
|
13 // The Value is used to represent all possible values a field may take. |
|
14 // The following shows which Go type is used to represent each proto Kind: |
|
15 // |
|
16 // ╔════════════╤═════════════════════════════════════╗ |
|
17 // ║ Go type │ Protobuf kind ║ |
|
18 // ╠════════════╪═════════════════════════════════════╣ |
|
19 // ║ bool │ BoolKind ║ |
|
20 // ║ int32 │ Int32Kind, Sint32Kind, Sfixed32Kind ║ |
|
21 // ║ int64 │ Int64Kind, Sint64Kind, Sfixed64Kind ║ |
|
22 // ║ uint32 │ Uint32Kind, Fixed32Kind ║ |
|
23 // ║ uint64 │ Uint64Kind, Fixed64Kind ║ |
|
24 // ║ float32 │ FloatKind ║ |
|
25 // ║ float64 │ DoubleKind ║ |
|
26 // ║ string │ StringKind ║ |
|
27 // ║ []byte │ BytesKind ║ |
|
28 // ║ EnumNumber │ EnumKind ║ |
|
29 // ║ Message │ MessageKind, GroupKind ║ |
|
30 // ╚════════════╧═════════════════════════════════════╝ |
|
31 // |
|
32 // Multiple protobuf Kinds may be represented by a single Go type if the type |
|
33 // can losslessly represent the information for the proto kind. For example, |
|
34 // Int64Kind, Sint64Kind, and Sfixed64Kind are all represented by int64, |
|
35 // but use different integer encoding methods. |
|
36 // |
|
37 // The List or Map types are used if the field cardinality is repeated. |
|
38 // A field is a List if FieldDescriptor.IsList reports true. |
|
39 // A field is a Map if FieldDescriptor.IsMap reports true. |
|
40 // |
|
41 // Converting to/from a Value and a concrete Go value panics on type mismatch. |
|
42 // For example, ValueOf("hello").Int() panics because this attempts to |
|
43 // retrieve an int64 from a string. |
|
44 type Value value |
|
45 |
|
46 // The protoreflect API uses a custom Value union type instead of interface{} |
|
47 // to keep the future open for performance optimizations. Using an interface{} |
|
48 // always incurs an allocation for primitives (e.g., int64) since it needs to |
|
49 // be boxed on the heap (as interfaces can only contain pointers natively). |
|
50 // Instead, we represent the Value union as a flat struct that internally keeps |
|
51 // track of which type is set. Using unsafe, the Value union can be reduced |
|
52 // down to 24B, which is identical in size to a slice. |
|
53 // |
|
54 // The latest compiler (Go1.11) currently suffers from some limitations: |
|
55 // • With inlining, the compiler should be able to statically prove that |
|
56 // only one of these switch cases are taken and inline one specific case. |
|
57 // See https://golang.org/issue/22310. |
|
58 |
|
59 // ValueOf returns a Value initialized with the concrete value stored in v. |
|
60 // This panics if the type does not match one of the allowed types in the |
|
61 // Value union. |
|
62 func ValueOf(v interface{}) Value { |
|
63 switch v := v.(type) { |
|
64 case nil: |
|
65 return Value{} |
|
66 case bool: |
|
67 return ValueOfBool(v) |
|
68 case int32: |
|
69 return ValueOfInt32(v) |
|
70 case int64: |
|
71 return ValueOfInt64(v) |
|
72 case uint32: |
|
73 return ValueOfUint32(v) |
|
74 case uint64: |
|
75 return ValueOfUint64(v) |
|
76 case float32: |
|
77 return ValueOfFloat32(v) |
|
78 case float64: |
|
79 return ValueOfFloat64(v) |
|
80 case string: |
|
81 return ValueOfString(v) |
|
82 case []byte: |
|
83 return ValueOfBytes(v) |
|
84 case EnumNumber: |
|
85 return ValueOfEnum(v) |
|
86 case Message, List, Map: |
|
87 return valueOfIface(v) |
|
88 case ProtoMessage: |
|
89 panic(fmt.Sprintf("invalid proto.Message(%T) type, expected a protoreflect.Message type", v)) |
|
90 default: |
|
91 panic(fmt.Sprintf("invalid type: %T", v)) |
|
92 } |
|
93 } |
|
94 |
|
95 // ValueOfBool returns a new boolean value. |
|
96 func ValueOfBool(v bool) Value { |
|
97 if v { |
|
98 return Value{typ: boolType, num: 1} |
|
99 } else { |
|
100 return Value{typ: boolType, num: 0} |
|
101 } |
|
102 } |
|
103 |
|
104 // ValueOfInt32 returns a new int32 value. |
|
105 func ValueOfInt32(v int32) Value { |
|
106 return Value{typ: int32Type, num: uint64(v)} |
|
107 } |
|
108 |
|
109 // ValueOfInt64 returns a new int64 value. |
|
110 func ValueOfInt64(v int64) Value { |
|
111 return Value{typ: int64Type, num: uint64(v)} |
|
112 } |
|
113 |
|
114 // ValueOfUint32 returns a new uint32 value. |
|
115 func ValueOfUint32(v uint32) Value { |
|
116 return Value{typ: uint32Type, num: uint64(v)} |
|
117 } |
|
118 |
|
119 // ValueOfUint64 returns a new uint64 value. |
|
120 func ValueOfUint64(v uint64) Value { |
|
121 return Value{typ: uint64Type, num: v} |
|
122 } |
|
123 |
|
124 // ValueOfFloat32 returns a new float32 value. |
|
125 func ValueOfFloat32(v float32) Value { |
|
126 return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))} |
|
127 } |
|
128 |
|
129 // ValueOfFloat64 returns a new float64 value. |
|
130 func ValueOfFloat64(v float64) Value { |
|
131 return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))} |
|
132 } |
|
133 |
|
134 // ValueOfString returns a new string value. |
|
135 func ValueOfString(v string) Value { |
|
136 return valueOfString(v) |
|
137 } |
|
138 |
|
139 // ValueOfBytes returns a new bytes value. |
|
140 func ValueOfBytes(v []byte) Value { |
|
141 return valueOfBytes(v[:len(v):len(v)]) |
|
142 } |
|
143 |
|
144 // ValueOfEnum returns a new enum value. |
|
145 func ValueOfEnum(v EnumNumber) Value { |
|
146 return Value{typ: enumType, num: uint64(v)} |
|
147 } |
|
148 |
|
149 // ValueOfMessage returns a new Message value. |
|
150 func ValueOfMessage(v Message) Value { |
|
151 return valueOfIface(v) |
|
152 } |
|
153 |
|
154 // ValueOfList returns a new List value. |
|
155 func ValueOfList(v List) Value { |
|
156 return valueOfIface(v) |
|
157 } |
|
158 |
|
159 // ValueOfMap returns a new Map value. |
|
160 func ValueOfMap(v Map) Value { |
|
161 return valueOfIface(v) |
|
162 } |
|
163 |
|
164 // IsValid reports whether v is populated with a value. |
|
165 func (v Value) IsValid() bool { |
|
166 return v.typ != nilType |
|
167 } |
|
168 |
|
169 // Interface returns v as an interface{}. |
|
170 // |
|
171 // Invariant: v == ValueOf(v).Interface() |
|
172 func (v Value) Interface() interface{} { |
|
173 switch v.typ { |
|
174 case nilType: |
|
175 return nil |
|
176 case boolType: |
|
177 return v.Bool() |
|
178 case int32Type: |
|
179 return int32(v.Int()) |
|
180 case int64Type: |
|
181 return int64(v.Int()) |
|
182 case uint32Type: |
|
183 return uint32(v.Uint()) |
|
184 case uint64Type: |
|
185 return uint64(v.Uint()) |
|
186 case float32Type: |
|
187 return float32(v.Float()) |
|
188 case float64Type: |
|
189 return float64(v.Float()) |
|
190 case stringType: |
|
191 return v.String() |
|
192 case bytesType: |
|
193 return v.Bytes() |
|
194 case enumType: |
|
195 return v.Enum() |
|
196 default: |
|
197 return v.getIface() |
|
198 } |
|
199 } |
|
200 |
|
201 func (v Value) typeName() string { |
|
202 switch v.typ { |
|
203 case nilType: |
|
204 return "nil" |
|
205 case boolType: |
|
206 return "bool" |
|
207 case int32Type: |
|
208 return "int32" |
|
209 case int64Type: |
|
210 return "int64" |
|
211 case uint32Type: |
|
212 return "uint32" |
|
213 case uint64Type: |
|
214 return "uint64" |
|
215 case float32Type: |
|
216 return "float32" |
|
217 case float64Type: |
|
218 return "float64" |
|
219 case stringType: |
|
220 return "string" |
|
221 case bytesType: |
|
222 return "bytes" |
|
223 case enumType: |
|
224 return "enum" |
|
225 default: |
|
226 switch v := v.getIface().(type) { |
|
227 case Message: |
|
228 return "message" |
|
229 case List: |
|
230 return "list" |
|
231 case Map: |
|
232 return "map" |
|
233 default: |
|
234 return fmt.Sprintf("<unknown: %T>", v) |
|
235 } |
|
236 } |
|
237 } |
|
238 |
|
239 func (v Value) panicMessage(what string) string { |
|
240 return fmt.Sprintf("type mismatch: cannot convert %v to %s", v.typeName(), what) |
|
241 } |
|
242 |
|
243 // Bool returns v as a bool and panics if the type is not a bool. |
|
244 func (v Value) Bool() bool { |
|
245 switch v.typ { |
|
246 case boolType: |
|
247 return v.num > 0 |
|
248 default: |
|
249 panic(v.panicMessage("bool")) |
|
250 } |
|
251 } |
|
252 |
|
253 // Int returns v as a int64 and panics if the type is not a int32 or int64. |
|
254 func (v Value) Int() int64 { |
|
255 switch v.typ { |
|
256 case int32Type, int64Type: |
|
257 return int64(v.num) |
|
258 default: |
|
259 panic(v.panicMessage("int")) |
|
260 } |
|
261 } |
|
262 |
|
263 // Uint returns v as a uint64 and panics if the type is not a uint32 or uint64. |
|
264 func (v Value) Uint() uint64 { |
|
265 switch v.typ { |
|
266 case uint32Type, uint64Type: |
|
267 return uint64(v.num) |
|
268 default: |
|
269 panic(v.panicMessage("uint")) |
|
270 } |
|
271 } |
|
272 |
|
273 // Float returns v as a float64 and panics if the type is not a float32 or float64. |
|
274 func (v Value) Float() float64 { |
|
275 switch v.typ { |
|
276 case float32Type, float64Type: |
|
277 return math.Float64frombits(uint64(v.num)) |
|
278 default: |
|
279 panic(v.panicMessage("float")) |
|
280 } |
|
281 } |
|
282 |
|
283 // String returns v as a string. Since this method implements fmt.Stringer, |
|
284 // this returns the formatted string value for any non-string type. |
|
285 func (v Value) String() string { |
|
286 switch v.typ { |
|
287 case stringType: |
|
288 return v.getString() |
|
289 default: |
|
290 return fmt.Sprint(v.Interface()) |
|
291 } |
|
292 } |
|
293 |
|
294 // Bytes returns v as a []byte and panics if the type is not a []byte. |
|
295 func (v Value) Bytes() []byte { |
|
296 switch v.typ { |
|
297 case bytesType: |
|
298 return v.getBytes() |
|
299 default: |
|
300 panic(v.panicMessage("bytes")) |
|
301 } |
|
302 } |
|
303 |
|
304 // Enum returns v as a EnumNumber and panics if the type is not a EnumNumber. |
|
305 func (v Value) Enum() EnumNumber { |
|
306 switch v.typ { |
|
307 case enumType: |
|
308 return EnumNumber(v.num) |
|
309 default: |
|
310 panic(v.panicMessage("enum")) |
|
311 } |
|
312 } |
|
313 |
|
314 // Message returns v as a Message and panics if the type is not a Message. |
|
315 func (v Value) Message() Message { |
|
316 switch vi := v.getIface().(type) { |
|
317 case Message: |
|
318 return vi |
|
319 default: |
|
320 panic(v.panicMessage("message")) |
|
321 } |
|
322 } |
|
323 |
|
324 // List returns v as a List and panics if the type is not a List. |
|
325 func (v Value) List() List { |
|
326 switch vi := v.getIface().(type) { |
|
327 case List: |
|
328 return vi |
|
329 default: |
|
330 panic(v.panicMessage("list")) |
|
331 } |
|
332 } |
|
333 |
|
334 // Map returns v as a Map and panics if the type is not a Map. |
|
335 func (v Value) Map() Map { |
|
336 switch vi := v.getIface().(type) { |
|
337 case Map: |
|
338 return vi |
|
339 default: |
|
340 panic(v.panicMessage("map")) |
|
341 } |
|
342 } |
|
343 |
|
344 // MapKey returns v as a MapKey and panics for invalid MapKey types. |
|
345 func (v Value) MapKey() MapKey { |
|
346 switch v.typ { |
|
347 case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType: |
|
348 return MapKey(v) |
|
349 default: |
|
350 panic(v.panicMessage("map key")) |
|
351 } |
|
352 } |
|
353 |
|
354 // MapKey is used to index maps, where the Go type of the MapKey must match |
|
355 // the specified key Kind (see MessageDescriptor.IsMapEntry). |
|
356 // The following shows what Go type is used to represent each proto Kind: |
|
357 // |
|
358 // ╔═════════╤═════════════════════════════════════╗ |
|
359 // ║ Go type │ Protobuf kind ║ |
|
360 // ╠═════════╪═════════════════════════════════════╣ |
|
361 // ║ bool │ BoolKind ║ |
|
362 // ║ int32 │ Int32Kind, Sint32Kind, Sfixed32Kind ║ |
|
363 // ║ int64 │ Int64Kind, Sint64Kind, Sfixed64Kind ║ |
|
364 // ║ uint32 │ Uint32Kind, Fixed32Kind ║ |
|
365 // ║ uint64 │ Uint64Kind, Fixed64Kind ║ |
|
366 // ║ string │ StringKind ║ |
|
367 // ╚═════════╧═════════════════════════════════════╝ |
|
368 // |
|
369 // A MapKey is constructed and accessed through a Value: |
|
370 // k := ValueOf("hash").MapKey() // convert string to MapKey |
|
371 // s := k.String() // convert MapKey to string |
|
372 // |
|
373 // The MapKey is a strict subset of valid types used in Value; |
|
374 // converting a Value to a MapKey with an invalid type panics. |
|
375 type MapKey value |
|
376 |
|
377 // IsValid reports whether k is populated with a value. |
|
378 func (k MapKey) IsValid() bool { |
|
379 return Value(k).IsValid() |
|
380 } |
|
381 |
|
382 // Interface returns k as an interface{}. |
|
383 func (k MapKey) Interface() interface{} { |
|
384 return Value(k).Interface() |
|
385 } |
|
386 |
|
387 // Bool returns k as a bool and panics if the type is not a bool. |
|
388 func (k MapKey) Bool() bool { |
|
389 return Value(k).Bool() |
|
390 } |
|
391 |
|
392 // Int returns k as a int64 and panics if the type is not a int32 or int64. |
|
393 func (k MapKey) Int() int64 { |
|
394 return Value(k).Int() |
|
395 } |
|
396 |
|
397 // Uint returns k as a uint64 and panics if the type is not a uint32 or uint64. |
|
398 func (k MapKey) Uint() uint64 { |
|
399 return Value(k).Uint() |
|
400 } |
|
401 |
|
402 // String returns k as a string. Since this method implements fmt.Stringer, |
|
403 // this returns the formatted string value for any non-string type. |
|
404 func (k MapKey) String() string { |
|
405 return Value(k).String() |
|
406 } |
|
407 |
|
408 // Value returns k as a Value. |
|
409 func (k MapKey) Value() Value { |
|
410 return Value(k) |
|
411 } |