blob: 5a34147241939a8f4f3a39182a44c4401d88f4d8 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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
5package protoreflect
6
7import (
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.
44type 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.
62func 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.
96func 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.
105func ValueOfInt32(v int32) Value {
106 return Value{typ: int32Type, num: uint64(v)}
107}
108
109// ValueOfInt64 returns a new int64 value.
110func ValueOfInt64(v int64) Value {
111 return Value{typ: int64Type, num: uint64(v)}
112}
113
114// ValueOfUint32 returns a new uint32 value.
115func ValueOfUint32(v uint32) Value {
116 return Value{typ: uint32Type, num: uint64(v)}
117}
118
119// ValueOfUint64 returns a new uint64 value.
120func ValueOfUint64(v uint64) Value {
121 return Value{typ: uint64Type, num: v}
122}
123
124// ValueOfFloat32 returns a new float32 value.
125func ValueOfFloat32(v float32) Value {
126 return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
127}
128
129// ValueOfFloat64 returns a new float64 value.
130func ValueOfFloat64(v float64) Value {
131 return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
132}
133
134// ValueOfString returns a new string value.
135func ValueOfString(v string) Value {
136 return valueOfString(v)
137}
138
139// ValueOfBytes returns a new bytes value.
140func ValueOfBytes(v []byte) Value {
141 return valueOfBytes(v[:len(v):len(v)])
142}
143
144// ValueOfEnum returns a new enum value.
145func ValueOfEnum(v EnumNumber) Value {
146 return Value{typ: enumType, num: uint64(v)}
147}
148
149// ValueOfMessage returns a new Message value.
150func ValueOfMessage(v Message) Value {
151 return valueOfIface(v)
152}
153
154// ValueOfList returns a new List value.
155func ValueOfList(v List) Value {
156 return valueOfIface(v)
157}
158
159// ValueOfMap returns a new Map value.
160func ValueOfMap(v Map) Value {
161 return valueOfIface(v)
162}
163
164// IsValid reports whether v is populated with a value.
165func (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()
172func (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
201func (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
239func (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.
244func (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.
254func (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.
264func (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.
274func (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.
285func (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.
295func (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.
305func (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.
315func (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.
325func (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.
335func (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.
345func (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.
375type MapKey value
376
377// IsValid reports whether k is populated with a value.
378func (k MapKey) IsValid() bool {
379 return Value(k).IsValid()
380}
381
382// Interface returns k as an interface{}.
383func (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.
388func (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.
393func (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.
398func (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.
404func (k MapKey) String() string {
405 return Value(k).String()
406}
407
408// Value returns k as a Value.
409func (k MapKey) Value() Value {
410 return Value(k)
411}