value_union.go

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