diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f93be6..caef2ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ -# Changelog +## [2.4.0-pre.5] - 2024-02-13 + +### Changed + +* Updated Burst dependency to version 1.8.12 + +### Fixed + +* Memory leaks in unit tests. +* Memory leak when using `NativeBitArrayUnsafeUtility.ConvertExistingDataToNativeBitArray`. +* Brace parsing in FixedString AppendFormat handles incorrect formatting now + +### Updated + +* Upgraded Test Framework version to 1.4.3 + + ## [2.4.0-pre.2] - 2023-11-28 diff --git a/LICENSE.md b/LICENSE.md index 042cb72..6585a2d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -com.unity.collections copyright © 2023 Unity Technologies +com.unity.collections copyright © 2024 Unity Technologies Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license). diff --git a/Unity.Collections.PerformanceTests/QueueParallelPerformanceTests.cs b/Unity.Collections.PerformanceTests/QueueParallelPerformanceTests.cs index dfbc868..50dfd1e 100644 --- a/Unity.Collections.PerformanceTests/QueueParallelPerformanceTests.cs +++ b/Unity.Collections.PerformanceTests/QueueParallelPerformanceTests.cs @@ -41,7 +41,7 @@ void IBenchmarkContainerParallel.SetParams(int capacity, params int[] args) workers = args[0]; } - public void AllocNativeContainer(int capacity) => QueueParallelUtil.AllocInt(ref nativeContainer, 0, false); + public void AllocNativeContainer(int capacity) => QueueParallelUtil.AllocInt(ref nativeContainer, capacity >= 0 ? 0 : -1, false); public void AllocUnsafeContainer(int capacity) { } public object AllocBclContainer(int capacity) => QueueParallelUtil.AllocBclContainer(0, false); diff --git a/Unity.Collections.PerformanceTests/QueuePerformanceTests.cs b/Unity.Collections.PerformanceTests/QueuePerformanceTests.cs index 739e88a..e6babee 100644 --- a/Unity.Collections.PerformanceTests/QueuePerformanceTests.cs +++ b/Unity.Collections.PerformanceTests/QueuePerformanceTests.cs @@ -168,9 +168,9 @@ struct QueueEnqueueGrow : IBenchmarkContainer void IBenchmarkContainer.SetParams(int capacity, params int[] args) => this.capacity = capacity; - public void AllocNativeContainer(int capacity) => QueueUtil.AllocInt(ref nativeContainer, 0, false); - public void AllocUnsafeContainer(int capacity) => QueueUtil.AllocInt(ref unsafeContainer, 0, false); - public object AllocBclContainer(int capacity) => QueueUtil.AllocBclContainer(0, false); + public void AllocNativeContainer(int capacity) => QueueUtil.AllocInt(ref nativeContainer, capacity >= 0 ? 0 : -1, false); + public void AllocUnsafeContainer(int capacity) => QueueUtil.AllocInt(ref unsafeContainer, capacity >= 0 ? 0 : -1, false); + public object AllocBclContainer(int capacity) => QueueUtil.AllocBclContainer(capacity >= 0 ? 0 : -1, false); public void MeasureNativeContainer() { diff --git a/Unity.Collections.Tests/AllocatorCustomTests.cs b/Unity.Collections.Tests/AllocatorCustomTests.cs index 56e95fb..5d7b68e 100644 --- a/Unity.Collections.Tests/AllocatorCustomTests.cs +++ b/Unity.Collections.Tests/AllocatorCustomTests.cs @@ -345,7 +345,7 @@ public void CustomAllocatorHandle_MultiThreadWorks() Task.WaitAll(taskList.ToArray()); - exampleStruct.customAllocator.Dispose(); + exampleStruct.Dispose(); } } #endregion // allocator-custom-user-struct diff --git a/Unity.Collections.Tests/FixedStringTests.cs b/Unity.Collections.Tests/FixedStringTests.cs index 6bee4cb..04a0f60 100644 --- a/Unity.Collections.Tests/FixedStringTests.cs +++ b/Unity.Collections.Tests/FixedStringTests.cs @@ -46,7 +46,7 @@ public void FixedStringNFormatExtension1Params() aa.Junk(); FixedStringN format = "{0}"; FixedString32Bytes arg0 = "a"; - aa.AppendFormat(format, arg0); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0)); Assert.AreEqual("a", aa); aa.AssertNullTerminated(); } @@ -60,7 +60,7 @@ public void FixedStringNFormatExtension2Params() FixedStringN format = "{0} {1}"; FixedString32Bytes arg0 = "a"; FixedString32Bytes arg1 = "b"; - aa.AppendFormat(format, arg0, arg1); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1)); Assert.AreEqual("a b", aa); aa.AssertNullTerminated(); } @@ -75,7 +75,7 @@ public void FixedStringNFormatExtension3Params() FixedString32Bytes arg0 = "a"; FixedString32Bytes arg1 = "b"; FixedString32Bytes arg2 = "c"; - aa.AppendFormat(format, arg0, arg1, arg2); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2)); Assert.AreEqual("a b c", aa); aa.AssertNullTerminated(); } @@ -84,14 +84,14 @@ public void FixedStringNFormatExtension3Params() [Test] public void FixedStringNFormatExtension4Params() { - FixedStringN aa = default; + FixedStringN aa = default; aa.Junk(); FixedStringN format = "{0} {1} {2} {3}"; FixedString32Bytes arg0 = "a"; FixedString32Bytes arg1 = "b"; FixedString32Bytes arg2 = "c"; FixedString32Bytes arg3 = "d"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3)); Assert.AreEqual("a b c d", aa); aa.AssertNullTerminated(); } @@ -108,7 +108,7 @@ public void FixedStringNFormatExtension5Params() FixedString32Bytes arg2 = "c"; FixedString32Bytes arg3 = "d"; FixedString32Bytes arg4 = "e"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4)); Assert.AreEqual("a b c d e", aa); aa.AssertNullTerminated(); } @@ -126,7 +126,7 @@ public void FixedStringNFormatExtension6Params() FixedString32Bytes arg3 = "d"; FixedString32Bytes arg4 = "e"; FixedString32Bytes arg5 = "f"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5)); Assert.AreEqual("a b c d e f", aa); aa.AssertNullTerminated(); } @@ -145,7 +145,7 @@ public void FixedStringNFormatExtension7Params() FixedString32Bytes arg4 = "e"; FixedString32Bytes arg5 = "f"; FixedString32Bytes arg6 = "g"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6)); Assert.AreEqual("a b c d e f g", aa); aa.AssertNullTerminated(); } @@ -165,7 +165,7 @@ public void FixedStringNFormatExtension8Params() FixedString32Bytes arg5 = "f"; FixedString32Bytes arg6 = "g"; FixedString32Bytes arg7 = "h"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)); Assert.AreEqual("a b c d e f g h", aa); aa.AssertNullTerminated(); } @@ -186,7 +186,7 @@ public void FixedStringNFormatExtension9Params() FixedString32Bytes arg6 = "g"; FixedString32Bytes arg7 = "h"; FixedString32Bytes arg8 = "i"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); Assert.AreEqual("a b c d e f g h i", aa); aa.AssertNullTerminated(); } @@ -208,11 +208,87 @@ public void FixedStringNFormatExtension10Params() FixedString32Bytes arg7 = "h"; FixedString32Bytes arg8 = "i"; FixedString32Bytes arg9 = "j"; - aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); Assert.AreEqual("a b c d e f g h i j", aa); aa.AssertNullTerminated(); } + [Test] + public void FixedStringNFormatBadFormat() + { + FixedStringN aa = default; + aa.Junk(); + FixedStringN format = "{10}"; + FixedString32Bytes arg0 = "a"; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0 } "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{ 0} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0a} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{012 "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0{ "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0{ "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0} } "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{ {0} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{{{0}} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{{0} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + format = "{0}} "; + Assert.AreEqual(FormatError.BadFormatSpecifier, aa.AppendFormat(format, arg0)); + aa.AssertNullTerminated(); + } + + [Test] + public void FixedStringNFormatOverflow() + { + FixedString32Bytes aa = default; + aa.Junk(); + FixedStringN format = "{0}"; + FixedStringN arg0 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + Assert.AreEqual(FormatError.Overflow, aa.AppendFormat(format, arg0)); + } + + [Test] + public void FixedStringNFormatBraces() + { + FixedStringN aa = default; + aa.Junk(); + FixedStringN format = "{{0}}"; + FixedString32Bytes arg0 = "42"; + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0)); + Assert.AreEqual("{0}", aa); + aa.AssertNullTerminated(); + + aa = default; + format = "{{{0}}}"; + arg0 = "43"; + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0)); + Assert.AreEqual("{43}", aa); + aa.AssertNullTerminated(); + + aa = default; + format = "{{{0}"; + arg0 = "44"; + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0)); + Assert.AreEqual("{44", aa); + aa.AssertNullTerminated(); + + aa = default; + format = "{0}}}"; + arg0 = "45"; + Assert.AreEqual(FormatError.None, aa.AppendFormat(format, arg0)); + Assert.AreEqual("45}", aa); + aa.AssertNullTerminated(); + } + [Test] public void FixedStringNAppendString() { diff --git a/Unity.Collections.Tests/GenericContainerTest.cs b/Unity.Collections.Tests/GenericContainerTest.cs index 4c73526..6c7eb5a 100644 --- a/Unity.Collections.Tests/GenericContainerTest.cs +++ b/Unity.Collections.Tests/GenericContainerTest.cs @@ -494,7 +494,9 @@ void Test_Change_Length_Missing_Dependency(T container) [TestRequiresCollectionChecks()] public void IIndexable_Change_Length_Missing_Dependency() { - Test_Change_Length_Missing_Dependency, int>(new NativeList(16, Allocator.Persistent)); + var container = new NativeList(16, Allocator.Persistent); + Test_Change_Length_Missing_Dependency, int>(container); + container.Dispose(); } #endif //------------------------------------------------------------------------------------------------------- diff --git a/Unity.Collections.Tests/NativeRingQueueTests.cs b/Unity.Collections.Tests/NativeRingQueueTests.cs index aa254a4..a9f8444 100644 --- a/Unity.Collections.Tests/NativeRingQueueTests.cs +++ b/Unity.Collections.Tests/NativeRingQueueTests.cs @@ -136,5 +136,7 @@ public void NativeRingQueue_UseInJob() Assert.AreEqual(987, container.Dequeue()); Assert.AreEqual(0, container.Length); + + container.Dispose(); } } diff --git a/Unity.Collections.Tests/NativeTextTests.cs b/Unity.Collections.Tests/NativeTextTests.cs index 84e68cd..8d5daed 100644 --- a/Unity.Collections.Tests/NativeTextTests.cs +++ b/Unity.Collections.Tests/NativeTextTests.cs @@ -843,6 +843,7 @@ public void NativeTextReadOnlyCannotScheduledSourceTextForWrite() }); handle.Complete(); + a.Dispose(); Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); @@ -875,6 +876,8 @@ public void NativeTextReadOnlyCanReadFromSourceTextModifiedInJob() UnityEngine.Debug.Log(ro.ToString()); }); + a.Dispose(); + Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); } @@ -951,6 +954,9 @@ public void NativeTextReadOnlyThrowWhenUsingReadOnlyInJobAfterSourceHasBeenDispo a.Dispose(); }); + handle.Complete(); + a.Dispose(); + Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); } diff --git a/Unity.Collections.Tests/UnsafeListTests.cs b/Unity.Collections.Tests/UnsafeListTests.cs index be6d580..8b413df 100644 --- a/Unity.Collections.Tests/UnsafeListTests.cs +++ b/Unity.Collections.Tests/UnsafeListTests.cs @@ -186,37 +186,39 @@ public void UnsafeListT_AddReplicate() [Test] public unsafe void UnsafeListT_AddNoResize() { - var list = new UnsafeList(1, Allocator.Persistent, NativeArrayOptions.ClearMemory); + var container = new UnsafeList(1, Allocator.Persistent, NativeArrayOptions.ClearMemory); // List's capacity is always cache-line aligned, number of items fills up whole cache-line. int[] range = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - Assert.Throws(() => { fixed (int* r = range) list.AddRangeNoResize(r, 17); }); + Assert.Throws(() => { fixed (int* r = range) container.AddRangeNoResize(r, 17); }); #endif - list.SetCapacity(17); - Assert.DoesNotThrow(() => { fixed (int* r = range) list.AddRangeNoResize(r, 17); }); + container.SetCapacity(17); + Assert.DoesNotThrow(() => { fixed (int* r = range) container.AddRangeNoResize(r, 17); }); - list.Length = 16; - list.TrimExcess(); + container.Length = 16; + container.TrimExcess(); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - Assert.Throws(() => { list.AddNoResize(16); }); + Assert.Throws(() => { container.AddNoResize(16); }); #endif + + container.Dispose(); } [Test] public unsafe void UnsafeListT_AddNoResize_Read() { - var list = new UnsafeList(4, Allocator.Persistent, NativeArrayOptions.ClearMemory); - list.AddNoResize(4); - list.AddNoResize(6); - list.AddNoResize(4); - list.AddNoResize(9); - Expected(ref list, 4, new int[] { 4, 6, 4, 9 }); + var container = new UnsafeList(4, Allocator.Persistent, NativeArrayOptions.ClearMemory); + container.AddNoResize(4); + container.AddNoResize(6); + container.AddNoResize(4); + container.AddNoResize(9); + Expected(ref container, 4, new int[] { 4, 6, 4, 9 }); - list.Dispose(); + container.Dispose(); } [Test] diff --git a/Unity.Collections/AllocatorManager.cs b/Unity.Collections/AllocatorManager.cs index e6195fc..f6219ae 100644 --- a/Unity.Collections/AllocatorManager.cs +++ b/Unity.Collections/AllocatorManager.cs @@ -499,6 +499,7 @@ internal void InvalidateDependents() } } ChildSafetyHandles.Clear(); + ChildSafetyHandles.TrimExcess(); ChildSpinLock.Release(); #endif if (Parent.IsValid) @@ -515,6 +516,7 @@ internal void InvalidateDependents() } } ChildAllocators.Clear(); + ChildAllocators.TrimExcess(); } #endif diff --git a/Unity.Collections/DataStreamReader.cs b/Unity.Collections/DataStreamReader.cs index 48407bf..a472fad 100644 --- a/Unity.Collections/DataStreamReader.cs +++ b/Unity.Collections/DataStreamReader.cs @@ -359,18 +359,18 @@ public double ReadDouble() /// /// model for reading value in a packed manner. /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. - public uint ReadPackedUInt(StreamCompressionModel model) + public uint ReadPackedUInt(in StreamCompressionModel model) { - return ReadPackedUIntInternal(StreamCompressionModel.k_MaxHuffmanSymbolLength, model.decodeTable, model.bucketOffsets, model.bucketSizes); + return ReadPackedUIntInternal(StreamCompressionModel.k_MaxHuffmanSymbolLength, model); } - uint ReadPackedUIntInternal(int maxSymbolLength, ushort* decodeTable, uint* bucketOffsets, byte* bucketSizes) + uint ReadPackedUIntInternal(int maxSymbolLength, in StreamCompressionModel model) { CheckRead(); FillBitBuffer(); uint peekMask = (1u << maxSymbolLength) - 1u; uint peekBits = (uint)m_Context.m_BitBuffer & peekMask; - ushort huffmanEntry = decodeTable[(int)peekBits]; + ushort huffmanEntry = model.decodeTable[(int)peekBits]; int symbol = huffmanEntry >> 8; int length = huffmanEntry & 0xFF; @@ -387,8 +387,8 @@ uint ReadPackedUIntInternal(int maxSymbolLength, ushort* decodeTable, uint* buck m_Context.m_BitBuffer >>= length; m_Context.m_BitIndex -= length; - uint offset = bucketOffsets[symbol]; - byte bits = bucketSizes[symbol]; + uint offset = model.bucketOffsets[symbol]; + byte bits = model.bucketSizes[symbol]; return ReadRawBitsInternal(bits) + offset; } @@ -435,7 +435,7 @@ public uint ReadRawBits(int numbits) /// /// model for reading value in a packed manner. /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached. - public ulong ReadPackedULong(StreamCompressionModel model) + public ulong ReadPackedULong(in StreamCompressionModel model) { ulong value; ((uint*)&value)[0] = ReadPackedUInt(model); @@ -450,7 +450,7 @@ public ulong ReadPackedULong(StreamCompressionModel model) /// /// model for reading value in a packed manner. /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached. - public int ReadPackedInt(StreamCompressionModel model) + public int ReadPackedInt(in StreamCompressionModel model) { uint folded = ReadPackedUInt(model); return (int)(folded >> 1) ^ -(int)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...] @@ -463,7 +463,7 @@ public int ReadPackedInt(StreamCompressionModel model) /// /// model for reading value in a packed manner. /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached. - public long ReadPackedLong(StreamCompressionModel model) + public long ReadPackedLong(in StreamCompressionModel model) { ulong folded = ReadPackedULong(model); return (long)(folded >> 1) ^ -(long)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...] @@ -474,7 +474,7 @@ public long ReadPackedLong(StreamCompressionModel model) /// /// model for reading value in a packed manner. /// A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached. - public float ReadPackedFloat(StreamCompressionModel model) + public float ReadPackedFloat(in StreamCompressionModel model) { return ReadPackedFloatDelta(0, model); } @@ -484,7 +484,7 @@ public float ReadPackedFloat(StreamCompressionModel model) /// /// model for reading value in a packed manner. /// A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached. - public double ReadPackedDouble(StreamCompressionModel model) + public double ReadPackedDouble(in StreamCompressionModel model) { return ReadPackedDoubleDelta(0, model); } @@ -498,7 +498,7 @@ public double ReadPackedDouble(StreamCompressionModel model) /// If the data did not change, this also returns 0. ///
/// See: to verify if the read failed. - public int ReadPackedIntDelta(int baseline, StreamCompressionModel model) + public int ReadPackedIntDelta(int baseline, in StreamCompressionModel model) { int delta = ReadPackedInt(model); return baseline - delta; @@ -513,7 +513,7 @@ public int ReadPackedIntDelta(int baseline, StreamCompressionModel model) /// If the data did not change, this also returns 0. ///
/// See: to verify if the read failed. - public uint ReadPackedUIntDelta(uint baseline, StreamCompressionModel model) + public uint ReadPackedUIntDelta(uint baseline, in StreamCompressionModel model) { uint delta = (uint)ReadPackedInt(model); return baseline - delta; @@ -528,7 +528,7 @@ public uint ReadPackedUIntDelta(uint baseline, StreamCompressionModel model) /// If the data did not change, this also returns 0. ///
/// See: to verify if the read failed. - public long ReadPackedLongDelta(long baseline, StreamCompressionModel model) + public long ReadPackedLongDelta(long baseline, in StreamCompressionModel model) { long delta = ReadPackedLong(model); return baseline - delta; @@ -543,7 +543,7 @@ public long ReadPackedLongDelta(long baseline, StreamCompressionModel model) /// If the data did not change, this also returns 0. ///
/// See: to verify if the read failed. - public ulong ReadPackedULongDelta(ulong baseline, StreamCompressionModel model) + public ulong ReadPackedULongDelta(ulong baseline, in StreamCompressionModel model) { ulong delta = (ulong)ReadPackedLong(model); return baseline - delta; @@ -559,7 +559,7 @@ public ulong ReadPackedULongDelta(ulong baseline, StreamCompressionModel model) /// A 4-byte floating point value read from the current stream, or if there are no changes to the value. ///
/// See: to verify if the read failed.
- public float ReadPackedFloatDelta(float baseline, StreamCompressionModel model) + public float ReadPackedFloatDelta(float baseline, in StreamCompressionModel model) { CheckRead(); FillBitBuffer(); @@ -582,7 +582,7 @@ public float ReadPackedFloatDelta(float baseline, StreamCompressionModel model) /// A 8-byte floating point value read from the current stream, or if there are no changes to the value. ///
/// See: to verify if the read failed.
- public double ReadPackedDoubleDelta(double baseline, StreamCompressionModel model) + public double ReadPackedDoubleDelta(double baseline, in StreamCompressionModel model) { CheckRead(); FillBitBuffer(); @@ -689,7 +689,7 @@ unsafe ushort ReadFixedStringInternal(byte* data, int maxLength) /// The previous FixedString32Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// A FixedString32Bytes value read from the current stream, or 0 if the end of the stream has been reached. - public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes baseline, StreamCompressionModel model) + public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes baseline, in StreamCompressionModel model) { FixedString32Bytes str; byte* data = ((byte*)&str) + 2; @@ -703,7 +703,7 @@ public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes /// The previous FixedString64Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// A FixedString64Bytes value read from the current stream, or 0 if the end of the stream has been reached. - public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes baseline, StreamCompressionModel model) + public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes baseline, in StreamCompressionModel model) { FixedString64Bytes str; byte* data = ((byte*)&str) + 2; @@ -717,7 +717,7 @@ public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes /// The previous FixedString128Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// A FixedString128Bytes value read from the current stream, or 0 if the end of the stream has been reached. - public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128Bytes baseline, StreamCompressionModel model) + public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128Bytes baseline, in StreamCompressionModel model) { FixedString128Bytes str; byte* data = ((byte*)&str) + 2; @@ -731,7 +731,7 @@ public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128By /// The previous FixedString512Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// A FixedString512Bytes value read from the current stream, or 0 if the end of the stream has been reached. - public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512Bytes baseline, StreamCompressionModel model) + public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512Bytes baseline, in StreamCompressionModel model) { FixedString512Bytes str; byte* data = ((byte*)&str) + 2; @@ -745,7 +745,7 @@ public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512By /// The previous FixedString4096Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// A FixedString4096Bytes value read from the current stream, or 0 if the end of the stream has been reached. - public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString4096Bytes baseline, StreamCompressionModel model) + public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString4096Bytes baseline, in StreamCompressionModel model) { FixedString4096Bytes str; byte* data = ((byte*)&str) + 2; @@ -761,12 +761,12 @@ public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString409 /// Array containing the previous value, used to compute the diff. /// model for writing value in a packed manner. /// Length of data read into byte array, or zero if error occurred. - public ushort ReadPackedFixedStringDelta(NativeArray data, NativeArray baseData, StreamCompressionModel model) + public ushort ReadPackedFixedStringDelta(NativeArray data, NativeArray baseData, in StreamCompressionModel model) { return ReadPackedFixedStringDeltaInternal((byte*)data.GetUnsafePtr(), data.Length, (byte*)baseData.GetUnsafePtr(), (ushort)baseData.Length, model); } - unsafe ushort ReadPackedFixedStringDeltaInternal(byte* data, int maxLength, byte* baseData, ushort baseLength, StreamCompressionModel model) + unsafe ushort ReadPackedFixedStringDeltaInternal(byte* data, int maxLength, byte* baseData, ushort baseLength, in StreamCompressionModel model) { uint length = ReadPackedUIntDelta(baseLength, model); if (length > (uint)maxLength) diff --git a/Unity.Collections/DataStreamWriter.cs b/Unity.Collections/DataStreamWriter.cs index 59ad61f..27545f6 100644 --- a/Unity.Collections/DataStreamWriter.cs +++ b/Unity.Collections/DataStreamWriter.cs @@ -497,7 +497,7 @@ public bool WriteRawBits(uint value, int numbits) /// The 4-byte unsigned integer to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedUInt(uint value, StreamCompressionModel model) + public bool WritePackedUInt(uint value, in StreamCompressionModel model) { CheckWrite(); int bucket = model.CalculateBucket(value); @@ -522,7 +522,7 @@ public bool WritePackedUInt(uint value, StreamCompressionModel model) /// The 8-byte unsigned long to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedULong(ulong value, StreamCompressionModel model) + public bool WritePackedULong(ulong value, in StreamCompressionModel model) { var data = (uint*)&value; return WritePackedUInt(data[0], model) & @@ -536,7 +536,7 @@ public bool WritePackedULong(ulong value, StreamCompressionModel model) /// The 4-byte signed integer to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedInt(int value, StreamCompressionModel model) + public bool WritePackedInt(int value, in StreamCompressionModel model) { uint interleaved = (uint)((value >> 31) ^ (value << 1)); // interleave negative values between positive values: 0, -1, 1, -2, 2 return WritePackedUInt(interleaved, model); @@ -548,7 +548,7 @@ public bool WritePackedInt(int value, StreamCompressionModel model) /// The 8-byte signed long to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedLong(long value, StreamCompressionModel model) + public bool WritePackedLong(long value, in StreamCompressionModel model) { ulong interleaved = (ulong)((value >> 63) ^ (value << 1)); // interleave negative values between positive values: 0, -1, 1, -2, 2 return WritePackedULong(interleaved, model); @@ -560,7 +560,7 @@ public bool WritePackedLong(long value, StreamCompressionModel model) /// The 4-byte floating point value to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedFloat(float value, StreamCompressionModel model) + public bool WritePackedFloat(float value, in StreamCompressionModel model) { return WritePackedFloatDelta(value, 0, model); } @@ -571,7 +571,7 @@ public bool WritePackedFloat(float value, StreamCompressionModel model) /// The 8-byte floating point value to write. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedDouble(double value, StreamCompressionModel model) + public bool WritePackedDouble(double value, in StreamCompressionModel model) { return WritePackedDoubleDelta(value, 0, model); } @@ -584,7 +584,7 @@ public bool WritePackedDouble(double value, StreamCompressionModel model) /// The previous 4-byte unsigned integer value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedUIntDelta(uint value, uint baseline, StreamCompressionModel model) + public bool WritePackedUIntDelta(uint value, uint baseline, in StreamCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); @@ -597,7 +597,7 @@ public bool WritePackedUIntDelta(uint value, uint baseline, StreamCompressionMod /// The previous 4-byte signed integer value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedIntDelta(int value, int baseline, StreamCompressionModel model) + public bool WritePackedIntDelta(int value, int baseline, in StreamCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); @@ -610,7 +610,7 @@ public bool WritePackedIntDelta(int value, int baseline, StreamCompressionModel /// The previous 8-byte signed long value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedLongDelta(long value, long baseline, StreamCompressionModel model) + public bool WritePackedLongDelta(long value, long baseline, in StreamCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); @@ -624,7 +624,7 @@ public bool WritePackedLongDelta(long value, long baseline, StreamCompressionMod /// The previous 8-byte unsigned long, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public bool WritePackedULongDelta(ulong value, ulong baseline, StreamCompressionModel model) + public bool WritePackedULongDelta(ulong value, ulong baseline, in StreamCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); @@ -640,7 +640,7 @@ public bool WritePackedULongDelta(ulong value, ulong baseline, StreamCompression /// The previous 4-byte floating value, used to compute the diff. /// Not currently used. /// Whether the write was successful - public bool WritePackedFloatDelta(float value, float baseline, StreamCompressionModel model) + public bool WritePackedFloatDelta(float value, float baseline, in StreamCompressionModel model) { CheckWrite(); var bits = 0; @@ -674,7 +674,7 @@ public bool WritePackedFloatDelta(float value, float baseline, StreamCompression /// The previous 8-byte floating value, used to compute the diff. /// Not currently used. /// Whether the write was successful - public bool WritePackedDoubleDelta(double value, double baseline, StreamCompressionModel model) + public bool WritePackedDoubleDelta(double value, double baseline, in StreamCompressionModel model) { CheckWrite(); var bits = 0; @@ -769,7 +769,7 @@ public unsafe bool WriteFixedString4096(FixedString4096Bytes str) /// The previous FixedString32Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public unsafe bool WritePackedFixedString32Delta(FixedString32Bytes str, FixedString32Bytes baseline, StreamCompressionModel model) + public unsafe bool WritePackedFixedString32Delta(FixedString32Bytes str, FixedString32Bytes baseline, in StreamCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; @@ -783,7 +783,7 @@ public unsafe bool WritePackedFixedString32Delta(FixedString32Bytes str, FixedSt /// The previous FixedString64Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public unsafe bool WritePackedFixedString64Delta(FixedString64Bytes str, FixedString64Bytes baseline, StreamCompressionModel model) + public unsafe bool WritePackedFixedString64Delta(FixedString64Bytes str, FixedString64Bytes baseline, in StreamCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; @@ -797,7 +797,7 @@ public unsafe bool WritePackedFixedString64Delta(FixedString64Bytes str, FixedSt /// The previous FixedString128Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public unsafe bool WritePackedFixedString128Delta(FixedString128Bytes str, FixedString128Bytes baseline, StreamCompressionModel model) + public unsafe bool WritePackedFixedString128Delta(FixedString128Bytes str, FixedString128Bytes baseline, in StreamCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; @@ -811,7 +811,7 @@ public unsafe bool WritePackedFixedString128Delta(FixedString128Bytes str, Fixed /// The previous FixedString512Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public unsafe bool WritePackedFixedString512Delta(FixedString512Bytes str, FixedString512Bytes baseline, StreamCompressionModel model) + public unsafe bool WritePackedFixedString512Delta(FixedString512Bytes str, FixedString512Bytes baseline, in StreamCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; @@ -825,7 +825,7 @@ public unsafe bool WritePackedFixedString512Delta(FixedString512Bytes str, Fixed /// The previous FixedString4096Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful - public unsafe bool WritePackedFixedString4096Delta(FixedString4096Bytes str, FixedString4096Bytes baseline, StreamCompressionModel model) + public unsafe bool WritePackedFixedString4096Delta(FixedString4096Bytes str, FixedString4096Bytes baseline, in StreamCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; @@ -844,7 +844,7 @@ public unsafe bool WritePackedFixedString4096Delta(FixedString4096Bytes str, Fix /// The length of the previous value. /// model for writing value in a packed manner. /// Whether the write was successful - unsafe bool WritePackedFixedStringDelta(byte* data, uint length, byte* baseData, uint baseLength, StreamCompressionModel model) + unsafe bool WritePackedFixedStringDelta(byte* data, uint length, byte* baseData, uint baseLength, in StreamCompressionModel model) { var oldData = m_Data; if (!WritePackedUIntDelta(length, baseLength, model)) diff --git a/Unity.Collections/FixedStringFormatMethods.gen.cs b/Unity.Collections/FixedStringFormatMethods.gen.cs index 6bc5d31..f225d8f 100644 --- a/Unity.Collections/FixedStringFormatMethods.gen.cs +++ b/Unity.Collections/FixedStringFormatMethods.gen.cs @@ -37,8 +37,9 @@ public unsafe static partial class FixedStringMethods /// The string to append to.d /// A string to be interpolated and appended. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -46,25 +47,51 @@ public static unsafe void AppendFormat(ref this T dest, in U format, i ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -86,8 +113,9 @@ public static unsafe void AppendFormat(ref this T dest, in U format, i /// A string to be interpolated and appended. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -96,26 +124,52 @@ public static unsafe void AppendFormat(ref this T dest, in U forma ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -139,8 +193,9 @@ public static unsafe void AppendFormat(ref this T dest, in U forma /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -150,27 +205,53 @@ public static unsafe void AppendFormat(ref this T dest, in U f ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -196,8 +277,9 @@ public static unsafe void AppendFormat(ref this T dest, in U f /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -208,28 +290,54 @@ public static unsafe void AppendFormat(ref this T dest, in ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -257,8 +365,9 @@ public static unsafe void AppendFormat(ref this T dest, in /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -270,29 +379,55 @@ public static unsafe void AppendFormat(ref this T dest ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -322,8 +457,9 @@ public static unsafe void AppendFormat(ref this T dest /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/), typeof(FixedString128Bytes /*T5*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -336,30 +472,56 @@ public static unsafe void AppendFormat(ref this T ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - case 5: dest.Append(in arg5); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + case 5: err = dest.Append(in arg5); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -391,8 +553,9 @@ public static unsafe void AppendFormat(ref this T /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/), typeof(FixedString128Bytes /*T5*/), typeof(FixedString128Bytes /*T6*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -406,31 +569,57 @@ public static unsafe void AppendFormat(ref thi ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - case 5: dest.Append(in arg5); i+=2; break; - case 6: dest.Append(in arg6); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + case 5: err = dest.Append(in arg5); break; + case 6: err = dest.Append(in arg6); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -464,8 +653,9 @@ public static unsafe void AppendFormat(ref thi /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/), typeof(FixedString128Bytes /*T5*/), typeof(FixedString128Bytes /*T6*/), typeof(FixedString128Bytes /*T7*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -480,32 +670,58 @@ public static unsafe void AppendFormat(ref ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - case 5: dest.Append(in arg5); i+=2; break; - case 6: dest.Append(in arg6); i+=2; break; - case 7: dest.Append(in arg7); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + case 5: err = dest.Append(in arg5); break; + case 6: err = dest.Append(in arg6); break; + case 7: err = dest.Append(in arg7); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -541,8 +757,9 @@ public static unsafe void AppendFormat(ref /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/), typeof(FixedString128Bytes /*T5*/), typeof(FixedString128Bytes /*T6*/), typeof(FixedString128Bytes /*T7*/), typeof(FixedString128Bytes /*T8*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7, in T8 arg8) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7, in T8 arg8) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -558,33 +775,59 @@ public static unsafe void AppendFormat ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - case 5: dest.Append(in arg5); i+=2; break; - case 6: dest.Append(in arg6); i+=2; break; - case 7: dest.Append(in arg7); i+=2; break; - case 8: dest.Append(in arg8); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + case 5: err = dest.Append(in arg5); break; + case 6: err = dest.Append(in arg6); break; + case 7: err = dest.Append(in arg7); break; + case 8: err = dest.Append(in arg8); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } /// @@ -622,8 +865,9 @@ public static unsafe void AppendFormat /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. /// A FixedString*N*Bytes to interpolate into the format string. + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), typeof(FixedString128Bytes /*T0*/), typeof(FixedString128Bytes /*T1*/), typeof(FixedString128Bytes /*T2*/), typeof(FixedString128Bytes /*T3*/), typeof(FixedString128Bytes /*T4*/), typeof(FixedString128Bytes /*T5*/), typeof(FixedString128Bytes /*T6*/), typeof(FixedString128Bytes /*T7*/), typeof(FixedString128Bytes /*T8*/), typeof(FixedString128Bytes /*T9*/) })] - public static unsafe void AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7, in T8 arg8, in T9 arg9) + public static unsafe FormatError AppendFormat(ref this T dest, in U format, in T0 arg0, in T1 arg1, in T2 arg2, in T3 arg3, in T4 arg4, in T5 arg5, in T6 arg6, in T7 arg7, in T8 arg8, in T9 arg9) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes where T0 : unmanaged, INativeList, IUTF8Bytes @@ -640,34 +884,60 @@ public static unsafe void AppendFormat= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { - case 0: dest.Append(in arg0); i+=2; break; - case 1: dest.Append(in arg1); i+=2; break; - case 2: dest.Append(in arg2); i+=2; break; - case 3: dest.Append(in arg3); i+=2; break; - case 4: dest.Append(in arg4); i+=2; break; - case 5: dest.Append(in arg5); i+=2; break; - case 6: dest.Append(in arg6); i+=2; break; - case 7: dest.Append(in arg7); i+=2; break; - case 8: dest.Append(in arg8); i+=2; break; - case 9: dest.Append(in arg9); i+=2; break; - default: - dest.AppendRawByte(formatBytes[i]); - break; + case 0: err = dest.Append(in arg0); break; + case 1: err = dest.Append(in arg1); break; + case 2: err = dest.Append(in arg2); break; + case 3: err = dest.Append(in arg3); break; + case 4: err = dest.Append(in arg4); break; + case 5: err = dest.Append(in arg5); break; + case 6: err = dest.Append(in arg6); break; + case 7: err = dest.Append(in arg7); break; + case 8: err = dest.Append(in arg8); break; + case 9: err = dest.Append(in arg9); break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } diff --git a/Unity.Collections/FixedStringFormatMethods.tt b/Unity.Collections/FixedStringFormatMethods.tt index 57eeae1..a5bfaf1 100644 --- a/Unity.Collections/FixedStringFormatMethods.tt +++ b/Unity.Collections/FixedStringFormatMethods.tt @@ -52,8 +52,9 @@ namespace Unity.Collections /// The string to append to.d /// A string to be interpolated and appended. /// <#=ARGxDOCS#> + /// if successful. Otherwise returns the appropriate . [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes /*T*/), typeof(FixedString128Bytes /*U*/), <#=BCOMPAT#> })] - public static unsafe void AppendFormat>(ref this T dest, in U format, <#=PARAMS#>) + public static unsafe FormatError AppendFormat>(ref this T dest, in U format, <#=PARAMS#>) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes <# @@ -64,30 +65,56 @@ namespace Unity.Collections ref var formatRef = ref UnsafeUtilityExtensions.AsRef(in format); int formatLength = formatRef.Length; byte* formatBytes = formatRef.GetUnsafePtr(); - for (var i = 0; i < formatLength; ++i) + int i = 0; + FormatError err = FormatError.None; + while (i < formatLength) { - if (formatBytes[i] == (byte)'{') + byte currByte = formatBytes[i++]; + if (currByte == (byte)'{') { - if (formatLength - i >= 3 && formatBytes[i + 1] != (byte)'{') + if (i < formatLength) + currByte = formatBytes[i++]; + else + return FormatError.BadFormatSpecifier; + + if (currByte >= (byte)'0' && currByte <= (byte)'9' && i < formatLength && formatBytes[i++] == (byte)'}') { - var index = formatBytes[i + 1] - (byte)'0'; - switch (index) + switch (currByte - (byte)'0') { <# for(var a = 0; a < ARGS; ++a) { - WriteLine($" case {a}: dest.Append(in arg{a}); i+=2; break;"); + WriteLine($" case {a}: err = dest.Append(in arg{a}); break;"); } #> - default: - dest.AppendRawByte(formatBytes[i]); - break; + default: err = FormatError.BadFormatSpecifier; break; } } + else if (currByte == (byte)'{') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; + } + else if (currByte == (byte)'}') + { + if (i < formatLength) + currByte = formatBytes[i++]; + else + err = FormatError.BadFormatSpecifier; + + if (currByte == (byte)'}') + err = dest.AppendRawByte(currByte); + else + err = FormatError.BadFormatSpecifier; } else - dest.AppendRawByte(formatBytes[i]); + err = dest.AppendRawByte(currByte); + + if (err != FormatError.None) + return err; } + + return FormatError.None; } <# diff --git a/Unity.Collections/NativeBitArray.cs b/Unity.Collections/NativeBitArray.cs index 5239e8f..5f0058e 100644 --- a/Unity.Collections/NativeBitArray.cs +++ b/Unity.Collections/NativeBitArray.cs @@ -5,6 +5,7 @@ using Unity.Burst; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; +using static Unity.Collections.AllocatorManager; namespace Unity.Collections { @@ -27,6 +28,7 @@ public unsafe struct NativeBitArray #endif [NativeDisableUnsafePtrRestriction] internal UnsafeBitArray* m_BitArray; + internal AllocatorHandle m_Allocator; /// /// Initializes and returns an instance of NativeBitArray. @@ -42,7 +44,8 @@ public NativeBitArray(int numBits, AllocatorManager.AllocatorHandle allocator, N CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data, "Unity.Collections.NativeBitArray"); #endif m_BitArray = UnsafeBitArray.Alloc(allocator); - *m_BitArray = new UnsafeBitArray(numBits, allocator, options); + m_Allocator = allocator; + * m_BitArray = new UnsafeBitArray(numBits, allocator, options); } /// @@ -112,8 +115,9 @@ public void Dispose() #if ENABLE_UNITY_COLLECTIONS_CHECKS CollectionHelper.DisposeSafetyHandle(ref m_Safety); #endif - UnsafeBitArray.Free(m_BitArray); + UnsafeBitArray.Free(m_BitArray, m_Allocator); m_BitArray = null; + m_Allocator = Invalid; } /// @@ -135,12 +139,13 @@ public JobHandle Dispose(JobHandle inputDeps) } #if ENABLE_UNITY_COLLECTIONS_CHECKS - var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray, m_Safety = m_Safety } }.Schedule(inputDeps); + var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray, m_Allocator = m_Allocator, m_Safety = m_Safety } }.Schedule(inputDeps); AtomicSafetyHandle.Release(m_Safety); #else - var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray } }.Schedule(inputDeps); + var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray, m_Allocator = m_Allocator } }.Schedule(inputDeps); #endif m_BitArray = null; + m_Allocator = Invalid; return jobHandle; @@ -612,6 +617,7 @@ internal unsafe struct NativeBitArrayDispose { [NativeDisableUnsafePtrRestriction] public UnsafeBitArray* m_BitArrayData; + public AllocatorHandle m_Allocator; #if ENABLE_UNITY_COLLECTIONS_CHECKS public AtomicSafetyHandle m_Safety; @@ -619,7 +625,7 @@ internal unsafe struct NativeBitArrayDispose public void Dispose() { - UnsafeBitArray.Free(m_BitArrayData); + UnsafeBitArray.Free(m_BitArrayData, m_Allocator); } } @@ -682,6 +688,7 @@ public static unsafe NativeBitArray ConvertExistingDataToNativeBitArray(void* pt return new NativeBitArray { m_BitArray = bitArray, + m_Allocator = Allocator.Persistent, }; } } diff --git a/Unity.Collections/StreamCompressionModel.cs b/Unity.Collections/StreamCompressionModel.cs index 68c1075..f58f4e1 100644 --- a/Unity.Collections/StreamCompressionModel.cs +++ b/Unity.Collections/StreamCompressionModel.cs @@ -13,8 +13,8 @@ namespace Unity.Collections /// It codes the bucket index with Huffman, and uses several raw bits that correspond /// to the size of the bucket to code the position in the bucket. /// - /// For example, if you want to send a 32-bit integer over the network, it's - /// impractical to create a Huffman tree that encompasses every value the integer + /// For example, if you want to send a 32-bit integer over the network, it's + /// impractical to create a Huffman tree that encompasses every value the integer /// can take because it requires a tree with 2^32 leaves. This type manages that situation. /// /// The buckets are small, around 0, and become progressively larger as the data moves away from zero. @@ -235,7 +235,7 @@ static void GenerateHuffmanDecodeTable(NativeArray decodeTable, int deco /// /// A 4-byte unsigned integer value to find a bucket for. /// The bucket index where to put the value. - public int CalculateBucket(uint value) + readonly public int CalculateBucket(uint value) { int bucketIndex = k_FirstBucketCandidate[math.lzcnt(value)]; if (bucketIndex + 1 < k_AlphabetSize && value >= bucketOffsets[bucketIndex + 1]) @@ -244,6 +244,19 @@ public int CalculateBucket(uint value) return bucketIndex; } + /// + /// The compressed size in bits of the given unsigned integer value + /// + /// the unsigned int value you want to compress + /// + public readonly int GetCompressedSizeInBits(uint value) + { + int bucket = CalculateBucket(value); + int bits = bucketSizes[bucket]; + ushort encodeEntry = encodeTable[bucket]; + return (encodeEntry & 0xff) + bits; + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] static void CheckAlphabetSize(int alphabetSize) { diff --git a/Unity.Collections/Unicode.cs b/Unity.Collections/Unicode.cs index 861d2fb..f0caf12 100644 --- a/Unity.Collections/Unicode.cs +++ b/Unity.Collections/Unicode.cs @@ -18,6 +18,13 @@ public enum FormatError /// Note that the format's write failed. It did not truncate. /// Overflow, + + /// + /// The source format specifier is not itself correctly formatted, or + /// a format specifier tokens were found outside of accepted usage. + /// Note that the format's write failed. + /// + BadFormatSpecifier, } /// diff --git a/Unity.Collections/UnsafeBitArray.cs b/Unity.Collections/UnsafeBitArray.cs index 168c243..0229769 100644 --- a/Unity.Collections/UnsafeBitArray.cs +++ b/Unity.Collections/UnsafeBitArray.cs @@ -83,13 +83,12 @@ public UnsafeBitArray(int numBits, AllocatorManager.AllocatorHandle allocator, N return data; } - internal static void Free(UnsafeBitArray* data) + internal static void Free(UnsafeBitArray* data, AllocatorManager.AllocatorHandle allocator) { if (data == null) { throw new InvalidOperationException("UnsafeBitArray has yet to be created or has been destroyed!"); } - var allocator = data->Allocator; data->Dispose(); Memory.Unmanaged.Free(data, allocator); } diff --git a/ValidationExceptions.json b/ValidationExceptions.json index d4a7aa7..71282a7 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -3,12 +3,12 @@ { "ValidationTest": "API Validation", "ExceptionMessage": "Breaking changes require a new major version.", - "PackageVersion": "2.4.0-pre.2" + "PackageVersion": "2.4.0-pre.5" }, { "ValidationTest": "API Validation", "ExceptionMessage": "Assembly \"Unity.PerformanceTesting\" no longer exists or is no longer included in build. This change requires a new major version.", - "PackageVersion": "2.4.0-pre.2" + "PackageVersion": "2.4.0-pre.5" } ], "WarningExceptions": [] diff --git a/package.json b/package.json index aeb00c0..539a4ec 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "com.unity.collections", "displayName": "Collections", - "version": "2.4.0-pre.2", + "version": "2.4.0-pre.5", "unity": "2022.3", "unityRelease": "11f1", "dependencies": { - "com.unity.burst": "1.8.10", + "com.unity.burst": "1.8.12", "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.test-framework": "1.3.8", + "com.unity.test-framework": "1.4.3", "com.unity.test-framework.performance": "3.0.3" }, "description": "A C# collections library providing data structures that can be used in jobs, and optimized by Burst compiler.", @@ -17,15 +17,15 @@ "unity" ], "_upm": { - "changelog": "### Changed\n\n* Updated Burst dependency to 1.8.10\n* Add lightweight spinlock to secure multithread write access to the `ChildSafetyHandles` in custom allocator framework." + "changelog": "### Changed\n\n* Updated Burst dependency to version 1.8.12\n\n### Fixed\n\n* Memory leaks in unit tests.\n* Memory leak when using `NativeBitArrayUnsafeUtility.ConvertExistingDataToNativeBitArray`.\n* Brace parsing in FixedString AppendFormat handles incorrect formatting now\n\n### Updated\n\n* Upgraded Test Framework version to 1.4.3" }, "upmCi": { - "footprint": "5c97bfa9131679b3b12955c38af67d230e84024a" + "footprint": "9b2fc38357300d43e27e9779698eaf641422edbb" }, "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.collections@2.4/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/dots.git", "type": "git", - "revision": "7e5e9a90429eccc3d4787f733265f7846004c554" + "revision": "1ae43e0525b8a09c7fe52ab40c762938a4937244" } }