Nessuna descrizione
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PrimitiveValue.cs 42KB

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