value_union.go raw

   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  //
  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.
  70  type 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.
  88  func 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.
 122  func 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.
 131  func ValueOfInt32(v int32) Value {
 132  	return Value{typ: int32Type, num: uint64(v)}
 133  }
 134  
 135  // ValueOfInt64 returns a new int64 value.
 136  func ValueOfInt64(v int64) Value {
 137  	return Value{typ: int64Type, num: uint64(v)}
 138  }
 139  
 140  // ValueOfUint32 returns a new uint32 value.
 141  func ValueOfUint32(v uint32) Value {
 142  	return Value{typ: uint32Type, num: uint64(v)}
 143  }
 144  
 145  // ValueOfUint64 returns a new uint64 value.
 146  func ValueOfUint64(v uint64) Value {
 147  	return Value{typ: uint64Type, num: v}
 148  }
 149  
 150  // ValueOfFloat32 returns a new float32 value.
 151  func ValueOfFloat32(v float32) Value {
 152  	return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
 153  }
 154  
 155  // ValueOfFloat64 returns a new float64 value.
 156  func ValueOfFloat64(v float64) Value {
 157  	return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
 158  }
 159  
 160  // ValueOfString returns a new string value.
 161  func ValueOfString(v string) Value {
 162  	return valueOfString(v)
 163  }
 164  
 165  // ValueOfBytes returns a new bytes value.
 166  func ValueOfBytes(v []byte) Value {
 167  	return valueOfBytes(v[:len(v):len(v)])
 168  }
 169  
 170  // ValueOfEnum returns a new enum value.
 171  func ValueOfEnum(v EnumNumber) Value {
 172  	return Value{typ: enumType, num: uint64(v)}
 173  }
 174  
 175  // ValueOfMessage returns a new Message value.
 176  func ValueOfMessage(v Message) Value {
 177  	return valueOfIface(v)
 178  }
 179  
 180  // ValueOfList returns a new List value.
 181  func ValueOfList(v List) Value {
 182  	return valueOfIface(v)
 183  }
 184  
 185  // ValueOfMap returns a new Map value.
 186  func ValueOfMap(v Map) Value {
 187  	return valueOfIface(v)
 188  }
 189  
 190  // IsValid reports whether v is populated with a value.
 191  func (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()
 198  func (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  
 227  func (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  
 265  func (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.
 270  func (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.
 280  func (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.
 290  func (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.
 300  func (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.
 311  func (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.
 321  func (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].
 331  func (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].
 341  func (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].
 351  func (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].
 361  func (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.
 371  func (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.
 402  type MapKey value
 403  
 404  // IsValid reports whether k is populated with a value.
 405  func (k MapKey) IsValid() bool {
 406  	return Value(k).IsValid()
 407  }
 408  
 409  // Interface returns k as an any.
 410  func (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.
 415  func (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.
 420  func (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.
 425  func (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.
 431  func (k MapKey) String() string {
 432  	return Value(k).String()
 433  }
 434  
 435  // Value returns k as a [Value].
 436  func (k MapKey) Value() Value {
 437  	return Value(k)
 438  }
 439