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}