No Description
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.

FixedString.tt 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. <#/*THIS IS A T4 FILE - see t4_text_templating.md for what it is and how to run codegen*/#>
  2. <#@ template debug="True" #>
  3. <#@ output extension=".gen.cs" encoding="utf-8" #>
  4. <#@ assembly name="System.Core" #>
  5. //------------------------------------------------------------------------------
  6. // <auto-generated>
  7. // This code was generated by a tool.
  8. //
  9. // TextTransform Samples/Packages/com.unity.collections/Unity.Collections/FixedString.tt
  10. //
  11. // Changes to this file may cause incorrect behavior and will be lost if
  12. // the code is regenerated.
  13. // </auto-generated>
  14. //------------------------------------------------------------------------------
  15. using System.Collections.Generic;
  16. using System.Collections;
  17. using System.Diagnostics;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System;
  21. using Unity.Collections.LowLevel.Unsafe;
  22. using Unity.Mathematics;
  23. using UnityEngine.Internal;
  24. using UnityEngine;
  25. using Unity.Properties;
  26. namespace Unity.Collections
  27. {
  28. // A temporary copy of a struct is made before it is displayed in a C# debugger.
  29. // However, only the first element of data members with names is copied at this time.
  30. // Therefore, it's important that all data visible in the debugger, has a name
  31. // and includes no 'fixed' array. This is why we name every byte in the following struct.
  32. /// <summary>
  33. /// For internal use only. This type is 8 byte aligned
  34. /// </summary>
  35. [Serializable]
  36. [StructLayout(LayoutKind.Explicit, Size=16)]
  37. [GenerateTestsForBurstCompatibility]
  38. internal struct FixedBytes16Align8
  39. {
  40. <#
  41. for(var i = 0; i < 2; ++i)
  42. {
  43. var offset = (i*8).ToString("D4");
  44. #>
  45. /// <summary>
  46. /// For internal use only.
  47. /// </summary>
  48. [SerializeField] [FieldOffset(<#=i*8#>)] public ulong byte<#=offset#>;
  49. <#
  50. }
  51. #>
  52. }
  53. /// <summary>
  54. /// For internal use only.
  55. /// </summary>
  56. [Serializable]
  57. [StructLayout(LayoutKind.Explicit, Size=16)]
  58. [GenerateTestsForBurstCompatibility]
  59. public struct FixedBytes16
  60. {
  61. <#
  62. for(var i = 0; i < 16; ++i)
  63. {
  64. var offset = i.ToString("D4");
  65. #>
  66. /// <summary>
  67. /// For internal use only.
  68. /// </summary>
  69. [FieldOffset(<#=i#>)] public byte byte<#=offset#>;
  70. <#
  71. }
  72. #>
  73. }
  74. <#
  75. {
  76. var SIZES = new [] {32,64,128,512,4096};
  77. foreach (var BYTES in SIZES) {
  78. #>
  79. // A temporary copy of a struct is made before it is displayed in a C# debugger.
  80. // However, only the first element of data members with names is copied at this time.
  81. // Therefore, it's important that all data visible in the debugger, has a name
  82. // and includes no 'fixed' array. This is why we name every byte in the following struct.
  83. /// <summary>
  84. /// For internal use only.
  85. /// </summary>
  86. [Serializable]
  87. [StructLayout(LayoutKind.Explicit, Size=<#=BYTES#>)]
  88. [GenerateTestsForBurstCompatibility]
  89. internal struct FixedBytes<#=BYTES#>Align8
  90. {
  91. <#
  92. for(var i = 0; i < (BYTES/16)*16; i += 16)
  93. {
  94. var offset = i.ToString("D4");
  95. #>
  96. /// <summary>
  97. /// For internal use only.
  98. /// </summary>
  99. [SerializeField] [FieldOffset(<#=i#>)] internal FixedBytes16Align8 offset<#=offset#>;
  100. <#
  101. }
  102. #>
  103. }
  104. <#
  105. }}
  106. #>
  107. <#
  108. {
  109. var SIZES = new [] {32,64,128,512,4096};
  110. foreach (var BYTES in SIZES) {
  111. // 2 bytes for the ushort length
  112. var MAXLENGTH = BYTES - 2;
  113. var TYPENAME = $"FixedString{BYTES}Bytes";
  114. var OLD_TYPENAME = $"FixedString{BYTES}";
  115. #>
  116. // A temporary copy of a struct is made before it is displayed in a C# debugger.
  117. // However, only the first element of data members with names is copied at this time.
  118. // Therefore, it's important that all data visible in the debugger, has a name
  119. // and includes no 'fixed' array. This is why we name every byte in the following struct.
  120. /// <summary>
  121. /// For internal use only.
  122. /// </summary>
  123. [Serializable]
  124. [StructLayout(LayoutKind.Explicit, Size=<#=MAXLENGTH#>)]
  125. [GenerateTestsForBurstCompatibility]
  126. public struct FixedBytes<#=MAXLENGTH#>
  127. {
  128. <#
  129. for(var i = 0; i < (MAXLENGTH/16)*16; i += 16)
  130. {
  131. var offset = i.ToString("D4");
  132. #>
  133. /// <summary>
  134. /// For internal use only.
  135. /// </summary>
  136. [FieldOffset(<#=i#>)] public FixedBytes16 offset<#=offset#>;
  137. <#
  138. }
  139. for(var i = (MAXLENGTH/16)*16; i < MAXLENGTH; ++i)
  140. {
  141. var offset = i.ToString("D4");
  142. #>
  143. /// <summary>
  144. /// For internal use only.
  145. /// </summary>
  146. [FieldOffset(<#=i#>)] public byte byte<#=offset#>;
  147. <#
  148. }
  149. #>
  150. }
  151. /// <summary>
  152. /// An unmanaged UTF-8 string whose content is stored directly in the <#=BYTES#>-byte struct.
  153. /// </summary>
  154. /// <remarks>
  155. /// The binary layout of this string is guaranteed, for now and all time, to be a length (a little-endian two byte integer)
  156. /// followed by the bytes of the characters (with no padding). A zero byte always immediately follows the last character.
  157. /// Effectively, the number of bytes for storing characters is 3 less than <#=BYTES#> (two length bytes and one null byte).
  158. ///
  159. /// This layout is identical to a <see cref="FixedList<#=BYTES#>Bytes{T}"/> of bytes, thus allowing reinterpretation between FixedString<#=BYTES#>Bytes and FixedList<#=BYTES#>Bytes.
  160. ///
  161. /// By virtue of being an unmanaged, non-allocated struct with no pointers, this string is fully compatible with jobs and Burst compilation.
  162. /// Unlike managed string types, these strings can be put in any unmanaged ECS components, FixedList, or any other unmanaged structs.
  163. /// </remarks>
  164. [Serializable]
  165. [StructLayout(LayoutKind.Sequential, Size=<#=BYTES#>)]
  166. [GenerateTestsForBurstCompatibility]
  167. public partial struct <#=TYPENAME#>
  168. : INativeList<byte>
  169. , IUTF8Bytes
  170. , IComparable<String>
  171. , IEquatable<String>
  172. <#
  173. foreach (var OTHERBYTES in SIZES)
  174. {
  175. #>
  176. , IComparable<FixedString<#=OTHERBYTES#>Bytes>
  177. , IEquatable<FixedString<#=OTHERBYTES#>Bytes>
  178. <#
  179. }
  180. #>
  181. {
  182. internal const ushort utf8MaxLengthInBytes = <#=MAXLENGTH-1#>;
  183. [SerializeField] internal ushort utf8LengthInBytes;
  184. [SerializeField] internal FixedBytes<#=MAXLENGTH#> bytes;
  185. /// <summary>
  186. /// Returns the maximum number of UTF-8 bytes that can be stored in this string.
  187. /// </summary>
  188. /// <returns>
  189. /// The maximum number of UTF-8 bytes that can be stored in this string.
  190. /// </returns>
  191. public static int UTF8MaxLengthInBytes => utf8MaxLengthInBytes;
  192. /// <summary>
  193. /// For internal use only. Use <see cref="ToString"/> instead.
  194. /// </summary>
  195. /// <value>For internal use only. Use <see cref="ToString"/> instead.</value>
  196. [CreateProperty]
  197. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  198. [ExcludeFromBurstCompatTesting("Returns managed string")]
  199. public string Value => ToString();
  200. /// <summary>
  201. /// Returns a pointer to the character bytes.
  202. /// </summary>
  203. /// <remarks>
  204. /// The pointer returned by this method points into the internals of the target FixedString object. It is the
  205. /// caller's responsibility to ensure that the pointer is not used after the FixedString object is destroyed or goes
  206. /// out of scope.
  207. /// </remarks>
  208. /// <returns>A pointer to the character bytes.</returns>
  209. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  210. public readonly unsafe byte* GetUnsafePtr()
  211. {
  212. fixed(void* b = &bytes)
  213. return (byte*)b;
  214. }
  215. /// <summary>
  216. /// The current length in bytes of this string's content.
  217. /// </summary>
  218. /// <remarks>
  219. /// The length value does not include the null-terminator byte.
  220. /// </remarks>
  221. /// <param name="value">The new length in bytes of the string's content.</param>
  222. /// <exception cref="ArgumentOutOfRangeException">Thrown if the new length is out of bounds.</exception>
  223. /// <value>
  224. /// The current length in bytes of this string's content.
  225. /// </value>
  226. public int Length
  227. {
  228. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  229. readonly get
  230. {
  231. return utf8LengthInBytes;
  232. }
  233. set
  234. {
  235. CheckLengthInRange(value);
  236. utf8LengthInBytes = (ushort)value;
  237. unsafe
  238. {
  239. GetUnsafePtr()[utf8LengthInBytes] = 0;
  240. }
  241. }
  242. }
  243. /// <summary>
  244. /// The number of bytes this string has for storing UTF-8 characters.
  245. /// </summary>
  246. /// <value>The number of bytes this string has for storing UTF-8 characters.</value>
  247. /// <remarks>
  248. /// Does not include the null-terminator byte.
  249. ///
  250. /// A setter is included for conformity with <see cref="INativeList{T}"/>, but <see cref="Capacity"/> is fixed at <#=BYTES-3#>.
  251. /// Setting the value to anything other than <#=BYTES-3#> throws an exception.
  252. ///
  253. /// In UTF-8 encoding, each Unicode code point (character) requires 1 to 4 bytes,
  254. /// so the number of characters that can be stored may be less than the capacity.
  255. /// </remarks>
  256. /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to set the capacity to anything other than <#=BYTES-3#>.</exception>
  257. public int Capacity
  258. {
  259. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  260. readonly get
  261. {
  262. return utf8MaxLengthInBytes;
  263. }
  264. set
  265. {
  266. CheckCapacityInRange(value);
  267. }
  268. }
  269. /// <summary>
  270. /// Attempts to set the length in bytes. Does nothing if the new length is invalid.
  271. /// </summary>
  272. /// <param name="newLength">The desired length.</param>
  273. /// <param name="clearOptions">Whether added or removed bytes should be cleared (zeroed). (Increasing the length adds bytes; decreasing the length removes bytes.)</param>
  274. /// <returns>True if the new length is valid.</returns>
  275. public bool TryResize(int newLength, NativeArrayOptions clearOptions = NativeArrayOptions.ClearMemory)
  276. {
  277. if (newLength < 0 || newLength > utf8MaxLengthInBytes)
  278. return false;
  279. if (newLength == utf8LengthInBytes)
  280. return true;
  281. unsafe
  282. {
  283. if (clearOptions == NativeArrayOptions.ClearMemory)
  284. {
  285. if (newLength > utf8LengthInBytes)
  286. UnsafeUtility.MemClear(GetUnsafePtr() + utf8LengthInBytes, newLength - utf8LengthInBytes);
  287. else
  288. UnsafeUtility.MemClear(GetUnsafePtr() + newLength, utf8LengthInBytes - newLength);
  289. }
  290. utf8LengthInBytes = (ushort)newLength;
  291. // always null terminate
  292. GetUnsafePtr()[utf8LengthInBytes] = 0;
  293. }
  294. return true;
  295. }
  296. /// <summary>
  297. /// Returns true if this string is empty (has no characters).
  298. /// </summary>
  299. /// <value>True if this string is empty (has no characters).</value>
  300. public readonly bool IsEmpty
  301. {
  302. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  303. get => utf8LengthInBytes == 0;
  304. }
  305. /// <summary>
  306. /// Returns the byte (not character) at an index.
  307. /// </summary>
  308. /// <param name="index">A byte index.</param>
  309. /// <value>The byte at the index.</value>
  310. /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
  311. public byte this[int index]
  312. {
  313. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  314. readonly get
  315. {
  316. unsafe
  317. {
  318. CheckIndexInRange(index);
  319. return GetUnsafePtr()[index];
  320. }
  321. }
  322. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  323. set
  324. {
  325. unsafe
  326. {
  327. CheckIndexInRange(index);
  328. GetUnsafePtr()[index] = value;
  329. }
  330. }
  331. }
  332. /// <summary>
  333. /// Returns the reference to a byte (not character) at an index.
  334. /// </summary>
  335. /// <param name="index">A byte index.</param>
  336. /// <returns>A reference to the byte at the index.</returns>
  337. /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
  338. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  339. public ref byte ElementAt(int index)
  340. {
  341. unsafe
  342. {
  343. CheckIndexInRange(index);
  344. return ref GetUnsafePtr()[index];
  345. }
  346. }
  347. /// <summary>
  348. /// Sets the length to 0.
  349. /// </summary>
  350. public void Clear()
  351. {
  352. Length = 0;
  353. }
  354. /// <summary>
  355. /// Appends a byte.
  356. /// </summary>
  357. /// <remarks>
  358. /// A zero byte will always follow the newly appended byte.
  359. ///
  360. /// No validation is performed: it is your responsibility for the bytes of the string to form valid UTF-8 when you're done appending bytes.
  361. /// </remarks>
  362. /// <param name="value">A byte to append.</param>
  363. public void Add(in byte value)
  364. {
  365. this[Length++] = value;
  366. }
  367. /// <summary>
  368. /// An enumerator over the characters (not bytes) of a FixedString<#=BYTES#>Bytes.
  369. /// </summary>
  370. /// <remarks>
  371. /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
  372. /// The first <see cref="MoveNext"/> call advances the enumerator's index to the first character.
  373. /// </remarks>
  374. public struct Enumerator : IEnumerator
  375. {
  376. FixedString<#=BYTES#>Bytes target;
  377. int offset;
  378. Unicode.Rune current;
  379. /// <summary>
  380. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes.Enumerator.
  381. /// </summary>
  382. /// <param name="other">A FixeString<#=BYTES#> for which to create an enumerator.</param>
  383. public Enumerator(FixedString<#=BYTES#>Bytes other)
  384. {
  385. target = other;
  386. offset = 0;
  387. current = default;
  388. }
  389. /// <summary>
  390. /// Does nothing.
  391. /// </summary>
  392. public void Dispose()
  393. {
  394. }
  395. /// <summary>
  396. /// Advances the enumerator to the next character.
  397. /// </summary>
  398. /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
  399. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  400. public bool MoveNext()
  401. {
  402. if (offset >= target.Length)
  403. return false;
  404. unsafe
  405. {
  406. Unicode.Utf8ToUcs(out current, target.GetUnsafePtr(), ref offset, target.Length);
  407. }
  408. return true;
  409. }
  410. /// <summary>
  411. /// Resets the enumerator to its initial state.
  412. /// </summary>
  413. public void Reset()
  414. {
  415. offset = 0;
  416. current = default;
  417. }
  418. /// <summary>
  419. /// The current character.
  420. /// </summary>
  421. /// <remarks>
  422. /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
  423. /// </remarks>
  424. /// <value>The current character.</value>
  425. public Unicode.Rune Current
  426. {
  427. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  428. get => current;
  429. }
  430. object IEnumerator.Current => Current;
  431. }
  432. /// <summary>
  433. /// Returns an enumerator for iterating over the characters of this string.
  434. /// </summary>
  435. /// <returns>An enumerator for iterating over the characters of the FixedString<#=BYTES#>Bytes.</returns>
  436. public Enumerator GetEnumerator()
  437. {
  438. return new Enumerator(this);
  439. }
  440. /// <summary>
  441. /// Returns the lexicographical sort order of this string relative to another.
  442. /// </summary>
  443. /// <param name="other">A `System.String` to compare with.</param>
  444. /// <returns>An integer denoting the lexicographical sort order of this string relative to the other:
  445. ///
  446. /// 0 denotes both strings have the same sort position.<br/>
  447. /// -1 denotes that this string should be sorted to precede the other string.<br/>
  448. /// +1 denotes that this string should be sorted to follow the other string.<br/>
  449. /// </returns>
  450. [ExcludeFromBurstCompatTesting("Takes managed string")]
  451. public int CompareTo(String other)
  452. {
  453. return ToString().CompareTo(other);
  454. }
  455. /// <summary>
  456. /// Returns true if this string and another have the same length and all the same characters.
  457. /// </summary>
  458. /// <param name="other">A string to compare for equality.</param>
  459. /// <returns>True if this string and the other have the same length and all the same characters.</returns>
  460. [ExcludeFromBurstCompatTesting("Takes managed string")]
  461. public bool Equals(String other)
  462. {
  463. unsafe {
  464. int alen = utf8LengthInBytes;
  465. int blen = other.Length;
  466. byte* aptr = (byte*) UnsafeUtilityExtensions.AddressOf(bytes);
  467. fixed(char* bptr = other)
  468. {
  469. return UTF8ArrayUnsafeUtility.StrCmp(aptr, alen, bptr, blen) == 0;
  470. }
  471. }
  472. }
  473. /// <summary>
  474. /// Returns a reference to a FixedList<#=BYTES#>Bytes&lt;byte&gt; representation of this string.
  475. /// </summary>
  476. /// <remarks>
  477. /// The referenced FixedListByte<#=BYTES#> is the very same bytes as the original FixedString<#=BYTES#>Bytes,
  478. /// so it is only valid as long as the original FixedString<#=BYTES#>Bytes is valid.
  479. /// </remarks>
  480. /// <returns>A ref to a FixedListByte<#=BYTES#> representation of the FixedString<#=BYTES#>Bytes.</returns>
  481. public unsafe ref FixedList<#=BYTES#>Bytes<byte> AsFixedList()
  482. {
  483. return ref UnsafeUtility.AsRef<FixedList<#=BYTES#>Bytes<byte>>(UnsafeUtility.AddressOf(ref this));
  484. }
  485. /// <summary>
  486. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes with the characters copied from a string.
  487. /// </summary>
  488. /// <param name="source">The source string to copy.</param>
  489. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes.</exception>
  490. [ExcludeFromBurstCompatTesting("Takes managed string")]
  491. public FixedString<#=BYTES#>Bytes(String source)
  492. {
  493. this = default;
  494. var error = Initialize(source);
  495. CheckCopyError(error, source);
  496. }
  497. /// <summary>
  498. /// Initializes an instance of FixedString<#=BYTES#>Bytes with the characters copied from a string.
  499. /// </summary>
  500. /// <param name="source">The source string to copy.</param>
  501. /// <returns>If the length of the source string exceeds this fixed string's UTF8 capacity, only the portion that fits is copied in and CopyError.Truncation is returned.</returns>
  502. [ExcludeFromBurstCompatTesting("Takes managed string")]
  503. internal CopyError Initialize(String source)
  504. {
  505. return this.CopyFromTruncated(source);
  506. }
  507. /// <summary>
  508. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes with a single character repeatedly appended some number of times.
  509. /// </summary>
  510. /// <param name="rune">The Unicode.Rune to repeat.</param>
  511. /// <param name="count">The number of times to repeat the character. Default is 1.</param>
  512. public FixedString<#=BYTES#>Bytes(Unicode.Rune rune, int count = 1)
  513. {
  514. this = default;
  515. Initialize(rune, count);
  516. }
  517. /// <summary>
  518. /// Initializes an instance of FixedString<#=BYTES#>Bytes with a single character repeatedly appended some number of times.
  519. /// </summary>
  520. /// <param name="rune">The Unicode.Rune to repeat.</param>
  521. /// <param name="count">The number of times to repeat the character. Default is 1.</param>
  522. /// <returns>If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned.</returns>
  523. internal FormatError Initialize(Unicode.Rune rune, int count = 1)
  524. {
  525. this = default;
  526. return this.Append(rune, count);
  527. }
  528. /// <summary>
  529. /// Initializes an instance of FixedString<#=BYTES#>Bytes that is a copy of another string.
  530. /// </summary>
  531. /// <param name="srcBytes">The source buffer.</param>
  532. /// <param name="srcLength">The number of bytes to read from the source.</param>
  533. /// <returns>If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned.</returns>
  534. unsafe internal FormatError Initialize(byte* srcBytes, int srcLength)
  535. {
  536. bytes = default;
  537. utf8LengthInBytes = 0;
  538. unsafe {
  539. int len = 0;
  540. byte* dstBytes = GetUnsafePtr();
  541. var error = UTF8ArrayUnsafeUtility.AppendUTF8Bytes(dstBytes, ref len, utf8MaxLengthInBytes, srcBytes, srcLength);
  542. if(error != FormatError.None)
  543. return error;
  544. this.Length = len;
  545. }
  546. return FormatError.None;
  547. }
  548. /// <summary>
  549. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string.
  550. /// </summary>
  551. /// <param name="other">The string to copy.</param>
  552. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes.</exception>
  553. unsafe public FixedString<#=BYTES#>Bytes(NativeText.ReadOnly other)
  554. {
  555. this = default;
  556. var error = Initialize(other.GetUnsafePtr(), other.Length);
  557. CheckFormatError(error);
  558. }
  559. /// <summary>
  560. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string.
  561. /// </summary>
  562. /// <param name="other">The UnsafeText to copy.</param>
  563. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes.</exception>
  564. unsafe public FixedString<#=BYTES#>Bytes(in UnsafeText other)
  565. {
  566. this = default;
  567. var error = Initialize(other.GetUnsafePtr(), other.Length);
  568. CheckFormatError(error);
  569. }
  570. <#
  571. //
  572. // Generate easy conversion and comparison between this and other FixedString types
  573. //
  574. foreach (var OTHERBYTES in SIZES)
  575. {
  576. #>
  577. /// <summary>
  578. /// Returns the lexicographical sort order of this string relative to another.
  579. /// </summary>
  580. /// <param name="other">A string to compare with.</param>
  581. /// <returns>A number denoting the lexicographical sort order of this string relative to the other:
  582. ///
  583. /// 0 denotes that both strings have the same sort position.<br/>
  584. /// -1 denotes that this string should be sorted to precede the other.<br/>
  585. /// +1 denotes that this string should be sorted to follow the other.<br/>
  586. /// </returns>
  587. public int CompareTo(FixedString<#=OTHERBYTES#>Bytes other)
  588. {
  589. return FixedStringMethods.CompareTo(ref this, other);
  590. }
  591. /// <summary>
  592. /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string.
  593. /// </summary>
  594. /// <param name="other">The string to copy.</param>
  595. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes.</exception>
  596. public FixedString<#=BYTES#>Bytes(in FixedString<#=OTHERBYTES#>Bytes other)
  597. {
  598. this = default;
  599. var error = Initialize(other);
  600. CheckFormatError(error);
  601. }
  602. /// <summary>
  603. /// Initializes an instance of FixedString<#=BYTES#>Bytes that is a copy of another string.
  604. /// </summary>
  605. /// <param name="other">The string to copy.</param>
  606. /// <returns>If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned.</returns>
  607. unsafe internal FormatError Initialize(in FixedString<#=OTHERBYTES#>Bytes other)
  608. {
  609. return Initialize((byte*) UnsafeUtilityExtensions.AddressOf(other.bytes), other.utf8LengthInBytes);
  610. }
  611. /// <summary>
  612. /// Returns true if a FixedString<#=BYTES#>Bytes and another string are equal.
  613. /// </summary>
  614. /// <remarks>Two strings are equal if they have equal length and all their characters match.</remarks>
  615. /// <param name="a">A FixedString<#=BYTES#>Bytes to compare for equality.</param>
  616. /// <param name="b">A FixedString<#=OTHERBYTES#>Bytes to compare for equality.</param>
  617. /// <returns>True if the two strings are equal.</returns>
  618. public static bool operator ==(in FixedString<#=BYTES#>Bytes a, in FixedString<#=OTHERBYTES#>Bytes b)
  619. {
  620. // this must not call any methods on 'a' or 'b'
  621. unsafe {
  622. int alen = a.utf8LengthInBytes;
  623. int blen = b.utf8LengthInBytes;
  624. byte* aptr = (byte*) UnsafeUtilityExtensions.AddressOf(a.bytes);
  625. byte* bptr = (byte*) UnsafeUtilityExtensions.AddressOf(b.bytes);
  626. return UTF8ArrayUnsafeUtility.EqualsUTF8Bytes(aptr, alen, bptr, blen);
  627. }
  628. }
  629. /// <summary>
  630. /// Returns true if a FixedString<#=BYTES#>Bytes and another string are unequal.
  631. /// </summary>
  632. /// <remarks>Two strings are equal if they have equal length and all their characters match.</remarks>
  633. /// <param name="a">A FixedString<#=BYTES#>Bytes to compare for inequality.</param>
  634. /// <param name="b">A FixedString<#=OTHERBYTES#>Bytes to compare for inequality.</param>
  635. /// <returns>True if the two strings are unequal.</returns>
  636. public static bool operator !=(in FixedString<#=BYTES#>Bytes a, in FixedString<#=OTHERBYTES#>Bytes b)
  637. {
  638. return !(a == b);
  639. }
  640. /// <summary>
  641. /// Returns true if this string and another string are equal.
  642. /// </summary>
  643. /// <remarks>Two strings are equal if they have equal length and all their characters match.</remarks>
  644. /// <param name="other">A FixedString<#=OTHERBYTES#>Bytes to compare for equality.</param>
  645. /// <returns>True if the two strings are equal.</returns>
  646. public bool Equals(FixedString<#=OTHERBYTES#>Bytes other)
  647. {
  648. return this == other;
  649. }
  650. <#
  651. if (OTHERBYTES > BYTES)
  652. {
  653. // Generate implicit conversions to bigger-sized FixedStrings
  654. #>
  655. /// <summary>
  656. /// Returns a new FixedString<#=OTHERBYTES#>Bytes that is a copy of another string.
  657. /// </summary>
  658. /// <param name="fs">A FixedString<#=BYTES#>Bytes to copy.</param>
  659. /// <returns>A new FixedString<#=OTHERBYTES#>Bytes that is a copy of the other string.</returns>
  660. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=OTHERBYTES#>Bytes.</exception>
  661. public static implicit operator FixedString<#=OTHERBYTES#>Bytes(in FixedString<#=BYTES#>Bytes fs) => new FixedString<#=OTHERBYTES#>Bytes(in fs);
  662. <#
  663. }
  664. }
  665. #>
  666. /// <summary>
  667. /// Returns a new FixedString<#=BYTES#>Bytes that is a copy of another string.
  668. /// </summary>
  669. /// <param name="b">A string to copy.</param>
  670. /// <returns>A new FixedString<#=BYTES#>Bytes that is a copy of another string.</returns>
  671. /// <exception cref="ArgumentException">Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes.</exception>
  672. [ExcludeFromBurstCompatTesting("Takes managed string")]
  673. public static implicit operator FixedString<#=BYTES#>Bytes(string b) => new FixedString<#=BYTES#>Bytes(b);
  674. /// <summary>
  675. /// Returns a new managed string that is a copy of this string.
  676. /// </summary>
  677. /// <returns>A new managed string that is a copy of this string.</returns>
  678. [ExcludeFromBurstCompatTesting("Returns managed string")]
  679. public override String ToString()
  680. {
  681. return this.ConvertToString();
  682. }
  683. /// <summary>
  684. /// Returns a hash code of this string.
  685. /// </summary>
  686. /// <remarks>Only the character bytes are included in the hash: any bytes beyond <see cref="Length"/> are not part of the hash.</remarks>
  687. /// <returns>The hash code of this string.</returns>
  688. public override int GetHashCode()
  689. {
  690. return this.ComputeHashCode();
  691. }
  692. /// <summary>
  693. /// Returns true if this string and an object are equal.
  694. /// </summary>
  695. /// <remarks>
  696. /// Returns false if the object is neither a System.String or a FixedString.
  697. ///
  698. /// Two strings are equal if they have equal length and all their characters match.</remarks>
  699. /// <param name="obj">An object to compare for equality.</param>
  700. /// <returns>True if this string and the object are equal.</returns>
  701. [ExcludeFromBurstCompatTesting("Takes managed object")]
  702. public override bool Equals(object obj)
  703. {
  704. if(ReferenceEquals(null, obj)) return false;
  705. if(obj is String aString) return Equals(aString);
  706. <#
  707. foreach(var OTHERBYTES in SIZES)
  708. {
  709. var OTHERTYPENAME = "FixedString" + OTHERBYTES + "Bytes";
  710. WriteLine(" if(obj is {0} a{0}) return Equals(a{0});", OTHERTYPENAME);
  711. }
  712. #>
  713. return false;
  714. }
  715. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  716. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  717. readonly void CheckIndexInRange(int index)
  718. {
  719. if (index < 0)
  720. throw new IndexOutOfRangeException($"Index {index} must be positive.");
  721. if (index >= utf8LengthInBytes)
  722. throw new IndexOutOfRangeException($"Index {index} is out of range in FixedString<#=BYTES#>Bytes of '{utf8LengthInBytes}' Length.");
  723. }
  724. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  725. void CheckLengthInRange(int length)
  726. {
  727. if (length < 0)
  728. throw new ArgumentOutOfRangeException($"Length {length} must be positive.");
  729. if (length > utf8MaxLengthInBytes)
  730. throw new ArgumentOutOfRangeException($"Length {length} is out of range in FixedString<#=BYTES#>Bytes of '{utf8MaxLengthInBytes}' Capacity.");
  731. }
  732. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  733. void CheckCapacityInRange(int capacity)
  734. {
  735. if (capacity > utf8MaxLengthInBytes)
  736. throw new ArgumentOutOfRangeException($"Capacity {capacity} must be lower than {utf8MaxLengthInBytes}.");
  737. }
  738. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  739. static void CheckCopyError(CopyError error, String source)
  740. {
  741. if (error != CopyError.None)
  742. throw new ArgumentException($"FixedString<#=BYTES#>Bytes: {error} while copying \"{source}\"");
  743. }
  744. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  745. static void CheckFormatError(FormatError error)
  746. {
  747. if (error != FormatError.None)
  748. throw new ArgumentException("Source is too long to fit into fixed string of this size");
  749. }
  750. }
  751. <#}}#>
  752. }