A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using Unity.Collections.LowLevel.Unsafe;
4using Unity.Mathematics;
5
6namespace Unity.Collections
7{
8 /// <summary>
9 /// Provides extension methods for string, UnsafeText, and NativeText.
10 /// </summary>
11 [GenerateTestsForBurstCompatibility]
12 public unsafe static partial class FixedStringMethods
13 {
14 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
15 internal static void CheckSubstringInRange(int strLength, int startIndex, int length)
16 {
17 if (startIndex < 0)
18 {
19 throw new ArgumentOutOfRangeException($"startIndex {startIndex} must be positive.");
20 }
21
22 if (length < 0)
23 {
24 throw new ArgumentOutOfRangeException($"length {length} cannot be negative.");
25 }
26
27 if (startIndex > strLength)
28 {
29 throw new ArgumentOutOfRangeException($"startIndex {startIndex} cannot be larger than string length {strLength}.");
30 }
31 }
32
33 /// <summary>
34 /// Retrieves a substring of this string. The substring starts from a specific character index, and has a specified length.
35 /// </summary>
36 /// <typeparam name="T">A string type.</typeparam>
37 /// <param name="str">A string to get the substring from.</param>
38 /// <param name="startIndex">Start index of substring.</param>
39 /// <param name="length">Length of substring.</param>
40 /// <returns>A new string with length equivalent to `length` that begins at `startIndex`.</returns>
41 /// <exception cref="ArgumentOutOfRangeException">Thrown if startIndex or length parameter is negative, or if startIndex is larger than the string length.</exception>
42 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
43 public static T Substring<T>(ref this T str, int startIndex, int length)
44 where T : unmanaged, INativeList<byte>, IUTF8Bytes
45 {
46 CheckSubstringInRange(str.Length, startIndex, length);
47 length = math.min(length, str.Length - startIndex);
48
49 var substr = new T();
50 substr.Append(str.GetUnsafePtr() + startIndex, length);
51 return substr;
52 }
53
54 /// <summary>
55 /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string.
56 /// </summary>
57 /// <typeparam name="T">A string type.</typeparam>
58 /// <param name="str">A string to get the substring from.</param>
59 /// <param name="startIndex">Start index of substring.</param>
60 /// <returns>A new string that begins at `startIndex`.</returns>
61 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
62 public static T Substring<T>(ref this T str, int startIndex)
63 where T : unmanaged, INativeList<byte>, IUTF8Bytes
64 {
65 return str.Substring(startIndex, str.Length - startIndex);
66 }
67
68 /// <summary>
69 /// Retrieves a substring from this string. The substring starts from a specific character index, and has a specified length. Allocates memory to the new substring with the allocator specified.
70 /// </summary>
71 /// <param name="str">A <see cref="NativeText"/> string to get the substring from.</param>
72 /// <param name="startIndex">Start index of substring.</param>
73 /// <param name="length">Length of substring.</param>
74 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
75 /// <returns>A `NativeText` string with a length equivalent to `length` that starts at `startIndex` and an allocator type of `allocator`.</returns>
76 /// <exception cref="ArgumentOutOfRangeException">Thrown if startIndex or length parameter is negative, or if startIndex is larger than string length.</exception>
77 public static NativeText Substring(ref this NativeText str, int startIndex, int length, AllocatorManager.AllocatorHandle allocator)
78 {
79 CheckSubstringInRange(str.Length, startIndex, length);
80 length = math.min(length, str.Length - startIndex);
81
82 var substr = new NativeText(length, allocator);
83 substr.Append(str.GetUnsafePtr() + startIndex, length);
84 return substr;
85 }
86
87 /// <summary>
88 /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string. Allocates memory to the new substring with the allocator specified.
89 /// </summary>
90 /// <param name="str">A <see cref="NativeText"/> string to get the substring from.</param>
91 /// <param name="startIndex">Start index of substring.</param>
92 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
93 /// <returns>A NativeText string that begins at `startIndex` and has an allocator of type `allocator`.</returns>
94 public static NativeText Substring(ref this NativeText str, int startIndex, AllocatorManager.AllocatorHandle allocator)
95 {
96 return str.Substring(startIndex, str.Length - startIndex);
97 }
98
99 /// <summary>
100 /// Retrieves a substring of this string. The substring starts from a specific character index, and has a specified length. The new substring has the same allocator as the string.
101 /// </summary>
102 /// <param name="str">A <see cref="NativeText"/> string to get the substring from.</param>
103 /// <param name="startIndex">Start index of substring.</param>
104 /// <param name="length">Length of substring.</param>
105 /// <returns>A NativeText string that has length equivalent to `length` and begins at `startIndex`.</returns>
106 /// <exception cref="ArgumentOutOfRangeException">Thrown if startIndex or length parameter is negative, or if startIndex is larger than string length.</exception>
107 public static NativeText Substring(ref this NativeText str, int startIndex, int length)
108 {
109 return str.Substring(startIndex, length, str.m_Data->m_UntypedListData.Allocator);
110 }
111
112 /// <summary>
113 /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string. The new substring has the same allocator as the string.
114 /// </summary>
115 /// <param name="str">A <see cref="NativeText"/> to get the substring from.</param>
116 /// <param name="startIndex">Start index of substring.</param>
117 /// <returns>A NativeText string that begins at `startIndex`.</returns>
118 public static NativeText Substring(ref this NativeText str, int startIndex)
119 {
120 return str.Substring(startIndex, str.Length - startIndex);
121 }
122
123 /// <summary>
124 /// Returns the index of the first occurrence of a single Unicode rune in this string.
125 /// </summary>
126 /// <typeparam name="T">A string type.</typeparam>
127 /// <param name="fs">A string to search.</param>
128 /// <param name="rune">A single UTF-8 Unicode Rune to search for within this string.</param>
129 /// <returns>The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found.</returns>
130 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
131 public static int IndexOf<T>(ref this T fs, Unicode.Rune rune)
132 where T : unmanaged, INativeList<byte>, IUTF8Bytes
133 {
134 var dstLen = fs.Length;
135 int index = 0;
136 while(index < dstLen)
137 {
138 int tempIndex = index;
139 var runeAtIndex = Read(ref fs, ref tempIndex);
140 if (runeAtIndex.value == rune.value)
141 {
142 return index;
143 }
144 index = tempIndex;
145 }
146 return -1;
147 }
148
149 /// <summary>
150 /// Returns the index of the first occurrence of a byte sequence in this string.
151 /// </summary>
152 /// <typeparam name="T">A string type.</typeparam>
153 /// <param name="fs">A string to search.</param>
154 /// <param name="bytes">A byte sequence to search for within this string.</param>
155 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
156 /// <returns>The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found.</returns>
157 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
158 public static int IndexOf<T>(ref this T fs, byte* bytes, int bytesLen)
159 where T : unmanaged, INativeList<byte>, IUTF8Bytes
160 {
161 var dst = fs.GetUnsafePtr();
162 var dstLen = fs.Length;
163 for (var i = 0; i <= dstLen - bytesLen; ++i)
164 {
165 for (var j = 0; j < bytesLen; ++j)
166 if (dst[i + j] != bytes[j])
167 goto end_of_loop;
168 return i;
169 end_of_loop : {}
170 }
171 return -1;
172 }
173
174 /// <summary>
175 /// Returns the index of the first occurrence of a byte sequence within a subrange of this string.
176 /// </summary>
177 /// <typeparam name="T">A string type.</typeparam>
178 /// <param name="fs">A string to search.</param>
179 /// <param name="bytes">A byte sequence to search for within this string.</param>
180 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
181 /// <param name="startIndex">The first index in this string to consider as the first byte of the byte sequence.</param>
182 /// <param name="distance">The last index in this string to consider as the first byte of the byte sequence.</param>
183 /// <returns>The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found.</returns>
184 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
185 public static int IndexOf<T>(ref this T fs, byte* bytes, int bytesLen, int startIndex, int distance = Int32.MaxValue)
186 where T : unmanaged, INativeList<byte>, IUTF8Bytes
187 {
188 var dst = fs.GetUnsafePtr();
189 var dstLen = fs.Length;
190 var searchrange = Math.Min(distance - 1, dstLen - bytesLen);
191 for (var i = startIndex; i <= searchrange; ++i)
192 {
193 for (var j = 0; j < bytesLen; ++j)
194 if (dst[i + j] != bytes[j])
195 goto end_of_loop;
196 return i;
197 end_of_loop : {}
198 }
199 return -1;
200 }
201
202 /// <summary>
203 /// Returns the index of the first occurrence of a substring within this string.
204 /// </summary>
205 /// <typeparam name="T">A string type.</typeparam>
206 /// <typeparam name="T2">A string type.</typeparam>
207 /// <param name="fs">A string to search.</param>
208 /// <param name="other">A substring to search for within this string.</param>
209 /// <returns>The index of the first occurrence of the second string within this string. Returns -1 if no occurrence is found.</returns>
210 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
211 public static int IndexOf<T,T2>(ref this T fs, in T2 other)
212 where T : unmanaged, INativeList<byte>, IUTF8Bytes
213 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
214 {
215 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
216 return fs.IndexOf(oref.GetUnsafePtr(), oref.Length);
217 }
218
219 /// <summary>
220 /// Returns the index of the first occurrence of a substring within a subrange of this string.
221 /// </summary>
222 /// <typeparam name="T">A string type.</typeparam>
223 /// <typeparam name="T2">A string type.</typeparam>
224 /// <param name="fs">A string to search.</param>
225 /// <param name="other">A substring to search for within this string.</param>
226 /// <param name="startIndex">The first index in this string to consider as an occurrence of the second string.</param>
227 /// <param name="distance">The last index in this string to consider as an occurrence of the second string.</param>
228 /// <returns>The index of the first occurrence of the substring within this string. Returns -1 if no occurrence is found.</returns>
229 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
230 public static int IndexOf<T,T2>(ref this T fs, in T2 other, int startIndex, int distance = Int32.MaxValue)
231 where T : unmanaged, INativeList<byte>, IUTF8Bytes
232 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
233 {
234 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
235 return fs.IndexOf(oref.GetUnsafePtr(), oref.Length, startIndex, distance);
236 }
237
238 /// <summary>
239 /// Returns true if a given substring occurs within this string.
240 /// </summary>
241 /// <typeparam name="T">A string type.</typeparam>
242 /// <typeparam name="T2">A string type.</typeparam>
243 /// <param name="fs">A string to search.</param>
244 /// <param name="other">A substring to search for within this string.</param>
245 /// <returns>True if the substring occurs within this string.</returns>
246 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
247 public static bool Contains<T,T2>(ref this T fs, in T2 other)
248 where T : unmanaged, INativeList<byte>, IUTF8Bytes
249 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
250 {
251 return fs.IndexOf(in other) != -1;
252 }
253
254 /// <summary>
255 /// Returns the index of the last occurrence of a single Unicode rune within this string.
256 /// </summary>
257 /// <typeparam name="T">A string type.</typeparam>
258 /// <param name="fs">A string to search.</param>
259 /// <param name="rune">A single Unicode.Rune to search for within this string.</param>
260 /// <returns>The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrence is found.</returns>
261 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
262 public static int LastIndexOf<T>(ref this T fs, Unicode.Rune rune)
263 where T : unmanaged, INativeList<byte>, IUTF8Bytes
264 {
265 if (Unicode.IsValidCodePoint(rune.value))
266 {
267 var dstLen = fs.Length;
268 for (var i = dstLen - 1; i >= 0; --i)
269 {
270 var runeAtIndex = Peek(ref fs, i);
271 if (Unicode.IsValidCodePoint(runeAtIndex.value) && runeAtIndex.value == rune.value)
272 {
273 return i;
274 }
275 }
276 }
277 return -1;
278 }
279
280 /// <summary>
281 /// Returns the index of the last occurrence of a byte sequence within this string.
282 /// </summary>
283 /// <typeparam name="T">A string type.</typeparam>
284 /// <param name="fs">A string to search.</param>
285 /// <param name="bytes">A byte sequence to search for within this string.</param>
286 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
287 /// <returns>The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrence is found.</returns>
288 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
289 public static int LastIndexOf<T>(ref this T fs, byte* bytes, int bytesLen)
290 where T : unmanaged, INativeList<byte>, IUTF8Bytes
291 {
292 var dst = fs.GetUnsafePtr();
293 var dstLen = fs.Length;
294 for (var i = dstLen - bytesLen; i >= 0; --i)
295 {
296 for (var j = 0; j < bytesLen; ++j)
297 if (dst[i + j] != bytes[j])
298 goto end_of_loop;
299 return i;
300 end_of_loop : {}
301 }
302 return -1;
303 }
304
305 /// <summary>
306 /// Returns the index of the last occurrence of a byte sequence within a subrange of this string.
307 /// </summary>
308 /// <typeparam name="T">A string type.</typeparam>
309 /// <param name="fs">A string to search.</param>
310 /// <param name="bytes">A byte sequence to search for within this string.</param>
311 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
312 /// <param name="startIndex">The smallest index in this string to consider as the first byte of the byte sequence.</param>
313 /// <param name="distance">The greatest index in this string to consider as the first byte of the byte sequence.</param>
314 /// <returns>The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrences found.</returns>
315 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
316 public static int LastIndexOf<T>(ref this T fs, byte* bytes, int bytesLen, int startIndex, int distance = int.MaxValue)
317 where T : unmanaged, INativeList<byte>, IUTF8Bytes
318 {
319 var dst = fs.GetUnsafePtr();
320 var dstLen = fs.Length;
321 startIndex = Math.Min(dstLen - bytesLen, startIndex);
322 var searchrange = Math.Max(0, startIndex - distance);
323 for (var i = startIndex; i >= searchrange; --i)
324 {
325 for (var j = 0; j < bytesLen; ++j)
326 if (dst[i + j] != bytes[j])
327 goto end_of_loop;
328 return i;
329 end_of_loop : {}
330 }
331 return -1;
332 }
333
334 /// <summary>
335 /// Returns the index of the last occurrence of a substring within this string.
336 /// </summary>
337 /// <typeparam name="T">A string type.</typeparam>
338 /// <typeparam name="T2">A string type.</typeparam>
339 /// <param name="fs">A string to search.</param>
340 /// <param name="other">A substring to search for in the this string.</param>
341 /// <returns>The index of the last occurrence of the substring within this string. Returns -1 if no occurrence is found.</returns>
342 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
343 public static int LastIndexOf<T,T2>(ref this T fs, in T2 other)
344 where T : unmanaged, INativeList<byte>, IUTF8Bytes
345 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
346 {
347 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
348 return fs.LastIndexOf(oref.GetUnsafePtr(), oref.Length);
349 }
350
351 /// <summary>
352 /// Returns the index of the last occurrence of a substring within a subrange of this string.
353 /// </summary>
354 /// <typeparam name="T">A string type.</typeparam>
355 /// <typeparam name="T2">A string type.</typeparam>
356 /// <param name="fs">A string to search.</param>
357 /// <param name="other">A substring to search for within this string.</param>
358 /// <param name="startIndex">The greatest index in this string to consider as an occurrence of the substring.</param>
359 /// <param name="distance">The smallest index in this string to consider as an occurrence of the substring.</param>
360 /// <returns>the index of the last occurrence of the substring within the first string. Returns -1 if no occurrence is found.</returns>
361 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
362 public static int LastIndexOf<T,T2>(ref this T fs, in T2 other, int startIndex, int distance = Int32.MaxValue)
363 where T : unmanaged, INativeList<byte>, IUTF8Bytes
364 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
365 {
366 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
367 return fs.LastIndexOf(oref.GetUnsafePtr(), oref.Length, startIndex, distance);
368 }
369
370 /// <summary>
371 /// Returns the sort position of this string relative to a byte sequence.
372 /// </summary>
373 /// <typeparam name="T">A string type.</typeparam>
374 /// <param name="fs">A string to compare.</param>
375 /// <param name="bytes">A byte sequence to compare.</param>
376 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
377 /// <returns>A number denoting the sort position of this string relative to the byte sequence:
378 ///
379 /// 0 denotes that this string and byte sequence have the same sort position.<br/>
380 /// -1 denotes that this string should be sorted to precede the byte sequence.<br/>
381 /// +1 denotes that this string should be sorted to follow the byte sequence.<br/>
382 /// </returns>
383 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
384 public static int CompareTo<T>(ref this T fs, byte* bytes, int bytesLen)
385 where T : unmanaged, INativeList<byte>, IUTF8Bytes
386 {
387 var a = fs.GetUnsafePtr();
388 var aa = fs.Length;
389 int chars = aa < bytesLen ? aa : bytesLen;
390 for (var i = 0; i < chars; ++i)
391 {
392 if (a[i] < bytes[i])
393 return -1;
394 if (a[i] > bytes[i])
395 return 1;
396 }
397 if (aa < bytesLen)
398 return -1;
399 if (aa > bytesLen)
400 return 1;
401 return 0;
402 }
403
404 /// <summary>
405 /// Returns the sort position of this string relative to another.
406 /// </summary>
407 /// <typeparam name="T">A string type.</typeparam>
408 /// <typeparam name="T2">A string type.</typeparam>
409 /// <param name="fs">A string to compare.</param>
410 /// <param name="other">Another string to compare.</param>
411 /// <returns>A number denoting the relative sort position of the strings:
412 ///
413 /// 0 denotes that the strings have the same sort position.<br/>
414 /// -1 denotes that this string should be sorted to precede the other.<br/>
415 /// +1 denotes that this first string should be sorted to follow the other.<br/>
416 /// </returns>
417 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
418 public static int CompareTo<T,T2>(ref this T fs, in T2 other)
419 where T : unmanaged, INativeList<byte>, IUTF8Bytes
420 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
421 {
422 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
423 return fs.CompareTo(oref.GetUnsafePtr(), oref.Length);
424 }
425
426 /// <summary>
427 /// Returns true if this string and a byte sequence are equal (meaning they have the same length and content).
428 /// </summary>
429 /// <typeparam name="T">A string type.</typeparam>
430 /// <param name="fs">A string to compare for equality.</param>
431 /// <param name="bytes">A sequence of bytes to compare for equality.</param>
432 /// <param name="bytesLen">The number of bytes in the byte sequence.</param>
433 /// <returns>True if this string and the byte sequence have the same length and if this string's character bytes match the byte sequence.</returns>
434 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
435 public static bool Equals<T>(ref this T fs, byte* bytes, int bytesLen)
436 where T : unmanaged, INativeList<byte>, IUTF8Bytes
437 {
438 var a = fs.GetUnsafePtr();
439 var aa = fs.Length;
440 if (aa != bytesLen)
441 return false;
442 if (a == bytes)
443 return true;
444 return fs.CompareTo(bytes, bytesLen) == 0;
445 }
446
447 /// <summary>
448 /// Returns true if this string is equal to another.
449 /// </summary>
450 /// <typeparam name="T">A string type.</typeparam>
451 /// <typeparam name="T2">A string type.</typeparam>
452 /// <param name="fs">A string to compare for equality.</param>
453 /// <param name="other">Another string to compare for equality.</param>
454 /// <returns>true if the two strings have the same length and matching content.</returns>
455 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
456 public static bool Equals<T,T2>(ref this T fs, in T2 other)
457 where T : unmanaged, INativeList<byte>, IUTF8Bytes
458 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes
459 {
460 ref var oref = ref UnsafeUtilityExtensions.AsRef(in other);
461 return fs.Equals(oref.GetUnsafePtr(), oref.Length);
462 }
463
464 /// <summary>
465 /// Returns the Unicode.Rune at an index of this string.
466 /// </summary>
467 /// <typeparam name="T">A string type.</typeparam>
468 /// <param name="fs">A string to read.</param>
469 /// <param name="index">A reference to an index in bytes (not characters).</param>
470 /// <returns>The Unicode.Rune (character) which starts at the byte index. Returns Unicode.BadRune
471 /// if the byte(s) at the index do not form a valid UTF-8 encoded character.</returns>
472 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
473 public static Unicode.Rune Peek<T>(ref this T fs, int index)
474 where T : unmanaged, INativeList<byte>, IUTF8Bytes
475 {
476 if (index >= fs.Length)
477 return Unicode.BadRune;
478 Unicode.Utf8ToUcs(out var rune, fs.GetUnsafePtr(), ref index, fs.Capacity);
479 return rune;
480 }
481
482 /// <summary>
483 /// Returns the Unicode.Rune at an index of this string. Increments the index to the position of the next character.
484 /// </summary>
485 /// <typeparam name="T">A string type.</typeparam>
486 /// <param name="fs">A string to read.</param>
487 /// <param name="index">A reference to an index in bytes (not characters). Incremented by 1 to 4 depending upon the UTF-8 encoded size of the character read.</param>
488 /// <returns>The character (as a `Unicode.Rune`) which starts at the byte index. Returns `Unicode.BadRune`
489 /// if the byte(s) at the index do not form a valid UTF-8 encoded character.</returns>
490 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
491 public static Unicode.Rune Read<T>(ref this T fs, ref int index)
492 where T : unmanaged, INativeList<byte>, IUTF8Bytes
493 {
494 if (index >= fs.Length)
495 return Unicode.BadRune;
496 Unicode.Utf8ToUcs(out var rune, fs.GetUnsafePtr(), ref index, fs.Capacity);
497 return rune;
498 }
499
500 /// <summary>
501 /// Writes a Unicode.Rune at an index of this string. Increments the index to the position of the next character.
502 /// </summary>
503 /// <typeparam name="T">A string type.</typeparam>
504 /// <param name="fs">A string to modify.</param>
505 /// <param name="index">A reference to an index in bytes (not characters). Incremented by 1 to 4 depending upon the UTF-8 encoded size of the character written.</param>
506 /// <param name="rune">A rune to write to the string, encoded as UTF-8.</param>
507 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the index is invalid or if there is not enough space to store the encoded rune.</returns>
508 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
509 public static FormatError Write<T>(ref this T fs, ref int index, Unicode.Rune rune)
510 where T : unmanaged, INativeList<byte>, IUTF8Bytes
511 {
512 var err = Unicode.UcsToUtf8(fs.GetUnsafePtr(), ref index, fs.Capacity, rune);
513 if (err != ConversionError.None)
514 return FormatError.Overflow;
515 return FormatError.None;
516 }
517
518 /// <summary>
519 /// Returns a copy of this string as a managed string.
520 /// </summary>
521 /// <typeparam name="T">A string type.</typeparam>
522 /// <param name="fs">A string to copy.</param>
523 /// <returns>A copy of this string as a managed string.</returns>
524 [ExcludeFromBurstCompatTesting("Returns managed string")]
525 public static String ConvertToString<T>(ref this T fs)
526 where T : unmanaged, INativeList<byte>, IUTF8Bytes
527 {
528 var c = stackalloc char[fs.Length * 2];
529 int length = 0;
530 Unicode.Utf8ToUtf16(fs.GetUnsafePtr(), fs.Length, c, out length, fs.Length * 2);
531 return new String(c, 0, length);
532 }
533
534 /// <summary>
535 /// Returns a hash code of this string.
536 /// </summary>
537 /// <typeparam name="T">A string type.</typeparam>
538 /// <param name="fs">A string to get a hash code of.</param>
539 /// <returns>A hash code of this string.</returns>
540 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
541 public static int ComputeHashCode<T>(ref this T fs)
542 where T : unmanaged, INativeList<byte>, IUTF8Bytes
543 {
544 return (int)CollectionHelper.Hash(fs.GetUnsafePtr(), fs.Length);
545 }
546
547 /// <summary>
548 /// Returns the effective size in bytes of this string.
549 /// </summary>
550 /// <remarks>
551 /// "Effective size" is `Length + 3`, the number of bytes you need to copy when serializing the string.
552 /// (The plus 3 accounts for the null-terminator byte and the 2 bytes that store the Length).
553 ///
554 /// Useful for checking whether this string will fit in the space of a smaller string.
555 /// </remarks>
556 /// <typeparam name="T">A string type.</typeparam>
557 /// <param name="fs">A string to get the effective size of.</param>
558 /// <returns>The effective size in bytes of this string.</returns>
559 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
560 public static int EffectiveSizeOf<T>(ref this T fs)
561 where T : unmanaged, INativeList<byte>, IUTF8Bytes
562 {
563 return sizeof(ushort) + fs.Length + 1;
564 }
565
566 /// <summary>
567 /// Returns true if a given character occurs at the beginning of this string.
568 /// </summary>
569 /// <typeparam name="T">A string type.</typeparam>
570 /// <param name="fs">A string to search.</param>
571 /// <param name="rune">A character to search for within this string.</param>
572 /// <returns>True if the character occurs at the beginning of this string.</returns>
573 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
574 public static bool StartsWith<T>(ref this T fs, Unicode.Rune rune)
575 where T : unmanaged, INativeList<byte>, IUTF8Bytes
576 {
577 var len = rune.LengthInUtf8Bytes();
578 return fs.Length >= len
579 && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr(), len, &rune, 1)
580 ;
581 }
582
583 /// <summary>
584 /// Returns true if a given substring occurs at the beginning of this string.
585 /// </summary>
586 /// <typeparam name="T">A string type.</typeparam>
587 /// <typeparam name="U">A string type.</typeparam>
588 /// <param name="fs">A string to search.</param>
589 /// <param name="other">A substring to search for within this string.</param>
590 /// <returns>True if the substring occurs at the beginning of this string.</returns>
591 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
592 public static bool StartsWith<T, U>(ref this T fs, in U other)
593 where T : unmanaged, INativeList<byte>, IUTF8Bytes
594 where U : unmanaged, INativeList<byte>, IUTF8Bytes
595 {
596 var len = other.Length;
597 return fs.Length >= len
598 && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr(), len, other.GetUnsafePtr(), len)
599 ;
600 }
601
602 /// <summary>
603 /// Returns true if a given character occurs at the end of this string.
604 /// </summary>
605 /// <typeparam name="T">A string type.</typeparam>
606 /// <param name="fs">A string to search.</param>
607 /// <param name="rune">A character to search for within this string.</param>
608 /// <returns>True if the character occurs at the end of this string.</returns>
609 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
610 public static bool EndsWith<T>(ref this T fs, Unicode.Rune rune)
611 where T : unmanaged, INativeList<byte>, IUTF8Bytes
612 {
613 var len = rune.LengthInUtf8Bytes();
614 return fs.Length >= len
615 && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr() + fs.Length - len, len, &rune, 1)
616 ;
617 }
618
619 /// <summary>
620 /// Returns true if a given substring occurs at the end of this string.
621 /// </summary>
622 /// <typeparam name="T">A string type.</typeparam>
623 /// <typeparam name="U">A string type.</typeparam>
624 /// <param name="fs">A string to search.</param>
625 /// <param name="other">A substring to search for within this string.</param>
626 /// <returns>True if the substring occurs at the end of this string.</returns>
627 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })]
628 public static bool EndsWith<T, U>(ref this T fs, in U other)
629 where T : unmanaged, INativeList<byte>, IUTF8Bytes
630 where U : unmanaged, INativeList<byte>, IUTF8Bytes
631 {
632 var len = other.Length;
633 return fs.Length >= len
634 && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr() + fs.Length - len, len, other.GetUnsafePtr(), len)
635 ;
636 }
637
638 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
639 internal static int TrimStartIndex<T>(ref this T fs)
640 where T : unmanaged, INativeList<byte>, IUTF8Bytes
641 {
642 var lengthInBytes = fs.Length;
643 var ptr = fs.GetUnsafePtr();
644
645 int index = 0;
646 while (true)
647 {
648 var prev = index;
649 var error = Unicode.Utf8ToUcs(out var rune, ptr, ref index, lengthInBytes);
650 if (error != ConversionError.None
651 || !rune.IsWhiteSpace())
652 {
653 index -= index - prev;
654 break;
655 }
656 }
657
658 return index;
659 }
660
661 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
662 internal static int TrimStartIndex<T>(ref this T fs, ReadOnlySpan<Unicode.Rune> trimRunes)
663 where T : unmanaged, INativeList<byte>, IUTF8Bytes
664 {
665 var lengthInBytes = fs.Length;
666 var ptr = fs.GetUnsafePtr();
667
668 int index = 0;
669 while (true)
670 {
671 var prev = index;
672 var error = Unicode.Utf8ToUcs(out var rune, ptr, ref index, lengthInBytes);
673
674 var doTrim = false;
675 for (int i = 0, num = trimRunes.Length; i < num && !doTrim; i++)
676 {
677 doTrim |= trimRunes[i] == rune;
678 }
679
680 if (error != ConversionError.None
681 || !doTrim)
682 {
683 index -= index - prev;
684 break;
685 }
686 }
687
688 return index;
689 }
690
691 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
692 internal static int TrimEndIndex<T>(ref this T fs)
693 where T : unmanaged, INativeList<byte>, IUTF8Bytes
694 {
695 var lengthInBytes = fs.Length;
696 var ptr = fs.GetUnsafePtr();
697
698 int index = lengthInBytes;
699 while (true)
700 {
701 var prev = index;
702 var error = Unicode.Utf8ToUcsReverse(out var rune, ptr, ref index, lengthInBytes);
703 if (error != ConversionError.None
704 || !rune.IsWhiteSpace())
705 {
706 index += prev - index;
707 break;
708 }
709 }
710
711 return index;
712 }
713
714 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
715 internal static int TrimEndIndex<T>(ref this T fs, ReadOnlySpan<Unicode.Rune> trimRunes)
716 where T : unmanaged, INativeList<byte>, IUTF8Bytes
717 {
718 var lengthInBytes = fs.Length;
719 var ptr = fs.GetUnsafePtr();
720
721 int index = lengthInBytes;
722 while (true)
723 {
724 var prev = index;
725 var error = Unicode.Utf8ToUcsReverse(out var rune, ptr, ref index, lengthInBytes);
726
727 var doTrim = false;
728 for (int i = 0, num = trimRunes.Length; i < num && !doTrim; i++)
729 {
730 doTrim |= trimRunes[i] == rune;
731 }
732
733 if (error != ConversionError.None
734 || !doTrim)
735 {
736 index += prev - index;
737 break;
738 }
739 }
740
741 return index;
742 }
743
744 /// <summary>
745 /// Removes whitespace characters from begining of the string.
746 /// </summary>
747 /// <typeparam name="T">A string type.</typeparam>
748 /// <param name="fs">A string to perform operation.</param>
749 /// <returns>Returns instance of this string with whitespace characters removed from the start of the string.</returns>
750 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
751 public static T TrimStart<T>(ref this T fs)
752 where T : unmanaged, INativeList<byte>, IUTF8Bytes
753 {
754 var index = fs.TrimStartIndex();
755 var result = new T();
756 result.Append(fs.GetUnsafePtr() + index, fs.Length - index);
757
758 return result;
759 }
760
761 /// <summary>
762 /// Removes whitespace characters from begining of the string.
763 /// </summary>
764 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
765 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
766 /// <returns>Returns instance of this string with whitespace characters removed from the start of the string.</returns>
767 public static UnsafeText TrimStart(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator)
768 {
769 var index = fs.TrimStartIndex();
770 var lengthInBytes = fs.Length - index;
771 var result = new UnsafeText(lengthInBytes, allocator);
772 result.Append(fs.GetUnsafePtr() + index, lengthInBytes);
773
774 return result;
775 }
776
777 /// <summary>
778 /// Removes whitespace characters from begining of the string.
779 /// </summary>
780 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
781 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
782 /// <returns>Returns instance of this string with whitespace characters removed from the start of the string.</returns>
783 public static NativeText TrimStart(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator)
784 {
785 var index = fs.TrimStartIndex();
786 var lengthInBytes = fs.Length - index;
787 var result = new NativeText(lengthInBytes, allocator);
788 result.Append(fs.GetUnsafePtr() + index, lengthInBytes);
789
790 return result;
791 }
792
793 /// <summary>
794 /// Removes specific characters from begining of the string.
795 /// </summary>
796 /// <typeparam name="T">A string type.</typeparam>
797 /// <param name="fs">A string to perform operation.</param>
798 /// <param name="trimRunes">Runes that should be trimmed.</param>
799 /// <returns>Returns instance of this string with specific characters removed from the start of the string.</returns>
800 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
801 public static T TrimStart<T>(ref this T fs, ReadOnlySpan<Unicode.Rune> trimRunes)
802 where T : unmanaged, INativeList<byte>, IUTF8Bytes
803 {
804 var index = fs.TrimStartIndex(trimRunes);
805 var result = new T();
806 result.Append(fs.GetUnsafePtr() + index, fs.Length - index);
807
808 return result;
809 }
810
811 /// <summary>
812 /// Removes specific characters characters from begining of the string.
813 /// </summary>
814 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
815 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
816 /// <param name="trimRunes">Runes that should be trimmed.</param>
817 /// <returns>Returns instance of this string with specific characters removed from the start of the string.</returns>
818 public static UnsafeText TrimStart(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
819 {
820 var index = fs.TrimStartIndex(trimRunes);
821 var lengthInBytes = fs.Length - index;
822 var result = new UnsafeText(lengthInBytes, allocator);
823 result.Append(fs.GetUnsafePtr() + index, lengthInBytes);
824
825 return result;
826 }
827
828 /// <summary>
829 /// Removes specific characters from begining of the string.
830 /// </summary>
831 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
832 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
833 /// <param name="trimRunes">Runes that should be trimmed.</param>
834 /// <returns>Returns instance of this string with specific characters removed from the start of the string.</returns>
835 public static NativeText TrimStart(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
836 {
837 var index = fs.TrimStartIndex(trimRunes);
838 var lengthInBytes = fs.Length - index;
839 var result = new NativeText(lengthInBytes, allocator);
840 result.Append(fs.GetUnsafePtr() + index, lengthInBytes);
841
842 return result;
843 }
844
845 /// <summary>
846 /// Removes whitespace characters from the end of the string.
847 /// </summary>
848 /// <typeparam name="T">A string type.</typeparam>
849 /// <param name="fs">A string to perform operation.</param>
850 /// <returns>Returns instance of this string with whitespace characters removed from the end of the string.</returns>
851 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
852 public static T TrimEnd<T>(ref this T fs)
853 where T : unmanaged, INativeList<byte>, IUTF8Bytes
854 {
855 var index = fs.TrimEndIndex();
856 var result = new T();
857 result.Append(fs.GetUnsafePtr(), index);
858
859 return result;
860 }
861
862 /// <summary>
863 /// Removes whitespace characters from the end of the string.
864 /// </summary>
865 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
866 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
867 /// <returns>Returns instance of this string with whitespace characters removed from the end of the string.</returns>
868 public static UnsafeText TrimEnd(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator)
869 {
870 var index = fs.TrimEndIndex();
871 var lengthInBytes = index;
872 var result = new UnsafeText(lengthInBytes, allocator);
873 result.Append(fs.GetUnsafePtr(), lengthInBytes);
874
875 return result;
876 }
877
878 /// <summary>
879 /// Removes whitespace characters from the end of the string.
880 /// </summary>
881 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
882 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
883 /// <returns>Returns instance of this string with whitespace characters removed from the end of the string.</returns>
884 public static NativeText TrimEnd(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator)
885 {
886 var index = fs.TrimEndIndex();
887 var lengthInBytes = index;
888 var result = new NativeText(lengthInBytes, allocator);
889 result.Append(fs.GetUnsafePtr(), lengthInBytes);
890
891 return result;
892 }
893
894 /// <summary>
895 /// Removes specific characters from the end of the string.
896 /// </summary>
897 /// <typeparam name="T">A string type.</typeparam>
898 /// <param name="fs">A string to perform operation.</param>
899 /// <param name="trimRunes">Runes that should be trimmed.</param>
900 /// <returns>Returns instance of this string with specific characters removed from the end of the string.</returns>
901 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
902 public static T TrimEnd<T>(ref this T fs, ReadOnlySpan<Unicode.Rune> trimRunes)
903 where T : unmanaged, INativeList<byte>, IUTF8Bytes
904 {
905 var index = fs.TrimEndIndex(trimRunes);
906 var result = new T();
907 result.Append(fs.GetUnsafePtr(), index);
908
909 return result;
910 }
911
912 /// <summary>
913 /// Removes specific characters from the end of the string.
914 /// </summary>
915 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
916 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
917 /// <param name="trimRunes">Runes that should be trimmed.</param>
918 /// <returns>Returns instance of this string with specific characters removed from the end of the string.</returns>
919 public static UnsafeText TrimEnd(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
920 {
921 var index = fs.TrimEndIndex(trimRunes);
922 var lengthInBytes = index;
923 var result = new UnsafeText(lengthInBytes, allocator);
924 result.Append(fs.GetUnsafePtr(), lengthInBytes);
925
926 return result;
927 }
928
929 /// <summary>
930 /// Removes specific characters from the end of the string.
931 /// </summary>
932 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
933 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
934 /// <param name="trimRunes">Runes that should be trimmed.</param>
935 /// <returns>Returns instance of this string with specific characters removed from the end of the string.</returns>
936 public static NativeText TrimEnd(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
937 {
938 var index = fs.TrimEndIndex(trimRunes);
939 var lengthInBytes = index;
940 var result = new NativeText(lengthInBytes, allocator);
941 result.Append(fs.GetUnsafePtr(), lengthInBytes);
942
943 return result;
944 }
945
946 /// <summary>
947 /// Removes whitespace characters from the begining and the end of the string.
948 /// </summary>
949 /// <typeparam name="T">A string type.</typeparam>
950 /// <param name="fs">A string to perform operation.</param>
951 /// <returns>Returns instance of this string with whitespace characters removed from the begining and the end of the string.</returns>
952 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
953 public static T Trim<T>(ref this T fs)
954 where T : unmanaged, INativeList<byte>, IUTF8Bytes
955 {
956 var start = fs.TrimStartIndex();
957 if (start == fs.Length)
958 {
959 return new T();
960 }
961
962 var end = fs.TrimEndIndex();
963 var result = new T();
964 result.Append(fs.GetUnsafePtr() + start, end - start);
965
966 return result;
967 }
968
969 /// <summary>
970 /// Removes whitespace characters from the begining and the end of the string.
971 /// </summary>
972 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
973 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
974 /// <returns>Returns instance of this string with whitespace characters removed from the begining and the end of the string.</returns>
975 public static UnsafeText Trim(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator)
976 {
977 var start = fs.TrimStartIndex();
978 if (start == fs.Length)
979 {
980 return new UnsafeText(0, allocator);
981 }
982
983 var end = fs.TrimEndIndex();
984 var lengthInBytes = end - start;
985 var result = new UnsafeText(lengthInBytes, allocator);
986 result.Append(fs.GetUnsafePtr() + start, lengthInBytes);
987
988 return result;
989 }
990
991 /// <summary>
992 /// Removes whitespace characters from the begining and the end of the string.
993 /// </summary>
994 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
995 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
996 /// <returns>Returns instance of this string with whitespace characters removed from the begining and the end of the string.</returns>
997 public static NativeText Trim(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator)
998 {
999 var start = fs.TrimStartIndex();
1000 if (start == fs.Length)
1001 {
1002 return new NativeText(0, allocator);
1003 }
1004
1005 var end = fs.TrimEndIndex();
1006 var lengthInBytes = end - start;
1007 var result = new NativeText(lengthInBytes, allocator);
1008 result.Append(fs.GetUnsafePtr() + start, lengthInBytes);
1009
1010 return result;
1011 }
1012
1013 /// <summary>
1014 /// Removes specific characters from the begining and the end of the string.
1015 /// </summary>
1016 /// <typeparam name="T">A string type.</typeparam>
1017 /// <param name="fs">A string to perform operation.</param>
1018 /// <param name="trimRunes">Runes that should be trimmed.</param>
1019 /// <returns>Returns instance of this string with specific characters removed from the begining and the end of the string.</returns>
1020 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
1021 public static T Trim<T>(ref this T fs, ReadOnlySpan<Unicode.Rune> trimRunes)
1022 where T : unmanaged, INativeList<byte>, IUTF8Bytes
1023 {
1024 var start = fs.TrimStartIndex(trimRunes);
1025 if (start == fs.Length)
1026 {
1027 return new T();
1028 }
1029
1030 var end = fs.TrimEndIndex(trimRunes);
1031 var result = new T();
1032 result.Append(fs.GetUnsafePtr() + start, end - start);
1033
1034 return result;
1035 }
1036
1037 /// <summary>
1038 /// Removes specific characters from the begining and the end of the string.
1039 /// </summary>
1040 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
1041 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1042 /// <param name="trimRunes">Runes that should be trimmed.</param>
1043 /// <returns>Returns instance of this string with specific characters removed from the begining and the end of the string.</returns>
1044 public static UnsafeText Trim(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
1045 {
1046 var start = fs.TrimStartIndex(trimRunes);
1047 if (start == fs.Length)
1048 {
1049 return new UnsafeText(0, allocator);
1050 }
1051
1052 var end = fs.TrimEndIndex();
1053 var lengthInBytes = end - start;
1054 var result = new UnsafeText(lengthInBytes, allocator);
1055 result.Append(fs.GetUnsafePtr() + start, lengthInBytes);
1056
1057 return result;
1058 }
1059
1060 /// <summary>
1061 /// Removes specific characters from the begining and the end of the string.
1062 /// </summary>
1063 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
1064 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1065 /// <param name="trimRunes">Runes that should be trimmed.</param>
1066 /// <returns>Returns instance of this string with specific characters removed from the begining and the end of the string.</returns>
1067 public static NativeText Trim(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan<Unicode.Rune> trimRunes)
1068 {
1069 var start = fs.TrimStartIndex(trimRunes);
1070 if (start == fs.Length)
1071 {
1072 return new NativeText(0, allocator);
1073 }
1074
1075 var end = fs.TrimEndIndex();
1076 var lengthInBytes = end - start;
1077 var result = new NativeText(lengthInBytes, allocator);
1078 result.Append(fs.GetUnsafePtr() + start, lengthInBytes);
1079
1080 return result;
1081 }
1082
1083 /// <summary>
1084 /// Converts string to lowercase only ASCII characters.
1085 /// </summary>
1086 /// <typeparam name="T">A string type.</typeparam>
1087 /// <param name="fs">A string to perform operation.</param>
1088 /// <returns>Returns a copy of this string converted to lowercase ASCII.</returns>
1089 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
1090 public static T ToLowerAscii<T>(ref this T fs)
1091 where T : unmanaged, INativeList<byte>, IUTF8Bytes
1092 {
1093 var lengthInBytes = fs.Length;
1094 var ptr = fs.GetUnsafePtr();
1095
1096 T result = new T();
1097
1098 Unicode.Rune rune;
1099 var error = ConversionError.None;
1100 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1101 {
1102 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1103 result.Append(rune.ToLowerAscii());
1104 }
1105
1106 return result;
1107 }
1108
1109 /// <summary>
1110 /// Converts string to lowercase only ASCII characters.
1111 /// </summary>
1112 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
1113 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1114 /// <returns>Returns a copy of this string converted to lowercase ASCII.</returns>
1115 public static UnsafeText ToLowerAscii(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator)
1116 {
1117 var lengthInBytes = fs.Length;
1118 var ptr = fs.GetUnsafePtr();
1119
1120 var result = new UnsafeText(lengthInBytes, allocator);
1121
1122 Unicode.Rune rune;
1123 var error = ConversionError.None;
1124 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1125 {
1126 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1127 result.Append(rune.ToLowerAscii());
1128 }
1129
1130 return result;
1131 }
1132
1133 /// <summary>
1134 /// Converts string to lowercase only ASCII characters.
1135 /// </summary>
1136 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
1137 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1138 /// <returns>Returns a copy of this string converted to lowercase ASCII.</returns>
1139 public static NativeText ToLowerAscii(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator)
1140 {
1141 var lengthInBytes = fs.Length;
1142 var ptr = fs.GetUnsafePtr();
1143
1144 var result = new NativeText(lengthInBytes, allocator);
1145
1146 Unicode.Rune rune;
1147 var error = ConversionError.None;
1148 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1149 {
1150 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1151 result.Append(rune.ToLowerAscii());
1152 }
1153
1154 return result;
1155 }
1156
1157 /// <summary>
1158 /// Converts string to uppercase only ASCII characters.
1159 /// </summary>
1160 /// <typeparam name="T">A string type.</typeparam>
1161 /// <param name="fs">A string to perform operation.</param>
1162 /// <returns>Returns a copy of this string converted to uppercase ASCII.</returns>
1163 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })]
1164 public static T ToUpperAscii<T>(ref this T fs)
1165 where T : unmanaged, INativeList<byte>, IUTF8Bytes
1166 {
1167 var lengthInBytes = fs.Length;
1168 var ptr = fs.GetUnsafePtr();
1169
1170 T result = new T();
1171
1172 Unicode.Rune rune;
1173 var error = ConversionError.None;
1174 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1175 {
1176 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1177 result.Append(rune.ToUpperAscii());
1178 }
1179
1180 return result;
1181 }
1182
1183 /// <summary>
1184 /// Converts string to uppercase only ASCII characters.
1185 /// </summary>
1186 /// <param name="fs">A <see cref="UnsafeText"/> string to perform operation.</param>
1187 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1188 /// <returns>Returns a copy of this string converted to uppercase ASCII.</returns>
1189 public static UnsafeText ToUpperAscii(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator)
1190 {
1191 var lengthInBytes = fs.Length;
1192 var ptr = fs.GetUnsafePtr();
1193
1194 var result = new UnsafeText(lengthInBytes, allocator);
1195
1196 Unicode.Rune rune;
1197 var error = ConversionError.None;
1198 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1199 {
1200 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1201 result.Append(rune.ToUpperAscii());
1202 }
1203
1204 return result;
1205 }
1206
1207 /// <summary>
1208 /// Converts string to uppercase only ASCII characters.
1209 /// </summary>
1210 /// <param name="fs">A <see cref="NativeText"/> string to perform operation.</param>
1211 /// <param name="allocator">The <see cref="AllocatorManager.AllocatorHandle"/> allocator type to use.</param>
1212 /// <returns>Returns a copy of this string converted to uppercase ASCII.</returns>
1213 public static NativeText ToUpperAscii(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator)
1214 {
1215 var lengthInBytes = fs.Length;
1216 var ptr = fs.GetUnsafePtr();
1217
1218 var result = new NativeText(lengthInBytes, allocator);
1219
1220 Unicode.Rune rune;
1221 var error = ConversionError.None;
1222 for (var i = 0; i < lengthInBytes && error == ConversionError.None;)
1223 {
1224 error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes);
1225 result.Append(rune.ToUpperAscii());
1226 }
1227
1228 return result;
1229 }
1230 }
1231}