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.

UnsafeText.cs 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using Unity.Burst;
  5. using Unity.Jobs;
  6. using Unity.Jobs.LowLevel.Unsafe;
  7. using UnityEngine.Assertions;
  8. namespace Unity.Collections.LowLevel.Unsafe
  9. {
  10. internal static class UnsafeTextExtensions
  11. {
  12. public static ref UnsafeList<byte> AsUnsafeListOfBytes( this ref UnsafeText text )
  13. {
  14. return ref UnsafeUtility.As<UntypedUnsafeList, UnsafeList<byte>>(ref text.m_UntypedListData);
  15. }
  16. }
  17. /// <summary>
  18. /// An unmanaged, mutable, resizable UTF-8 string.
  19. /// </summary>
  20. /// <remarks>
  21. /// The string is always null-terminated, meaning a zero byte always immediately follows the last character.
  22. /// </remarks>
  23. [BurstCompatible]
  24. [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
  25. [StructLayout(LayoutKind.Sequential)]
  26. public unsafe struct UnsafeText : INativeDisposable, IUTF8Bytes, INativeList<byte>
  27. {
  28. // NOTE! This Length is always > 0, because we have a null terminating byte.
  29. // We hide this byte from UnsafeText users.
  30. internal UntypedUnsafeList m_UntypedListData;
  31. /// <summary>
  32. /// Initializes and returns an instance of UnsafeText.
  33. /// </summary>
  34. /// <param name="capacity">The initial capacity, in bytes.</param>
  35. /// <param name="allocator">The allocator to use.</param>
  36. public UnsafeText(int capacity, AllocatorManager.AllocatorHandle allocator)
  37. {
  38. m_UntypedListData = default;
  39. this.AsUnsafeListOfBytes() = new UnsafeList<byte>(capacity + 1, allocator);
  40. Length = 0;
  41. }
  42. /// <summary>
  43. /// Whether this string's character buffer has been allocated (and not yet deallocated).
  44. /// </summary>
  45. /// <value>Whether this string's character buffer has been allocated (and not yet deallocated).</value>
  46. public bool IsCreated => this.AsUnsafeListOfBytes().IsCreated;
  47. /// <summary>
  48. /// Releases all resources (memory).
  49. /// </summary>
  50. public void Dispose()
  51. {
  52. this.AsUnsafeListOfBytes().Dispose();
  53. }
  54. /// <summary>
  55. /// Creates and schedules a job that will dispose this string.
  56. /// </summary>
  57. /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param>
  58. /// <returns>The handle of a new job that will dispose this string. The new job depends upon inputDeps.</returns>
  59. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  60. public JobHandle Dispose(JobHandle inputDeps)
  61. {
  62. return this.AsUnsafeListOfBytes().Dispose(inputDeps);
  63. }
  64. /// <summary>
  65. /// Reports whether container is empty.
  66. /// </summary>
  67. /// <value>True if the string is empty or the string has not been constructed.</value>
  68. public bool IsEmpty => !IsCreated || Length == 0;
  69. /// <summary>
  70. /// The byte at an index.
  71. /// </summary>
  72. /// <param name="index">A zero-based byte index.</param>
  73. /// <value>The byte at the index.</value>
  74. /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
  75. public byte this[int index]
  76. {
  77. get
  78. {
  79. CheckIndexInRange(index);
  80. return UnsafeUtility.ReadArrayElement<byte>(m_UntypedListData.Ptr, index);
  81. }
  82. set
  83. {
  84. CheckIndexInRange(index);
  85. UnsafeUtility.WriteArrayElement(m_UntypedListData.Ptr, index, value);
  86. }
  87. }
  88. /// <summary>
  89. /// Returns a reference to the byte (not character) at an index.
  90. /// </summary>
  91. /// <remarks>
  92. /// Deallocating or reallocating this string's character buffer makes the reference invalid.
  93. /// </remarks>
  94. /// <param name="index">A byte index.</param>
  95. /// <returns>A reference to the byte at the index.</returns>
  96. /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
  97. public ref byte ElementAt(int index)
  98. {
  99. CheckIndexInRange(index);
  100. return ref UnsafeUtility.ArrayElementAsRef<byte>(m_UntypedListData.Ptr, index);
  101. }
  102. /// <summary>
  103. /// Sets the length to 0.
  104. /// </summary>
  105. public void Clear()
  106. {
  107. Length = 0;
  108. }
  109. /// <summary>
  110. /// Returns a pointer to this string's character buffer.
  111. /// </summary>
  112. /// <remarks>
  113. /// The pointer is made invalid by operations that reallocate the character buffer, such as setting <see cref="`Capacity`"/>.
  114. /// </remarks>
  115. /// <returns>A pointer to this string's character buffer.</returns>
  116. public byte* GetUnsafePtr()
  117. {
  118. return (byte*)m_UntypedListData.Ptr;
  119. }
  120. /// <summary>
  121. /// Attempt to set the length in bytes of this string.
  122. /// </summary>
  123. /// <param name="newLength">The new length in bytes of the string.</param>
  124. /// <param name="clearOptions">Whether any bytes added should be zeroed out.</param>
  125. /// <returns>Always true.</returns>
  126. public bool TryResize(int newLength, NativeArrayOptions clearOptions = NativeArrayOptions.ClearMemory)
  127. {
  128. // this can't ever fail, because if we can't resize malloc will abort
  129. this.AsUnsafeListOfBytes().Resize(newLength + 1, clearOptions);
  130. this.AsUnsafeListOfBytes()[newLength] = 0;
  131. return true;
  132. }
  133. /// <summary>
  134. /// The current capacity in bytes of this string.
  135. /// </summary>
  136. /// <remarks>
  137. /// The null-terminator byte is not included in the capacity, so the string's character buffer is `Capacity + 1` in size.
  138. /// </remarks>
  139. /// <value>The current capacity in bytes of the string.</value>
  140. public int Capacity
  141. {
  142. get => this.AsUnsafeListOfBytes().Capacity - 1;
  143. set
  144. {
  145. CheckCapacityInRange(value + 1, this.AsUnsafeListOfBytes().Length);
  146. this.AsUnsafeListOfBytes().SetCapacity(value + 1);
  147. }
  148. }
  149. /// <summary>
  150. /// The current length in bytes of this string.
  151. /// </summary>
  152. /// <remarks>
  153. /// The length does not include the null terminator byte.
  154. /// </remarks>
  155. /// <value>The current length in bytes of the UTF-8 encoded string.</value>
  156. public int Length
  157. {
  158. get => this.AsUnsafeListOfBytes().Length - 1;
  159. set
  160. {
  161. this.AsUnsafeListOfBytes().Resize(value + 1);
  162. this.AsUnsafeListOfBytes()[value] = 0;
  163. }
  164. }
  165. /// <summary>
  166. /// Returns a managed string copy of this string.
  167. /// </summary>
  168. /// <returns>A managed string copy of this string.</returns>
  169. [NotBurstCompatible]
  170. public override string ToString()
  171. {
  172. if (!IsCreated)
  173. return "";
  174. return this.ConvertToString();
  175. }
  176. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  177. void CheckIndexInRange(int index)
  178. {
  179. if (index < 0)
  180. throw new IndexOutOfRangeException($"Index {index} must be positive.");
  181. if (index >= Length)
  182. throw new IndexOutOfRangeException($"Index {index} is out of range in UnsafeText of {Length} length.");
  183. }
  184. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  185. void ThrowCopyError(CopyError error, string source)
  186. {
  187. throw new ArgumentException($"UnsafeText: {error} while copying \"{source}\"");
  188. }
  189. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  190. static void CheckCapacityInRange(int value, int length)
  191. {
  192. if (value < 0)
  193. throw new ArgumentOutOfRangeException($"Value {value} must be positive.");
  194. if ((uint)value < (uint)length)
  195. throw new ArgumentOutOfRangeException($"Value {value} is out of range in NativeList of '{length}' Length.");
  196. }
  197. }
  198. }