diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs new file mode 100644 index 0000000..c8a9a0e --- /dev/null +++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs @@ -0,0 +1,239 @@ +#if ZSTRING_COLLECTIONS_SUPPORT +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Text; +using Unity.Collections; + +namespace Cysharp.Text +{ + partial struct Utf16ValueStringBuilder + { + private static readonly Encoding UTF8NoBom = new UTF8Encoding(false); + + /// + /// Get the written buffer data as a . + /// + /// If the byte length of the buffer exceeds 29 bytes. + public FixedString32Bytes AsFixedString32Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString32Bytes(); + } + + int byteCount = GetUtf8ByteCount(in buffer, in index); + + if (byteCount > FixedString32Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString32Bytes ({FixedString32Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in byteCount)) + { + return new FixedString32Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the byte length of the buffer exceeds 61 bytes. + public FixedString64Bytes AsFixedString64Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString64Bytes(); + } + + int byteCount = GetUtf8ByteCount(in buffer, in index); + + if (byteCount > FixedString64Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString64Bytes ({FixedString64Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in byteCount)) + { + return new FixedString64Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the byte length of the buffer exceeds 125 bytes. + public FixedString128Bytes AsFixedString128Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString128Bytes(); + } + + int byteCount = GetUtf8ByteCount(in buffer, in index); + + if (byteCount > FixedString128Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString128Bytes ({FixedString128Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in byteCount)) + { + return new FixedString128Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the byte length of the buffer exceeds 509 bytes. + public FixedString512Bytes AsFixedString512Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString512Bytes(); + } + + int byteCount = GetUtf8ByteCount(in buffer, in index); + + if (byteCount > FixedString512Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString512Bytes ({FixedString512Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in byteCount)) + { + return new FixedString512Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the byte length of the buffer exceeds 4093 bytes. + public FixedString4096Bytes AsFixedString4096Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString4096Bytes(); + } + + int byteCount = GetUtf8ByteCount(in buffer, in index); + + if (byteCount > FixedString4096Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString4096Bytes ({FixedString4096Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in byteCount)) + { + return new FixedString4096Bytes(text.AsReadOnly()); + } + } + + /// + /// Helper method to copy a array buffer to + /// + /// The current buffer. + /// The amount of bytes this buffer requires. + /// with the written to it. + private static NativeText CopyToNativeText(in char[] buffer, in int byteCount) + { + byte[]? destinationArray = ArrayPool.Shared.Rent(byteCount); + Span destination = destinationArray.AsSpan(0, byteCount); + int writtenBytes = UTF8NoBom.GetBytes(buffer, destination); + + NativeText text = new NativeText(writtenBytes, Allocator.Temp); + for (int i = 0; i < writtenBytes; i++) + { + text.Add(destination[i]); + } + + ArrayPool.Shared.Return(destinationArray); + + return text; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetUtf8ByteCount(in char[] buffer, in int length) + { + return UTF8NoBom.GetByteCount(buffer, 0, length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString32Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString64Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString128Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString512Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString4096Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Helper method to append a fixed string to the builder. + /// + /// The fixed string. + /// The length of the string byte buffer. + /// The type of FixedString. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AppendFixedString(T value, int length) where T : unmanaged, IUTF8Bytes + { + unsafe + { + byte* bytes = value.GetUnsafePtr(); + ReadOnlySpan span = new ReadOnlySpan(bytes, length); + + int charCount = UTF8NoBom.GetCharCount(span); + + char[]? charBuffer = ArrayPool.Shared.Rent(charCount); + int written = UTF8NoBom.GetChars(span, charBuffer); + + Append(charBuffer.AsSpan(0, written)); + + ArrayPool.Shared.Return(charBuffer); + } + } + } +} +#endif diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs.meta b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs.meta new file mode 100644 index 0000000..95642f1 --- /dev/null +++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.UnityCollections.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8460f7c414464d7493c8212d7d7520ee +timeCreated: 1760212117 \ No newline at end of file diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs new file mode 100644 index 0000000..726d83d --- /dev/null +++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs @@ -0,0 +1,204 @@ +#if ZSTRING_COLLECTIONS_SUPPORT +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; + +namespace Cysharp.Text +{ + partial struct Utf8ValueStringBuilder + { + /// + /// Get the written buffer data as a . + /// + /// If the of the buffer exceeds 29 bytes. + public FixedString32Bytes AsFixedString32Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString32Bytes(); + } + + if (Length > FixedString32Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString32Bytes ({FixedString32Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in index)) + { + return new FixedString32Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the of the buffer exceeds 61 bytes. + public FixedString64Bytes AsFixedString64Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString64Bytes(); + } + + if (Length > FixedString64Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString64Bytes ({FixedString64Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in index)) + { + return new FixedString64Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the of the buffer exceeds 125 bytes. + public FixedString128Bytes AsFixedString128Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString128Bytes(); + } + + if (Length > FixedString128Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString128Bytes ({FixedString128Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in index)) + { + return new FixedString128Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the of the buffer exceeds 509 bytes. + public FixedString512Bytes AsFixedString512Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString512Bytes(); + } + + if (Length > FixedString512Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString512Bytes ({FixedString512Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in index)) + { + return new FixedString512Bytes(text.AsReadOnly()); + } + } + + /// + /// Get the written buffer data as a . + /// + /// If the of the buffer exceeds 4093 bytes. + public FixedString4096Bytes AsFixedString4096Bytes() + { + if (buffer == null || Length == 0) + { + return new FixedString4096Bytes(); + } + + if (Length > FixedString4096Bytes.UTF8MaxLengthInBytes) + { + throw new InvalidOperationException( + $"The current length ({Length}) exceeds the maximum length of FixedString4096Bytes ({FixedString4096Bytes.UTF8MaxLengthInBytes})."); + } + + using (NativeText text = CopyToNativeText(in buffer, in index)) + { + return new FixedString4096Bytes(text.AsReadOnly()); + } + } + + /// + /// Helper method to copy a array buffer to + /// + /// The current buffer. + /// The length of written elements in the buffer. + /// with the written to it. + private static NativeText CopyToNativeText(in byte[] buffer, in int length) + { + NativeText text = new NativeText(length, Allocator.Temp); + for (int i = 0; i < length; i++) + { + text.Add(buffer[i]); + } + + return text; + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString32Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString64Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString128Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString512Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Append a to the builder. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(FixedString4096Bytes value) + { + AppendFixedString(value, value.Length); + } + + /// + /// Helper method to append a fixed string to the builder. + /// + /// The fixed string. + /// The length of the string byte buffer. + /// The type of FixedString. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AppendFixedString(T value, int length) where T : unmanaged, IUTF8Bytes + { + unsafe + { + byte* bytes = value.GetUnsafePtr(); + AppendLiteral(new ReadOnlySpan(bytes, length)); + } + } + } +} +#endif diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs.meta b/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs.meta new file mode 100644 index 0000000..4aa42c8 --- /dev/null +++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf8/Utf8ValueStringBuilder.UnityCollections.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2c1cf0f17b4941be9dec84565c8a644a +timeCreated: 1760210314 \ No newline at end of file diff --git a/src/ZString.Unity/Assets/Scripts/ZString/ZString.asmdef b/src/ZString.Unity/Assets/Scripts/ZString/ZString.asmdef index 40c88be..65dee81 100644 --- a/src/ZString.Unity/Assets/Scripts/ZString/ZString.asmdef +++ b/src/ZString.Unity/Assets/Scripts/ZString/ZString.asmdef @@ -1,7 +1,8 @@ { "name": "ZString", "references": [ - "Unity.TextMeshPro" + "Unity.TextMeshPro", + "Unity.Collections" ], "includePlatforms": [], "excludePlatforms": [], @@ -20,6 +21,11 @@ "name": "com.unity.ugui", "expression": "2.0.0", "define": "ZSTRING_TEXTMESHPRO_SUPPORT" + }, + { + "name": "com.unity.collections", + "expression": "2.0.0", + "define": "ZSTRING_COLLECTIONS_SUPPORT" } ] } \ No newline at end of file