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