blob: f334f71bcb438afd1b6f3784d3256f64272d9137 [file] [log] [blame]
Andrea Campanella3614a922021-02-25 12:40:42 +01001// 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 default:
89 panic(fmt.Sprintf("invalid type: %T", v))
90 }
91}
92
93// ValueOfBool returns a new boolean value.
94func ValueOfBool(v bool) Value {
95 if v {
96 return Value{typ: boolType, num: 1}
97 } else {
98 return Value{typ: boolType, num: 0}
99 }
100}
101
102// ValueOfInt32 returns a new int32 value.
103func ValueOfInt32(v int32) Value {
104 return Value{typ: int32Type, num: uint64(v)}
105}
106
107// ValueOfInt64 returns a new int64 value.
108func ValueOfInt64(v int64) Value {
109 return Value{typ: int64Type, num: uint64(v)}
110}
111
112// ValueOfUint32 returns a new uint32 value.
113func ValueOfUint32(v uint32) Value {
114 return Value{typ: uint32Type, num: uint64(v)}
115}
116
117// ValueOfUint64 returns a new uint64 value.
118func ValueOfUint64(v uint64) Value {
119 return Value{typ: uint64Type, num: v}
120}
121
122// ValueOfFloat32 returns a new float32 value.
123func ValueOfFloat32(v float32) Value {
124 return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
125}
126
127// ValueOfFloat64 returns a new float64 value.
128func ValueOfFloat64(v float64) Value {
129 return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
130}
131
132// ValueOfString returns a new string value.
133func ValueOfString(v string) Value {
134 return valueOfString(v)
135}
136
137// ValueOfBytes returns a new bytes value.
138func ValueOfBytes(v []byte) Value {
139 return valueOfBytes(v[:len(v):len(v)])
140}
141
142// ValueOfEnum returns a new enum value.
143func ValueOfEnum(v EnumNumber) Value {
144 return Value{typ: enumType, num: uint64(v)}
145}
146
147// ValueOfMessage returns a new Message value.
148func ValueOfMessage(v Message) Value {
149 return valueOfIface(v)
150}
151
152// ValueOfList returns a new List value.
153func ValueOfList(v List) Value {
154 return valueOfIface(v)
155}
156
157// ValueOfMap returns a new Map value.
158func ValueOfMap(v Map) Value {
159 return valueOfIface(v)
160}
161
162// IsValid reports whether v is populated with a value.
163func (v Value) IsValid() bool {
164 return v.typ != nilType
165}
166
167// Interface returns v as an interface{}.
168//
169// Invariant: v == ValueOf(v).Interface()
170func (v Value) Interface() interface{} {
171 switch v.typ {
172 case nilType:
173 return nil
174 case boolType:
175 return v.Bool()
176 case int32Type:
177 return int32(v.Int())
178 case int64Type:
179 return int64(v.Int())
180 case uint32Type:
181 return uint32(v.Uint())
182 case uint64Type:
183 return uint64(v.Uint())
184 case float32Type:
185 return float32(v.Float())
186 case float64Type:
187 return float64(v.Float())
188 case stringType:
189 return v.String()
190 case bytesType:
191 return v.Bytes()
192 case enumType:
193 return v.Enum()
194 default:
195 return v.getIface()
196 }
197}
198
199func (v Value) typeName() string {
200 switch v.typ {
201 case nilType:
202 return "nil"
203 case boolType:
204 return "bool"
205 case int32Type:
206 return "int32"
207 case int64Type:
208 return "int64"
209 case uint32Type:
210 return "uint32"
211 case uint64Type:
212 return "uint64"
213 case float32Type:
214 return "float32"
215 case float64Type:
216 return "float64"
217 case stringType:
218 return "string"
219 case bytesType:
220 return "bytes"
221 case enumType:
222 return "enum"
223 default:
224 switch v := v.getIface().(type) {
225 case Message:
226 return "message"
227 case List:
228 return "list"
229 case Map:
230 return "map"
231 default:
232 return fmt.Sprintf("<unknown: %T>", v)
233 }
234 }
235}
236
237func (v Value) panicMessage(what string) string {
238 return fmt.Sprintf("type mismatch: cannot convert %v to %s", v.typeName(), what)
239}
240
241// Bool returns v as a bool and panics if the type is not a bool.
242func (v Value) Bool() bool {
243 switch v.typ {
244 case boolType:
245 return v.num > 0
246 default:
247 panic(v.panicMessage("bool"))
248 }
249}
250
251// Int returns v as a int64 and panics if the type is not a int32 or int64.
252func (v Value) Int() int64 {
253 switch v.typ {
254 case int32Type, int64Type:
255 return int64(v.num)
256 default:
257 panic(v.panicMessage("int"))
258 }
259}
260
261// Uint returns v as a uint64 and panics if the type is not a uint32 or uint64.
262func (v Value) Uint() uint64 {
263 switch v.typ {
264 case uint32Type, uint64Type:
265 return uint64(v.num)
266 default:
267 panic(v.panicMessage("uint"))
268 }
269}
270
271// Float returns v as a float64 and panics if the type is not a float32 or float64.
272func (v Value) Float() float64 {
273 switch v.typ {
274 case float32Type, float64Type:
275 return math.Float64frombits(uint64(v.num))
276 default:
277 panic(v.panicMessage("float"))
278 }
279}
280
281// String returns v as a string. Since this method implements fmt.Stringer,
282// this returns the formatted string value for any non-string type.
283func (v Value) String() string {
284 switch v.typ {
285 case stringType:
286 return v.getString()
287 default:
288 return fmt.Sprint(v.Interface())
289 }
290}
291
292// Bytes returns v as a []byte and panics if the type is not a []byte.
293func (v Value) Bytes() []byte {
294 switch v.typ {
295 case bytesType:
296 return v.getBytes()
297 default:
298 panic(v.panicMessage("bytes"))
299 }
300}
301
302// Enum returns v as a EnumNumber and panics if the type is not a EnumNumber.
303func (v Value) Enum() EnumNumber {
304 switch v.typ {
305 case enumType:
306 return EnumNumber(v.num)
307 default:
308 panic(v.panicMessage("enum"))
309 }
310}
311
312// Message returns v as a Message and panics if the type is not a Message.
313func (v Value) Message() Message {
314 switch vi := v.getIface().(type) {
315 case Message:
316 return vi
317 default:
318 panic(v.panicMessage("message"))
319 }
320}
321
322// List returns v as a List and panics if the type is not a List.
323func (v Value) List() List {
324 switch vi := v.getIface().(type) {
325 case List:
326 return vi
327 default:
328 panic(v.panicMessage("list"))
329 }
330}
331
332// Map returns v as a Map and panics if the type is not a Map.
333func (v Value) Map() Map {
334 switch vi := v.getIface().(type) {
335 case Map:
336 return vi
337 default:
338 panic(v.panicMessage("map"))
339 }
340}
341
342// MapKey returns v as a MapKey and panics for invalid MapKey types.
343func (v Value) MapKey() MapKey {
344 switch v.typ {
345 case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType:
346 return MapKey(v)
347 default:
348 panic(v.panicMessage("map key"))
349 }
350}
351
352// MapKey is used to index maps, where the Go type of the MapKey must match
353// the specified key Kind (see MessageDescriptor.IsMapEntry).
354// The following shows what Go type is used to represent each proto Kind:
355//
356// ╔═════════╤═════════════════════════════════════╗
357// ║ Go type │ Protobuf kind ║
358// ╠═════════╪═════════════════════════════════════╣
359// ║ bool │ BoolKind ║
360// ║ int32 │ Int32Kind, Sint32Kind, Sfixed32Kind ║
361// ║ int64 │ Int64Kind, Sint64Kind, Sfixed64Kind ║
362// ║ uint32 │ Uint32Kind, Fixed32Kind ║
363// ║ uint64 │ Uint64Kind, Fixed64Kind ║
364// ║ string │ StringKind ║
365// ╚═════════╧═════════════════════════════════════╝
366//
367// A MapKey is constructed and accessed through a Value:
368// k := ValueOf("hash").MapKey() // convert string to MapKey
369// s := k.String() // convert MapKey to string
370//
371// The MapKey is a strict subset of valid types used in Value;
372// converting a Value to a MapKey with an invalid type panics.
373type MapKey value
374
375// IsValid reports whether k is populated with a value.
376func (k MapKey) IsValid() bool {
377 return Value(k).IsValid()
378}
379
380// Interface returns k as an interface{}.
381func (k MapKey) Interface() interface{} {
382 return Value(k).Interface()
383}
384
385// Bool returns k as a bool and panics if the type is not a bool.
386func (k MapKey) Bool() bool {
387 return Value(k).Bool()
388}
389
390// Int returns k as a int64 and panics if the type is not a int32 or int64.
391func (k MapKey) Int() int64 {
392 return Value(k).Int()
393}
394
395// Uint returns k as a uint64 and panics if the type is not a uint32 or uint64.
396func (k MapKey) Uint() uint64 {
397 return Value(k).Uint()
398}
399
400// String returns k as a string. Since this method implements fmt.Stringer,
401// this returns the formatted string value for any non-string type.
402func (k MapKey) String() string {
403 return Value(k).String()
404}
405
406// Value returns k as a Value.
407func (k MapKey) Value() Value {
408 return Value(k)
409}