A game about forced loneliness, made by TACStudios
at master 444 lines 22 kB view raw
1using Unity.Collections.LowLevel.Unsafe; 2 3namespace Unity.Collections 4{ 5 /// <summary> 6 /// Provides extension methods for FixedString*N*Bytes. 7 /// </summary> 8 [GenerateTestsForBurstCompatibility] 9 public unsafe static partial class FixedStringMethods 10 { 11 /// <summary> 12 /// Appends a Unicode.Rune to this string. 13 /// </summary> 14 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 15 /// <param name="fs">A FixedString*N*Bytes.</param> 16 /// <param name="rune">A Unicode.Rune to append.</param> 17 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 18 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 19 public static FormatError Append<T>(ref this T fs, Unicode.Rune rune) 20 where T : unmanaged, INativeList<byte>, IUTF8Bytes 21 { 22 var len = fs.Length; 23 var runeLen = rune.LengthInUtf8Bytes(); 24 if (!fs.TryResize(len + runeLen, NativeArrayOptions.UninitializedMemory)) 25 return FormatError.Overflow; 26 return fs.Write(ref len, rune); 27 } 28 29 /// <summary> 30 /// Appends a char to this string. 31 /// </summary> 32 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 33 /// <param name="fs">A FixedString*N*Bytes.</param> 34 /// <param name="ch">A char to append.</param> 35 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 36 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 37 public static FormatError Append<T>(ref this T fs, char ch) 38 where T : unmanaged, INativeList<byte>, IUTF8Bytes 39 { 40 return fs.Append((Unicode.Rune) ch); 41 } 42 43 /// <summary> 44 /// Appends a byte to this string. 45 /// </summary> 46 /// <remarks> 47 /// No validation is performed: it is your responsibility for the data to be valid UTF-8 when you're done appending bytes. 48 /// </remarks> 49 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 50 /// <param name="fs">A FixedString*N*Bytes.</param> 51 /// <param name="a">A byte to append.</param> 52 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 53 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 54 public static FormatError AppendRawByte<T>(ref this T fs, byte a) 55 where T : unmanaged, INativeList<byte>, IUTF8Bytes 56 { 57 var origLength = fs.Length; 58 if (!fs.TryResize(origLength + 1, NativeArrayOptions.UninitializedMemory)) 59 return FormatError.Overflow; 60 fs.GetUnsafePtr()[origLength] = a; 61 return FormatError.None; 62 } 63 64 /// <summary> 65 /// Appends a Unicode.Rune a number of times to this string. 66 /// </summary> 67 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 68 /// <param name="fs">A FixedString*N*Bytes.</param> 69 /// <param name="rune">A Unicode.Rune to append some number of times.</param> 70 /// <param name="count">The number of times to append the rune.</param> 71 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 72 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 73 public static FormatError Append<T>(ref this T fs, Unicode.Rune rune, int count) 74 where T : unmanaged, INativeList<byte>, IUTF8Bytes 75 { 76 var origLength = fs.Length; 77 78 if (!fs.TryResize(origLength + rune.LengthInUtf8Bytes() * count, NativeArrayOptions.UninitializedMemory)) 79 return FormatError.Overflow; 80 81 var cap = fs.Capacity; 82 var b = fs.GetUnsafePtr(); 83 int offset = origLength; 84 for (int i = 0; i < count; ++i) 85 { 86 var error = Unicode.UcsToUtf8(b, ref offset, cap, rune); 87 if (error != ConversionError.None) 88 return FormatError.Overflow; 89 } 90 91 return FormatError.None; 92 } 93 94 /// <summary> 95 /// Appends a number (converted to UTF-8 characters) to this string. 96 /// </summary> 97 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 98 /// <param name="fs">A FixedString*N*Bytes.</param> 99 /// <param name="input">A long integer to append to the string.</param> 100 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 101 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 102 public static FormatError Append<T>(ref this T fs, long input) 103 where T : unmanaged, INativeList<byte>, IUTF8Bytes 104 { 105 const int maximumDigits = 20; 106 var temp = stackalloc byte[maximumDigits]; 107 int offset = maximumDigits; 108 if (input >= 0) 109 { 110 do 111 { 112 var digit = (byte)(input % 10); 113 temp[--offset] = (byte)('0' + digit); 114 input /= 10; 115 } 116 while (input != 0); 117 } 118 else 119 { 120 do 121 { 122 var digit = (byte)(input % 10); 123 temp[--offset] = (byte)('0' - digit); 124 input /= 10; 125 } 126 while (input != 0); 127 temp[--offset] = (byte)'-'; 128 } 129 130 return fs.Append(temp + offset, maximumDigits - offset); 131 } 132 133 /// <summary> 134 /// Appends a number (converted to UTF-8 characters) to this string. 135 /// </summary> 136 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 137 /// <param name="fs">A FixedString*N*Bytes.</param> 138 /// <param name="input">An int to append to the string.</param> 139 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 140 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 141 public static FormatError Append<T>(ref this T fs, int input) 142 where T : unmanaged, INativeList<byte>, IUTF8Bytes 143 { 144 return fs.Append((long)input); 145 } 146 147 /// <summary> 148 /// Appends a number (converted to UTF-8 characters) to this string. 149 /// </summary> 150 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 151 /// <param name="fs">A FixedString*N*Bytes.</param> 152 /// <param name="input">A ulong integer to append to the string.</param> 153 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 154 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 155 public static FormatError Append<T>(ref this T fs, ulong input) 156 where T : unmanaged, INativeList<byte>, IUTF8Bytes 157 { 158 const int maximumDigits = 20; 159 var temp = stackalloc byte[maximumDigits]; 160 int offset = maximumDigits; 161 do 162 { 163 var digit = (byte)(input % 10); 164 temp[--offset] = (byte)('0' + digit); 165 input /= 10; 166 } 167 while (input != 0); 168 169 return fs.Append(temp + offset, maximumDigits - offset); 170 } 171 172 /// <summary> 173 /// Appends a number (converted to UTF-8 characters) to this string. 174 /// </summary> 175 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 176 /// <param name="fs">A FixedString*N*Bytes.</param> 177 /// <param name="input">A uint to append to the string.</param> 178 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 179 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 180 public static FormatError Append<T>(ref this T fs, uint input) 181 where T : unmanaged, INativeList<byte>, IUTF8Bytes 182 { 183 return fs.Append((ulong)input); 184 } 185 186 /// <summary> 187 /// Appends a number (converted to UTF-8 characters) to this string. 188 /// </summary> 189 /// <typeparam name="T">The type of FixedString*N*Bytes.</typeparam> 190 /// <param name="fs">A FixedString*N*Bytes.</param> 191 /// <param name="input">A float to append to the string.</param> 192 /// <param name="decimalSeparator">The character to use as the decimal separator. Defaults to a period ('.').</param> 193 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the string is exceeded.</returns> 194 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 195 public static FormatError Append<T>(ref this T fs, float input, char decimalSeparator = '.') 196 where T : unmanaged, INativeList<byte>, IUTF8Bytes 197 { 198 FixedStringUtils.UintFloatUnion ufu = new FixedStringUtils.UintFloatUnion(); 199 ufu.floatValue = input; 200 var sign = ufu.uintValue >> 31; 201 ufu.uintValue &= ~(1 << 31); 202 FormatError error; 203 if ((ufu.uintValue & 0x7F800000) == 0x7F800000) 204 { 205 if (ufu.uintValue == 0x7F800000) 206 { 207 if (sign != 0 && ((error = fs.Append('-')) != FormatError.None)) 208 return error; 209 return fs.Append('I', 'n', 'f', 'i', 'n', 'i', 't', 'y'); 210 } 211 return fs.Append('N', 'a', 'N'); 212 } 213 if (sign != 0 && ufu.uintValue != 0) // C# prints -0 as 0 214 if ((error = fs.Append('-')) != FormatError.None) 215 return error; 216 ulong decimalMantissa = 0; 217 int decimalExponent = 0; 218 FixedStringUtils.Base2ToBase10(ref decimalMantissa, ref decimalExponent, ufu.floatValue); 219 var backwards = stackalloc char[9]; 220 int decimalDigits = 0; 221 do 222 { 223 if (decimalDigits >= 9) 224 return FormatError.Overflow; 225 var decimalDigit = decimalMantissa % 10; 226 backwards[8 - decimalDigits++] = (char)('0' + decimalDigit); 227 decimalMantissa /= 10; 228 } 229 while (decimalMantissa > 0); 230 char *ascii = backwards + 9 - decimalDigits; 231 var leadingZeroes = -decimalExponent - decimalDigits + 1; 232 if (leadingZeroes > 0) 233 { 234 if (leadingZeroes > 4) 235 return fs.AppendScientific(ascii, decimalDigits, decimalExponent, decimalSeparator); 236 if ((error = fs.Append('0', decimalSeparator)) != FormatError.None) 237 return error; 238 --leadingZeroes; 239 while (leadingZeroes > 0) 240 { 241 if ((error = fs.Append('0')) != FormatError.None) 242 return error; 243 --leadingZeroes; 244 } 245 for (var i = 0; i < decimalDigits; ++i) 246 { 247 if ((error = fs.Append(ascii[i])) != FormatError.None) 248 return error; 249 } 250 return FormatError.None; 251 } 252 var trailingZeroes = decimalExponent; 253 if (trailingZeroes > 0) 254 { 255 if (trailingZeroes > 4) 256 return fs.AppendScientific(ascii, decimalDigits, decimalExponent, decimalSeparator); 257 for (var i = 0; i < decimalDigits; ++i) 258 { 259 if ((error = fs.Append(ascii[i])) != FormatError.None) 260 return error; 261 } 262 while (trailingZeroes > 0) 263 { 264 if ((error = fs.Append('0')) != FormatError.None) 265 return error; 266 --trailingZeroes; 267 } 268 return FormatError.None; 269 } 270 var indexOfSeparator = decimalDigits + decimalExponent; 271 for (var i = 0; i < decimalDigits; ++i) 272 { 273 if (i == indexOfSeparator) 274 if ((error = fs.Append(decimalSeparator)) != FormatError.None) 275 return error; 276 if ((error = fs.Append(ascii[i])) != FormatError.None) 277 return error; 278 } 279 return FormatError.None; 280 } 281 282 /// <summary> 283 /// Appends another string to this string. 284 /// </summary> 285 /// <remarks> 286 /// When the method returns an error, the destination string is not modified. 287 /// </remarks> 288 /// <typeparam name="T">The type of the destination string.</typeparam> 289 /// <typeparam name="T2">The type of the source string.</typeparam> 290 /// <param name="fs">The destination string.</param> 291 /// <param name="input">The source string.</param> 292 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the destination string is exceeded.</returns> 293 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] 294 public static FormatError Append<T,T2>(ref this T fs, in T2 input) 295 where T : unmanaged, INativeList<byte>, IUTF8Bytes 296 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes 297 { 298 ref var inputRef = ref UnsafeUtilityExtensions.AsRef(input); 299 return fs.Append(inputRef.GetUnsafePtr(), inputRef.Length); 300 } 301 302 /// <summary> 303 /// Copies another string to this string (making the two strings equal). 304 /// </summary> 305 /// <remarks> 306 /// When the method returns an error, the destination string is not modified. 307 /// </remarks> 308 /// <typeparam name="T">The type of the destination string.</typeparam> 309 /// <typeparam name="T2">The type of the source string.</typeparam> 310 /// <param name="fs">The destination string.</param> 311 /// <param name="input">The source string.</param> 312 /// <returns>CopyError.None if successful. Returns CopyError.Truncation if the source string is too large to fit in the destination.</returns> 313 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] 314 public static CopyError CopyFrom<T, T2>(ref this T fs, in T2 input) 315 where T : unmanaged, INativeList<byte>, IUTF8Bytes 316 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes 317 { 318 fs.Length = 0; 319 var fe = Append(ref fs, input); 320 if (fe != FormatError.None) 321 return CopyError.Truncation; 322 return CopyError.None; 323 } 324 325 /// <summary> 326 /// Appends bytes to this string. 327 /// </summary> 328 /// <remarks> 329 /// When the method returns an error, the destination string is not modified. 330 /// 331 /// No validation is performed: it is your responsibility for the destination to contain valid UTF-8 when you're done appending bytes. 332 /// </remarks> 333 /// <typeparam name="T">The type of the destination string.</typeparam> 334 /// <param name="fs">The destination string.</param> 335 /// <param name="utf8Bytes">The bytes to append.</param> 336 /// <param name="utf8BytesLength">The number of bytes to append.</param> 337 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the destination string is exceeded.</returns> 338 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] 339 public unsafe static FormatError Append<T>(ref this T fs, byte* utf8Bytes, int utf8BytesLength) 340 where T : unmanaged, INativeList<byte>, IUTF8Bytes 341 { 342 var origLength = fs.Length; 343 if (!fs.TryResize(origLength + utf8BytesLength, NativeArrayOptions.UninitializedMemory)) 344 return FormatError.Overflow; 345 UnsafeUtility.MemCpy(fs.GetUnsafePtr() + origLength, utf8Bytes, utf8BytesLength); 346 return FormatError.None; 347 } 348 349 /// <summary> 350 /// Appends another string to this string. 351 /// </summary> 352 /// <remarks> 353 /// When the method returns an error, the destination string is not modified. 354 /// </remarks> 355 /// <typeparam name="T">The type of the destination string.</typeparam> 356 /// <param name="fs">The destination string.</param> 357 /// <param name="s">The string to append.</param> 358 /// <returns>FormatError.None if successful. Returns FormatError.Overflow if the capacity of the destination string is exceeded.</returns> 359 [ExcludeFromBurstCompatTesting("Takes managed string")] 360 public unsafe static FormatError Append<T>(ref this T fs, string s) 361 where T : unmanaged, INativeList<byte>, IUTF8Bytes 362 { 363 // we don't know how big the expansion from UTF16 to UTF8 will be, so we account for worst case. 364 int worstCaseCapacity = s.Length * 4; 365 byte* utf8Bytes = stackalloc byte[worstCaseCapacity]; 366 int utf8Len; 367 368 fixed (char* chars = s) 369 { 370 var err = UTF8ArrayUnsafeUtility.Copy(utf8Bytes, out utf8Len, worstCaseCapacity, chars, s.Length); 371 if (err != CopyError.None) 372 { 373 return FormatError.Overflow; 374 } 375 } 376 377 return fs.Append(utf8Bytes, utf8Len); 378 } 379 380 /// <summary> 381 /// Copies another string to this string (making the two strings equal). 382 /// Replaces any existing content of the FixedString. 383 /// </summary> 384 /// <remarks> 385 /// When the method returns an error, the destination string is not modified. 386 /// </remarks> 387 /// <typeparam name="T">The type of the destination string.</typeparam> 388 /// <param name="fs">The destination string.</param> 389 /// <param name="s">The source string.</param> 390 /// <returns>CopyError.None if successful. Returns CopyError.Truncation if the source string is too large to fit in the destination.</returns> 391 [ExcludeFromBurstCompatTesting("Takes managed string")] 392 public static CopyError CopyFrom<T>(ref this T fs, string s) 393 where T : unmanaged, INativeList<byte>, IUTF8Bytes 394 { 395 fs.Length = 0; 396 var fe = Append(ref fs, s); 397 if (fe != FormatError.None) 398 return CopyError.Truncation; 399 return CopyError.None; 400 } 401 402 /// <summary> 403 /// Copies another string to this string. If the string exceeds the capacity it will be truncated. 404 /// Replaces any existing content of the FixedString. 405 /// </summary> 406 /// <typeparam name="T">The type of the destination string.</typeparam> 407 /// <param name="fs">The destination string.</param> 408 /// <param name="s">The source string.</param> 409 /// <returns>CopyError.None if successful. Returns CopyError.Truncation if the source string is too large to fit in the destination.</returns> 410 [ExcludeFromBurstCompatTesting("Takes managed string")] 411 public static CopyError CopyFromTruncated<T>(ref this T fs, string s) 412 where T : unmanaged, INativeList<byte>, IUTF8Bytes 413 { 414 int utf8Len; 415 fixed (char* chars = s) 416 { 417 var error = UTF8ArrayUnsafeUtility.Copy(fs.GetUnsafePtr(), out utf8Len, fs.Capacity, chars, s.Length); 418 fs.Length = utf8Len; 419 return error; 420 } 421 } 422 423 /// <summary> 424 /// Copies another string to this string. If the string exceeds the capacity it will be truncated. 425 /// </summary> 426 /// <remarks> 427 /// When the method returns an error, the destination string is not modified. 428 /// </remarks> 429 /// <typeparam name="T">The type of the destination string.</typeparam> 430 /// <typeparam name="T2">The type of the source string.</typeparam> 431 /// <param name="fs">The destination string.</param> 432 /// <param name="input">The source string.</param> 433 /// <returns>CopyError.None if successful. Returns CopyError.Truncation if the source string is too large to fit in the destination.</returns> 434 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] 435 public static CopyError CopyFromTruncated<T, T2>(ref this T fs, in T2 input) 436 where T : unmanaged, INativeList<byte>, IUTF8Bytes 437 where T2 : unmanaged, INativeList<byte>, IUTF8Bytes 438 { 439 var error = UTF8ArrayUnsafeUtility.Copy(fs.GetUnsafePtr(), out int utf8Len, fs.Capacity, input.GetUnsafePtr(), input.Length); 440 fs.Length = utf8Len; 441 return error; 442 } 443 } 444}