PrimitiveValue.cs 37 KB

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