A game about forced loneliness, made by TACStudios
1using System; 2using System.Globalization; 3using System.Runtime.InteropServices; 4using Unity.Collections.LowLevel.Unsafe; 5 6////REVIEW: add Vector2 and Vector3 as primitive value types? 7 8namespace UnityEngine.InputSystem.Utilities 9{ 10 /// <summary> 11 /// A union holding a primitive value. 12 /// </summary> 13 /// <remarks> 14 /// This structure is used for storing things such as default states for controls 15 /// (see <see cref="Layouts.InputControlLayout.ControlItem.defaultState"/>). It can 16 /// store one value of any primitive, non-reference C# type (bool, char, int, float, etc). 17 /// </remarks> 18 [StructLayout(LayoutKind.Explicit)] 19 public struct PrimitiveValue : IEquatable<PrimitiveValue>, IConvertible 20 { 21 [FieldOffset(0)] private TypeCode m_Type; 22 [FieldOffset(4)] private bool m_BoolValue; 23 [FieldOffset(4)] private char m_CharValue; 24 [FieldOffset(4)] private byte m_ByteValue; 25 [FieldOffset(4)] private sbyte m_SByteValue; 26 [FieldOffset(4)] private short m_ShortValue; 27 [FieldOffset(4)] private ushort m_UShortValue; 28 [FieldOffset(4)] private int m_IntValue; 29 [FieldOffset(4)] private uint m_UIntValue; 30 [FieldOffset(4)] private long m_LongValue; 31 [FieldOffset(4)] private ulong m_ULongValue; 32 [FieldOffset(4)] private float m_FloatValue; 33 [FieldOffset(4)] private double m_DoubleValue; 34 35 internal unsafe byte* valuePtr => (byte*)UnsafeUtility.AddressOf(ref this) + 4; 36 37 /// <summary> 38 /// Type of value stored in the struct. <see cref="TypeCode.Empty"/> 39 /// if the struct does not hold a value (i.e. has been default-initialized). 40 /// </summary> 41 /// <value>Type of value stored in the struct.</value> 42 public TypeCode type => m_Type; 43 44 /// <summary> 45 /// If true, the struct does not contain a primitive value (i.e. has <see cref="type"/> 46 /// <see cref="TypeCode.Empty"/>). 47 /// </summary> 48 /// <value>Whether the struct is holding a value or not.</value> 49 public bool isEmpty => type == TypeCode.Empty; 50 51 /// <summary> 52 /// Create a PrimitiveValue holding a bool. 53 /// </summary> 54 /// <param name="value">A boolean value.</param> 55 public PrimitiveValue(bool value) 56 : this() 57 { 58 m_Type = TypeCode.Boolean; 59 m_BoolValue = value; 60 } 61 62 /// <summary> 63 /// Create a PrimitiveValue holding a character. 64 /// </summary> 65 /// <param name="value">A character.</param> 66 public PrimitiveValue(char value) 67 : this() 68 { 69 m_Type = TypeCode.Char; 70 m_CharValue = value; 71 } 72 73 /// <summary> 74 /// Create a PrimitiveValue holding a byte. 75 /// </summary> 76 /// <param name="value">A byte value.</param> 77 public PrimitiveValue(byte value) 78 : this() 79 { 80 m_Type = TypeCode.Byte; 81 m_ByteValue = value; 82 } 83 84 /// <summary> 85 /// Create a PrimitiveValue holding a signed byte. 86 /// </summary> 87 /// <param name="value">A signed byte value.</param> 88 public PrimitiveValue(sbyte value) 89 : this() 90 { 91 m_Type = TypeCode.SByte; 92 m_SByteValue = value; 93 } 94 95 /// <summary> 96 /// Create a PrimitiveValue holding a short. 97 /// </summary> 98 /// <param name="value">A short value.</param> 99 public PrimitiveValue(short value) 100 : this() 101 { 102 m_Type = TypeCode.Int16; 103 m_ShortValue = value; 104 } 105 106 /// <summary> 107 /// Create a PrimitiveValue holding an unsigned short. 108 /// </summary> 109 /// <param name="value">An unsigned short value.</param> 110 public PrimitiveValue(ushort value) 111 : this() 112 { 113 m_Type = TypeCode.UInt16; 114 m_UShortValue = value; 115 } 116 117 /// <summary> 118 /// Create a PrimitiveValue holding an int. 119 /// </summary> 120 /// <param name="value">An int value.</param> 121 public PrimitiveValue(int value) 122 : this() 123 { 124 m_Type = TypeCode.Int32; 125 m_IntValue = value; 126 } 127 128 /// <summary> 129 /// Create a PrimitiveValue holding an unsigned int. 130 /// </summary> 131 /// <param name="value">An unsigned int value.</param> 132 public PrimitiveValue(uint value) 133 : this() 134 { 135 m_Type = TypeCode.UInt32; 136 m_UIntValue = value; 137 } 138 139 /// <summary> 140 /// Create a PrimitiveValue holding a long. 141 /// </summary> 142 /// <param name="value">A long value.</param> 143 public PrimitiveValue(long value) 144 : this() 145 { 146 m_Type = TypeCode.Int64; 147 m_LongValue = value; 148 } 149 150 /// <summary> 151 /// Create a PrimitiveValue holding a ulong. 152 /// </summary> 153 /// <param name="value">An unsigned long value.</param> 154 public PrimitiveValue(ulong value) 155 : this() 156 { 157 m_Type = TypeCode.UInt64; 158 m_ULongValue = value; 159 } 160 161 /// <summary> 162 /// Create a PrimitiveValue holding a float. 163 /// </summary> 164 /// <param name="value">A float value.</param> 165 public PrimitiveValue(float value) 166 : this() 167 { 168 m_Type = TypeCode.Single; 169 m_FloatValue = value; 170 } 171 172 /// <summary> 173 /// Create a PrimitiveValue holding a double. 174 /// </summary> 175 /// <param name="value">A double value.</param> 176 public PrimitiveValue(double value) 177 : this() 178 { 179 m_Type = TypeCode.Double; 180 m_DoubleValue = value; 181 } 182 183 /// <summary> 184 /// Convert to another type of value. 185 /// </summary> 186 /// <param name="type">Type of value to convert to.</param> 187 /// <returns>The converted value.</returns> 188 /// <exception cref="ArgumentException">There is no conversion from the 189 /// PrimitiveValue's current <see cref="PrimitiveValue.type"/> to 190 /// <paramref name="type"/>.</exception> 191 /// <remarks> 192 /// This method simply calls the other conversion methods (<see cref="ToBoolean"/>, 193 /// <see cref="ToChar"/>, etc) based on the current type of value. <c>ArgumentException</c> 194 /// is thrown if there is no conversion from the current to the requested type. 195 /// 196 /// Every value can be converted to <c>TypeCode.Empty</c>. 197 /// </remarks> 198 /// <seealso cref="ToBoolean"/> 199 /// <seealso cref="ToChar"/> 200 /// <seealso cref="ToByte"/> 201 /// <seealso cref="ToSByte"/> 202 /// <seealso cref="ToInt16"/> 203 /// <seealso cref="ToInt32"/> 204 /// <seealso cref="ToInt64"/> 205 /// <seealso cref="ToUInt16"/> 206 /// <seealso cref="ToUInt32"/> 207 /// <seealso cref="ToUInt64"/> 208 /// <seealso cref="ToSingle"/> 209 /// <seealso cref="ToDouble"/> 210 public PrimitiveValue ConvertTo(TypeCode type) 211 { 212 switch (type) 213 { 214 case TypeCode.Boolean: return ToBoolean(); 215 case TypeCode.Char: return ToChar(); 216 case TypeCode.Byte: return ToByte(); 217 case TypeCode.SByte: return ToSByte(); 218 case TypeCode.Int16: return ToInt16(); 219 case TypeCode.Int32: return ToInt32(); 220 case TypeCode.Int64: return ToInt64(); 221 case TypeCode.UInt16: return ToInt16(); 222 case TypeCode.UInt32: return ToInt32(); 223 case TypeCode.UInt64: return ToUInt64(); 224 case TypeCode.Single: return ToSingle(); 225 case TypeCode.Double: return ToDouble(); 226 case TypeCode.Empty: return new PrimitiveValue(); 227 } 228 229 throw new ArgumentException($"Don't know how to convert PrimitiveValue to '{type}'", nameof(type)); 230 } 231 232 /// <summary> 233 /// Compare this value to <paramref name="other"/>. 234 /// </summary> 235 /// <param name="other">Another value.</param> 236 /// <returns>True if the two values are equal.</returns> 237 /// <remarks> 238 /// Equality is based on type and contents. The types of both values 239 /// must be identical and the memory contents of each value must be 240 /// bit-wise identical (i.e. things such as floating-point epsilons 241 /// are not taken into account). 242 /// </remarks> 243 public unsafe bool Equals(PrimitiveValue other) 244 { 245 if (m_Type != other.m_Type) 246 return false; 247 248 var thisValuePtr = UnsafeUtility.AddressOf(ref m_DoubleValue); 249 var otherValuePtr = UnsafeUtility.AddressOf(ref other.m_DoubleValue); 250 251 return UnsafeUtility.MemCmp(thisValuePtr, otherValuePtr, sizeof(double)) == 0; 252 } 253 254 /// <summary> 255 /// Compare this value to the value of <paramref name="obj"/>. 256 /// </summary> 257 /// <param name="obj">Either another PrimitiveValue or a boxed primitive 258 /// value such as a byte, bool, etc.</param> 259 /// <returns>True if the two values are equal.</returns> 260 /// <remarks> 261 /// If <paramref name="obj"/> is a boxed primitive value, it is automatically 262 /// converted to a PrimitiveValue. 263 /// </remarks> 264 public override bool Equals(object obj) 265 { 266 if (ReferenceEquals(null, obj)) 267 return false; 268 if (obj is PrimitiveValue value) 269 return Equals(value); 270 if (obj is bool || obj is char || obj is byte || obj is sbyte || obj is short 271 || obj is ushort || obj is int || obj is uint || obj is long || obj is ulong 272 || obj is float || obj is double) 273 return Equals(FromObject(obj)); 274 return false; 275 } 276 277 /// <summary> 278 /// Compare two PrimitiveValues for equality. 279 /// </summary> 280 /// <param name="left">First value.</param> 281 /// <param name="right">Second value.</param> 282 /// <returns>True if the two values are equal.</returns> 283 /// <seealso cref="Equals(PrimitiveValue)"/> 284 public static bool operator==(PrimitiveValue left, PrimitiveValue right) 285 { 286 return left.Equals(right); 287 } 288 289 /// <summary> 290 /// Compare two PrimitiveValues for inequality. 291 /// </summary> 292 /// <param name="left">First value.</param> 293 /// <param name="right">Second value.</param> 294 /// <returns>True if the two values are not equal.</returns> 295 /// <seealso cref="Equals(PrimitiveValue)"/> 296 public static bool operator!=(PrimitiveValue left, PrimitiveValue right) 297 { 298 return !left.Equals(right); 299 } 300 301 /// <summary> 302 /// Compute a hash code for the value. 303 /// </summary> 304 /// <returns>A hash code.</returns> 305 public override unsafe int GetHashCode() 306 { 307 unchecked 308 { 309 fixed(double* valuePtr = &m_DoubleValue) 310 { 311 var hashCode = m_Type.GetHashCode(); 312 hashCode = (hashCode * 397) ^ valuePtr->GetHashCode(); 313 return hashCode; 314 } 315 } 316 } 317 318 /// <summary> 319 /// Return a string representation of the value. 320 /// </summary> 321 /// <returns>A string representation of the value.</returns> 322 /// <remarks> 323 /// String versions of PrimitiveValues are always culture invariant. This means that 324 /// floating-point values, for example, will <em>not</em> the decimal separator of 325 /// the current culture. 326 /// </remarks> 327 /// <seealso cref="FromString"/> 328 public override string ToString() 329 { 330 switch (type) 331 { 332 case TypeCode.Boolean: 333 // Default ToString() uses "False" and "True". We want lowercase to match C# literals. 334 return m_BoolValue ? "true" : "false"; 335 case TypeCode.Char: 336 return $"'{m_CharValue.ToString()}'"; 337 case TypeCode.Byte: 338 return m_ByteValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 339 case TypeCode.SByte: 340 return m_SByteValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 341 case TypeCode.Int16: 342 return m_ShortValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 343 case TypeCode.UInt16: 344 return m_UShortValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 345 case TypeCode.Int32: 346 return m_IntValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 347 case TypeCode.UInt32: 348 return m_UIntValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 349 case TypeCode.Int64: 350 return m_LongValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 351 case TypeCode.UInt64: 352 return m_ULongValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 353 case TypeCode.Single: 354 return m_FloatValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 355 case TypeCode.Double: 356 return m_DoubleValue.ToString(CultureInfo.InvariantCulture.NumberFormat); 357 default: 358 return string.Empty; 359 } 360 } 361 362 /// <summary> 363 /// Parse the given string into a PrimitiveValue. 364 /// </summary> 365 /// <param name="value">A string containing a value.</param> 366 /// <returns>The PrimitiveValue parsed from the string.</returns> 367 /// <remarks> 368 /// Integers are parsed as longs. Floating-point numbers are parsed as doubles. 369 /// Hexadecimal notation is supported for integers. 370 /// </remarks> 371 /// <seealso cref="ToString()"/> 372 public static PrimitiveValue FromString(string value) 373 { 374 if (string.IsNullOrEmpty(value)) 375 return new PrimitiveValue(); 376 377 // Bool. 378 if (value.Equals("true", StringComparison.InvariantCultureIgnoreCase)) 379 return new PrimitiveValue(true); 380 if (value.Equals("false", StringComparison.InvariantCultureIgnoreCase)) 381 return new PrimitiveValue(false); 382 383 // Double. 384 if (value.Contains('.') || value.Contains("e") || value.Contains("E") || 385 value.Contains("infinity", StringComparison.InvariantCultureIgnoreCase)) 386 { 387 if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleResult)) 388 return new PrimitiveValue(doubleResult); 389 } 390 391 // Long. 392 if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longResult)) 393 { 394 return new PrimitiveValue(longResult); 395 } 396 // Try hex format. For whatever reason, HexNumber does not allow a 0x prefix so we manually 397 // get rid of it. 398 if (value.IndexOf("0x", StringComparison.InvariantCultureIgnoreCase) != -1) 399 { 400 var hexDigits = value.TrimStart(); 401 if (hexDigits.StartsWith("0x")) 402 hexDigits = hexDigits.Substring(2); 403 404 if (long.TryParse(hexDigits, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hexResult)) 405 return new PrimitiveValue(hexResult); 406 } 407 408 ////TODO: allow trailing width specifier 409 throw new NotImplementedException(); 410 } 411 412 /// <summary> 413 /// Equivalent to <see cref="type"/>. 414 /// </summary> 415 /// <returns>Type code for value stored in struct.</returns> 416 public TypeCode GetTypeCode() 417 { 418 return type; 419 } 420 421 /// <summary> 422 /// Convert the value to a boolean. 423 /// </summary> 424 /// <param name="provider">Ignored.</param> 425 /// <returns>Converted boolean value.</returns> 426 public bool ToBoolean(IFormatProvider provider = null) 427 { 428 switch (type) 429 { 430 case TypeCode.Boolean: 431 return m_BoolValue; 432 case TypeCode.Char: 433 return m_CharValue != default; 434 case TypeCode.Byte: 435 return m_ByteValue != default; 436 case TypeCode.SByte: 437 return m_SByteValue != default; 438 case TypeCode.Int16: 439 return m_ShortValue != default; 440 case TypeCode.UInt16: 441 return m_UShortValue != default; 442 case TypeCode.Int32: 443 return m_IntValue != default; 444 case TypeCode.UInt32: 445 return m_UIntValue != default; 446 case TypeCode.Int64: 447 return m_LongValue != default; 448 case TypeCode.UInt64: 449 return m_ULongValue != default; 450 case TypeCode.Single: 451 return !Mathf.Approximately(m_FloatValue, default); 452 case TypeCode.Double: 453 return !NumberHelpers.Approximately(m_DoubleValue, default); 454 default: 455 return default; 456 } 457 } 458 459 /// <summary> 460 /// Convert the value to a byte. 461 /// </summary> 462 /// <param name="provider">Ignored.</param> 463 /// <returns>Converted byte value.</returns> 464 public byte ToByte(IFormatProvider provider = null) 465 { 466 return (byte)ToInt64(provider); 467 } 468 469 /// <summary> 470 /// Convert the value to a char. 471 /// </summary> 472 /// <param name="provider">Ignored.</param> 473 /// <returns>Converted char value.</returns> 474 public char ToChar(IFormatProvider provider = null) 475 { 476 switch (type) 477 { 478 case TypeCode.Char: 479 return m_CharValue; 480 case TypeCode.Int16: 481 case TypeCode.Int32: 482 case TypeCode.Int64: 483 case TypeCode.UInt16: 484 case TypeCode.UInt32: 485 case TypeCode.UInt64: 486 return (char)ToInt64(provider); 487 default: 488 return default; 489 } 490 } 491 492 /// <summary> 493 /// Not supported. Throws <c>NotSupportedException</c>. 494 /// </summary> 495 /// <param name="provider">Ignored.</param> 496 /// <returns>Does not return.</returns> 497 /// <exception cref="NotSupportedException">Always thrown.</exception> 498 public DateTime ToDateTime(IFormatProvider provider = null) 499 { 500 throw new NotSupportedException("Converting PrimitiveValue to DateTime"); 501 } 502 503 /// <summary> 504 /// Convert the value to a decimal. 505 /// </summary> 506 /// <param name="provider">Ignored.</param> 507 /// <returns>Value converted to decimal format.</returns> 508 public decimal ToDecimal(IFormatProvider provider = null) 509 { 510 return new decimal(ToDouble(provider)); 511 } 512 513 /// <summary> 514 /// Convert the value to a double. 515 /// </summary> 516 /// <param name="provider">Ignored.</param> 517 /// <returns>Converted double value.</returns> 518 public double ToDouble(IFormatProvider provider = null) 519 { 520 switch (type) 521 { 522 case TypeCode.Boolean: 523 if (m_BoolValue) 524 return 1; 525 return 0; 526 case TypeCode.Char: 527 return m_CharValue; 528 case TypeCode.Byte: 529 return m_ByteValue; 530 case TypeCode.SByte: 531 return m_SByteValue; 532 case TypeCode.Int16: 533 return m_ShortValue; 534 case TypeCode.UInt16: 535 return m_UShortValue; 536 case TypeCode.Int32: 537 return m_IntValue; 538 case TypeCode.UInt32: 539 return m_UIntValue; 540 case TypeCode.Int64: 541 return m_LongValue; 542 case TypeCode.UInt64: 543 return m_ULongValue; 544 case TypeCode.Single: 545 return m_FloatValue; 546 case TypeCode.Double: 547 return m_DoubleValue; 548 default: 549 return default; 550 } 551 } 552 553 /// <summary> 554 /// Convert the value to a <c>short</c>. 555 /// </summary> 556 /// <param name="provider">Ignored.</param> 557 /// <returns>Converted <c>short</c> value.</returns> 558 public short ToInt16(IFormatProvider provider = null) 559 { 560 return (short)ToInt64(provider); 561 } 562 563 /// <summary> 564 /// Convert the value to an <c>int</c> 565 /// </summary> 566 /// <param name="provider">Ignored.</param> 567 /// <returns>Converted <c>int</c> value.</returns> 568 public int ToInt32(IFormatProvider provider = null) 569 { 570 return (int)ToInt64(provider); 571 } 572 573 /// <summary> 574 /// Convert the value to a <c>long</c> 575 /// </summary> 576 /// <param name="provider">Ignored.</param> 577 /// <returns>Converted <c>long</c> value.</returns> 578 public long ToInt64(IFormatProvider provider = null) 579 { 580 switch (type) 581 { 582 case TypeCode.Boolean: 583 if (m_BoolValue) 584 return 1; 585 return 0; 586 case TypeCode.Char: 587 return m_CharValue; 588 case TypeCode.Byte: 589 return m_ByteValue; 590 case TypeCode.SByte: 591 return m_SByteValue; 592 case TypeCode.Int16: 593 return m_ShortValue; 594 case TypeCode.UInt16: 595 return m_UShortValue; 596 case TypeCode.Int32: 597 return m_IntValue; 598 case TypeCode.UInt32: 599 return m_UIntValue; 600 case TypeCode.Int64: 601 return m_LongValue; 602 case TypeCode.UInt64: 603 return (long)m_ULongValue; 604 case TypeCode.Single: 605 return (long)m_FloatValue; 606 case TypeCode.Double: 607 return (long)m_DoubleValue; 608 default: 609 return default; 610 } 611 } 612 613 /// <summary> 614 /// Convert the value to a <c>sbyte</c>. 615 /// </summary> 616 /// <param name="provider">Ignored.</param> 617 /// <returns>Converted <c>sbyte</c> value.</returns> 618 public sbyte ToSByte(IFormatProvider provider = null) 619 { 620 return (sbyte)ToInt64(provider); 621 } 622 623 /// <summary> 624 /// Convert the value to a <c>float</c>. 625 /// </summary> 626 /// <param name="provider">Ignored.</param> 627 /// <returns>Converted <c>float</c> value.</returns> 628 public float ToSingle(IFormatProvider provider = null) 629 { 630 return (float)ToDouble(provider); 631 } 632 633 /// <summary> 634 /// Convert the value to a <c>string</c>. 635 /// </summary> 636 /// <param name="provider">Ignored.</param> 637 /// <returns>Converted <c>string</c> value.</returns> 638 /// <remarks> 639 /// Same as calling <see cref="ToString()"/>. 640 /// </remarks> 641 public string ToString(IFormatProvider provider) 642 { 643 return ToString(); 644 } 645 646 /// <summary> 647 /// Not supported. 648 /// </summary> 649 /// <param name="conversionType">Ignored.</param> 650 /// <param name="provider">Ignored.</param> 651 /// <returns>Does not return.</returns> 652 /// <exception cref="NotSupportedException">Always thrown.</exception> 653 public object ToType(Type conversionType, IFormatProvider provider) 654 { 655 throw new NotSupportedException(); 656 } 657 658 /// <summary> 659 /// Convert the value to a <c>ushort</c>. 660 /// </summary> 661 /// <param name="provider">Ignored.</param> 662 /// <returns>Converted <c>ushort</c> value.</returns> 663 public ushort ToUInt16(IFormatProvider provider = null) 664 { 665 return (ushort)ToUInt64(); 666 } 667 668 /// <summary> 669 /// Convert the value to a <c>uint</c>. 670 /// </summary> 671 /// <param name="provider">Ignored.</param> 672 /// <returns>Converted <c>uint</c> value.</returns> 673 public uint ToUInt32(IFormatProvider provider = null) 674 { 675 return (uint)ToUInt64(); 676 } 677 678 /// <summary> 679 /// Convert the value to a <c>ulong</c>. 680 /// </summary> 681 /// <param name="provider">Ignored.</param> 682 /// <returns>Converted <c>ulong</c> value.</returns> 683 public ulong ToUInt64(IFormatProvider provider = null) 684 { 685 switch (type) 686 { 687 case TypeCode.Boolean: 688 if (m_BoolValue) 689 return 1; 690 return 0; 691 case TypeCode.Char: 692 return m_CharValue; 693 case TypeCode.Byte: 694 return m_ByteValue; 695 case TypeCode.SByte: 696 return (ulong)m_SByteValue; 697 case TypeCode.Int16: 698 return (ulong)m_ShortValue; 699 case TypeCode.UInt16: 700 return m_UShortValue; 701 case TypeCode.Int32: 702 return (ulong)m_IntValue; 703 case TypeCode.UInt32: 704 return m_UIntValue; 705 case TypeCode.Int64: 706 return (ulong)m_LongValue; 707 case TypeCode.UInt64: 708 return m_ULongValue; 709 case TypeCode.Single: 710 return (ulong)m_FloatValue; 711 case TypeCode.Double: 712 return (ulong)m_DoubleValue; 713 default: 714 return default; 715 } 716 } 717 718 /// <summary> 719 /// Return a boxed version of the value. 720 /// </summary> 721 /// <returns>A boxed GC heap object.</returns> 722 /// <remarks> 723 /// This method always allocates GC heap memory. 724 /// </remarks> 725 public object ToObject() 726 { 727 switch (m_Type) 728 { 729 case TypeCode.Boolean: return m_BoolValue; 730 case TypeCode.Char: return m_CharValue; 731 case TypeCode.Byte: return m_ByteValue; 732 case TypeCode.SByte: return m_SByteValue; 733 case TypeCode.Int16: return m_ShortValue; 734 case TypeCode.UInt16: return m_UShortValue; 735 case TypeCode.Int32: return m_IntValue; 736 case TypeCode.UInt32: return m_UIntValue; 737 case TypeCode.Int64: return m_LongValue; 738 case TypeCode.UInt64: return m_ULongValue; 739 case TypeCode.Single: return m_FloatValue; 740 case TypeCode.Double: return m_DoubleValue; 741 default: return null; 742 } 743 } 744 745 /// <summary> 746 /// Create a PrimitiveValue from the given "blittable"/struct value. 747 /// </summary> 748 /// <param name="value">A value.</param> 749 /// <typeparam name="TValue">Type of value to convert. Must be either an <c>enum</c> 750 /// or one of the C# primitive value types (<c>bool</c>, <c>int</c>, <c>float</c>, etc.).</typeparam> 751 /// <returns>The PrimitiveValue converted from <paramref name="value"/>. If it is an 752 /// <c>enum</c> type, the PrimitiveValue will hold a value of the enum's underlying 753 /// type (i.e. <c>Type.GetEnumUnderlyingType</c>).</returns> 754 /// <exception cref="ArgumentException">No conversion exists from the given <typeparamref name="TValue"/> 755 /// type.</exception> 756 public static PrimitiveValue From<TValue>(TValue value) 757 where TValue : struct 758 { 759 var type = typeof(TValue); 760 if (type.IsEnum) 761 type = type.GetEnumUnderlyingType(); 762 763 var typeCode = Type.GetTypeCode(type); 764 switch (typeCode) 765 { 766 case TypeCode.Boolean: return new PrimitiveValue(Convert.ToBoolean(value)); 767 case TypeCode.Char: return new PrimitiveValue(Convert.ToChar(value)); 768 case TypeCode.Byte: return new PrimitiveValue(Convert.ToByte(value)); 769 case TypeCode.SByte: return new PrimitiveValue(Convert.ToSByte(value)); 770 case TypeCode.Int16: return new PrimitiveValue(Convert.ToInt16(value)); 771 case TypeCode.Int32: return new PrimitiveValue(Convert.ToInt32(value)); 772 case TypeCode.Int64: return new PrimitiveValue(Convert.ToInt64(value)); 773 case TypeCode.UInt16: return new PrimitiveValue(Convert.ToUInt16(value)); 774 case TypeCode.UInt32: return new PrimitiveValue(Convert.ToUInt32(value)); 775 case TypeCode.UInt64: return new PrimitiveValue(Convert.ToUInt64(value)); 776 case TypeCode.Single: return new PrimitiveValue(Convert.ToSingle(value)); 777 case TypeCode.Double: return new PrimitiveValue(Convert.ToDouble(value)); 778 } 779 780 throw new ArgumentException( 781 $"Cannot convert value '{value}' of type '{typeof(TValue).Name}' to PrimitiveValue", nameof(value)); 782 } 783 784 /// <summary> 785 /// Create a PrimitiveValue from a boxed value. 786 /// </summary> 787 /// <param name="value">A value. If <c>null</c>, the result will be <c>default(PrimitiveValue)</c>. 788 /// If it is a <c>string</c>, <see cref="FromString"/> is used. Otherwise must be either an <c>enum</c> 789 /// or one of the C# primitive value types (<c>bool</c>, <c>int</c>, <c>float</c>, etc.). If it is an 790 /// <c>enum</c> type, the PrimitiveValue will hold a value of the enum's underlying 791 /// type (i.e. <c>Type.GetEnumUnderlyingType</c>).</param> 792 /// <exception cref="ArgumentException">No conversion exists from the type of <paramref name="value"/>.</exception> 793 public static PrimitiveValue FromObject(object value) 794 { 795 if (value == null) 796 return new PrimitiveValue(); 797 798 if (value is string stringValue) 799 return FromString(stringValue); 800 801 if (value is bool b) 802 return new PrimitiveValue(b); 803 if (value is char ch) 804 return new PrimitiveValue(ch); 805 if (value is byte bt) 806 return new PrimitiveValue(bt); 807 if (value is sbyte sbt) 808 return new PrimitiveValue(sbt); 809 if (value is short s) 810 return new PrimitiveValue(s); 811 if (value is ushort us) 812 return new PrimitiveValue(us); 813 if (value is int i) 814 return new PrimitiveValue(i); 815 if (value is uint ui) 816 return new PrimitiveValue(ui); 817 if (value is long l) 818 return new PrimitiveValue(l); 819 if (value is ulong ul) 820 return new PrimitiveValue(ul); 821 if (value is float f) 822 return new PrimitiveValue(f); 823 if (value is double d) 824 return new PrimitiveValue(d); 825 826 // Enum. 827 if (value is Enum) 828 { 829 var underlyingType = value.GetType().GetEnumUnderlyingType(); 830 var underlyingTypeCode = Type.GetTypeCode(underlyingType); 831 switch (underlyingTypeCode) 832 { 833 case TypeCode.Byte: return new PrimitiveValue((byte)value); 834 case TypeCode.SByte: return new PrimitiveValue((sbyte)value); 835 case TypeCode.Int16: return new PrimitiveValue((short)value); 836 case TypeCode.Int32: return new PrimitiveValue((int)value); 837 case TypeCode.Int64: return new PrimitiveValue((long)value); 838 case TypeCode.UInt16: return new PrimitiveValue((ushort)value); 839 case TypeCode.UInt32: return new PrimitiveValue((uint)value); 840 case TypeCode.UInt64: return new PrimitiveValue((ulong)value); 841 } 842 } 843 844 throw new ArgumentException($"Cannot convert '{value}' to primitive value", nameof(value)); 845 } 846 847 /// <summary> 848 /// Create a PrimitiveValue holding a bool. 849 /// </summary> 850 /// <param name="value">A boolean value.</param> 851 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 852 public static implicit operator PrimitiveValue(bool value) 853 { 854 return new PrimitiveValue(value); 855 } 856 857 /// <summary> 858 /// Create a PrimitiveValue holding a character. 859 /// </summary> 860 /// <param name="value">A character.</param> 861 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 862 public static implicit operator PrimitiveValue(char value) 863 { 864 return new PrimitiveValue(value); 865 } 866 867 /// <summary> 868 /// Create a PrimitiveValue holding a byte. 869 /// </summary> 870 /// <param name="value">A byte value.</param> 871 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 872 public static implicit operator PrimitiveValue(byte value) 873 { 874 return new PrimitiveValue(value); 875 } 876 877 /// <summary> 878 /// Create a PrimitiveValue holding a signed byte. 879 /// </summary> 880 /// <param name="value">A signed byte value.</param> 881 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 882 public static implicit operator PrimitiveValue(sbyte value) 883 { 884 return new PrimitiveValue(value); 885 } 886 887 /// <summary> 888 /// Create a PrimitiveValue holding a short. 889 /// </summary> 890 /// <param name="value">A short value.</param> 891 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 892 public static implicit operator PrimitiveValue(short value) 893 { 894 return new PrimitiveValue(value); 895 } 896 897 /// <summary> 898 /// Create a PrimitiveValue holding an unsigned short. 899 /// </summary> 900 /// <param name="value">An unsigned short value.</param> 901 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 902 public static implicit operator PrimitiveValue(ushort value) 903 { 904 return new PrimitiveValue(value); 905 } 906 907 /// <summary> 908 /// Create a PrimitiveValue holding an int. 909 /// </summary> 910 /// <param name="value">An int value.</param> 911 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 912 public static implicit operator PrimitiveValue(int value) 913 { 914 return new PrimitiveValue(value); 915 } 916 917 /// <summary> 918 /// Create a PrimitiveValue holding an unsigned int. 919 /// </summary> 920 /// <param name="value">An unsigned int value.</param> 921 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 922 public static implicit operator PrimitiveValue(uint value) 923 { 924 return new PrimitiveValue(value); 925 } 926 927 /// <summary> 928 /// Create a PrimitiveValue holding a long. 929 /// </summary> 930 /// <param name="value">A long value.</param> 931 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 932 public static implicit operator PrimitiveValue(long value) 933 { 934 return new PrimitiveValue(value); 935 } 936 937 /// <summary> 938 /// Create a PrimitiveValue holding a ulong. 939 /// </summary> 940 /// <param name="value">An unsigned long value.</param> 941 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 942 public static implicit operator PrimitiveValue(ulong value) 943 { 944 return new PrimitiveValue(value); 945 } 946 947 /// <summary> 948 /// Create a PrimitiveValue holding a float. 949 /// </summary> 950 /// <param name="value">A float value.</param> 951 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 952 public static implicit operator PrimitiveValue(float value) 953 { 954 return new PrimitiveValue(value); 955 } 956 957 /// <summary> 958 /// Create a PrimitiveValue holding a double. 959 /// </summary> 960 /// <param name="value">A double value.</param> 961 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 962 public static implicit operator PrimitiveValue(double value) 963 { 964 return new PrimitiveValue(value); 965 } 966 967 // The following methods exist only to make the annoying Microsoft code analyzer happy. 968 969 /// <summary> 970 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 971 /// </summary> 972 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 973 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 974 public static PrimitiveValue FromBoolean(bool value) 975 { 976 return new PrimitiveValue(value); 977 } 978 979 /// <summary> 980 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 981 /// </summary> 982 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 983 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 984 public static PrimitiveValue FromChar(char value) 985 { 986 return new PrimitiveValue(value); 987 } 988 989 /// <summary> 990 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 991 /// </summary> 992 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 993 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 994 public static PrimitiveValue FromByte(byte value) 995 { 996 return new PrimitiveValue(value); 997 } 998 999 /// <summary> 1000 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1001 /// </summary> 1002 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1003 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1004 public static PrimitiveValue FromSByte(sbyte value) 1005 { 1006 return new PrimitiveValue(value); 1007 } 1008 1009 /// <summary> 1010 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1011 /// </summary> 1012 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1013 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1014 public static PrimitiveValue FromInt16(short value) 1015 { 1016 return new PrimitiveValue(value); 1017 } 1018 1019 /// <summary> 1020 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1021 /// </summary> 1022 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1023 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1024 public static PrimitiveValue FromUInt16(ushort value) 1025 { 1026 return new PrimitiveValue(value); 1027 } 1028 1029 /// <summary> 1030 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1031 /// </summary> 1032 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1033 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1034 public static PrimitiveValue FromInt32(int value) 1035 { 1036 return new PrimitiveValue(value); 1037 } 1038 1039 /// <summary> 1040 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1041 /// </summary> 1042 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1043 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1044 public static PrimitiveValue FromUInt32(uint value) 1045 { 1046 return new PrimitiveValue(value); 1047 } 1048 1049 /// <summary> 1050 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1051 /// </summary> 1052 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1053 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1054 public static PrimitiveValue FromInt64(long value) 1055 { 1056 return new PrimitiveValue(value); 1057 } 1058 1059 /// <summary> 1060 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1061 /// </summary> 1062 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1063 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1064 public static PrimitiveValue FromUInt64(ulong value) 1065 { 1066 return new PrimitiveValue(value); 1067 } 1068 1069 /// <summary> 1070 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1071 /// </summary> 1072 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1073 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1074 public static PrimitiveValue FromSingle(float value) 1075 { 1076 return new PrimitiveValue(value); 1077 } 1078 1079 /// <summary> 1080 /// Constructs a <c>PrimitiveValue</c> from <paramref name="value"/>. 1081 /// </summary> 1082 /// <param name="value">The value to be stored in the returned <c>PrimitiveValue</c>.</param> 1083 /// <returns>A <c>PrimitiveValue</c> set to <paramref name="value"/></returns> 1084 public static PrimitiveValue FromDouble(double value) 1085 { 1086 return new PrimitiveValue(value); 1087 } 1088 } 1089}