설명 없음
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 9.7KB

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