amit.ghosh | 258d14c | 2020-10-02 15:13:38 +0200 | [diff] [blame] | 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 | type 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. |
| 62 | func 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. |
| 94 | func 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. |
| 103 | func ValueOfInt32(v int32) Value { |
| 104 | return Value{typ: int32Type, num: uint64(v)} |
| 105 | } |
| 106 | |
| 107 | // ValueOfInt64 returns a new int64 value. |
| 108 | func ValueOfInt64(v int64) Value { |
| 109 | return Value{typ: int64Type, num: uint64(v)} |
| 110 | } |
| 111 | |
| 112 | // ValueOfUint32 returns a new uint32 value. |
| 113 | func ValueOfUint32(v uint32) Value { |
| 114 | return Value{typ: uint32Type, num: uint64(v)} |
| 115 | } |
| 116 | |
| 117 | // ValueOfUint64 returns a new uint64 value. |
| 118 | func ValueOfUint64(v uint64) Value { |
| 119 | return Value{typ: uint64Type, num: v} |
| 120 | } |
| 121 | |
| 122 | // ValueOfFloat32 returns a new float32 value. |
| 123 | func ValueOfFloat32(v float32) Value { |
| 124 | return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))} |
| 125 | } |
| 126 | |
| 127 | // ValueOfFloat64 returns a new float64 value. |
| 128 | func ValueOfFloat64(v float64) Value { |
| 129 | return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))} |
| 130 | } |
| 131 | |
| 132 | // ValueOfString returns a new string value. |
| 133 | func ValueOfString(v string) Value { |
| 134 | return valueOfString(v) |
| 135 | } |
| 136 | |
| 137 | // ValueOfBytes returns a new bytes value. |
| 138 | func ValueOfBytes(v []byte) Value { |
| 139 | return valueOfBytes(v[:len(v):len(v)]) |
| 140 | } |
| 141 | |
| 142 | // ValueOfEnum returns a new enum value. |
| 143 | func ValueOfEnum(v EnumNumber) Value { |
| 144 | return Value{typ: enumType, num: uint64(v)} |
| 145 | } |
| 146 | |
| 147 | // ValueOfMessage returns a new Message value. |
| 148 | func ValueOfMessage(v Message) Value { |
| 149 | return valueOfIface(v) |
| 150 | } |
| 151 | |
| 152 | // ValueOfList returns a new List value. |
| 153 | func ValueOfList(v List) Value { |
| 154 | return valueOfIface(v) |
| 155 | } |
| 156 | |
| 157 | // ValueOfMap returns a new Map value. |
| 158 | func ValueOfMap(v Map) Value { |
| 159 | return valueOfIface(v) |
| 160 | } |
| 161 | |
| 162 | // IsValid reports whether v is populated with a value. |
| 163 | func (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() |
| 170 | func (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 | |
| 199 | func (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 | |
| 237 | func (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. |
| 242 | func (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. |
| 252 | func (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. |
| 262 | func (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. |
| 272 | func (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. |
| 283 | func (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. |
| 293 | func (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. |
| 303 | func (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. |
| 313 | func (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. |
| 323 | func (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. |
| 333 | func (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. |
| 343 | func (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. |
| 373 | type MapKey value |
| 374 | |
| 375 | // IsValid reports whether k is populated with a value. |
| 376 | func (k MapKey) IsValid() bool { |
| 377 | return Value(k).IsValid() |
| 378 | } |
| 379 | |
| 380 | // Interface returns k as an interface{}. |
| 381 | func (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. |
| 386 | func (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. |
| 391 | func (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. |
| 396 | func (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. |
| 402 | func (k MapKey) String() string { |
| 403 | return Value(k).String() |
| 404 | } |
| 405 | |
| 406 | // Value returns k as a Value. |
| 407 | func (k MapKey) Value() Value { |
| 408 | return Value(k) |
| 409 | } |