From 2edbdf9f91db2c047f1d13955cd13b47890e5ec7 Mon Sep 17 00:00:00 2001 From: Joshua Yue Date: Tue, 28 Jul 2026 15:25:59 -0700 Subject: [PATCH 1/4] Add ReadOnlySpan constructors to BitArray Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b --- .../ref/System.Collections.cs | 3 + .../tests/BitArray/BitArray_CtorTests.cs | 185 +++++++++++++++--- .../src/System/Collections/BitArray.cs | 48 ++++- 3 files changed, 199 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Collections/ref/System.Collections.cs b/src/libraries/System.Collections/ref/System.Collections.cs index b97b5f48300945..5aabe94de2b3aa 100644 --- a/src/libraries/System.Collections/ref/System.Collections.cs +++ b/src/libraries/System.Collections/ref/System.Collections.cs @@ -565,6 +565,9 @@ public BitArray(System.Collections.BitArray bits) { } public BitArray(int length) { } public BitArray(int length, bool defaultValue) { } public BitArray(int[] values) { } + public BitArray(System.ReadOnlySpan values) { } + public BitArray(System.ReadOnlySpan bytes) { } + public BitArray(System.ReadOnlySpan values) { } public int Count { get { throw null; } } public bool IsReadOnly { get { throw null; } } public bool IsSynchronized { get { throw null; } } diff --git a/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs b/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs index 199b44cd04aa86..5c8e11a8ccf9e0 100644 --- a/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs +++ b/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs @@ -1,8 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using Xunit; namespace System.Collections.Tests @@ -109,16 +111,14 @@ public static IEnumerable Ctor_BoolArray_TestData() [MemberData(nameof(Ctor_BoolArray_TestData))] public static void Ctor_BoolArray(bool[] values) { - BitArray bitArray = new BitArray(values); - Assert.Equal(values.Length, bitArray.Length); - for (int i = 0; i < bitArray.Length; i++) - { - Assert.Equal(values[i], bitArray[i]); - Assert.Equal(values[i], bitArray.Get(i)); - } - ICollection collection = bitArray; - Assert.Equal(values.Length, collection.Count); - Assert.False(collection.IsSynchronized); + AssertBitArray(new BitArray(values), values); + } + + [Theory] + [MemberData(nameof(Ctor_BoolArray_TestData))] + public static void Ctor_BoolSpan(bool[] values) + { + AssertBitArray(new BitArray(values.AsSpan()), values); } [Fact] @@ -195,16 +195,14 @@ public static IEnumerable Ctor_IntArray_TestData() [MemberData(nameof(Ctor_IntArray_TestData))] public static void Ctor_IntArray(int[] array, bool[] expected) { - BitArray bitArray = new BitArray(array); - Assert.Equal(expected.Length, bitArray.Length); - for (int i = 0; i < expected.Length; i++) - { - Assert.Equal(expected[i], bitArray[i]); - Assert.Equal(expected[i], bitArray.Get(i)); - } - ICollection collection = bitArray; - Assert.Equal(expected.Length, collection.Count); - Assert.False(collection.IsSynchronized); + AssertBitArray(new BitArray(array), expected); + } + + [Theory] + [MemberData(nameof(Ctor_IntArray_TestData))] + public static void Ctor_IntSpan(int[] array, bool[] expected) + { + AssertBitArray(new BitArray(array.AsSpan()), expected); } [Fact] @@ -234,16 +232,14 @@ public static IEnumerable Ctor_ByteArray_TestData() [MemberData(nameof(Ctor_ByteArray_TestData))] public static void Ctor_ByteArray(byte[] bytes, bool[] expected) { - BitArray bitArray = new BitArray(bytes); - Assert.Equal(expected.Length, bitArray.Length); - for (int i = 0; i < bitArray.Length; i++) - { - Assert.Equal(expected[i], bitArray[i]); - Assert.Equal(expected[i], bitArray.Get(i)); - } - ICollection collection = bitArray; - Assert.Equal(expected.Length, collection.Count); - Assert.False(collection.IsSynchronized); + AssertBitArray(new BitArray(bytes), expected); + } + + [Theory] + [MemberData(nameof(Ctor_ByteArray_TestData))] + public static void Ctor_ByteSpan(byte[] bytes, bool[] expected) + { + AssertBitArray(new BitArray(bytes.AsSpan()), expected); } [Fact] @@ -258,6 +254,121 @@ public static void Ctor_LargeByteArrayOverflowingBitArray_ThrowsArgumentExceptio AssertExtensions.Throws("bytes", () => new BitArray(new byte[int.MaxValue / BitsPerByte + 1 ])); } + [Fact] + public static void Ctor_EmptySpans() + { + Assert.Empty(new BitArray(default(ReadOnlySpan))); + Assert.Empty(new BitArray(default(ReadOnlySpan))); + Assert.Empty(new BitArray(default(ReadOnlySpan))); + } + + [Fact] + public static void Ctor_SpanSlices() + { + bool[] boolValues = [false, true, false, true, true, false]; + AssertBitArray(new BitArray(boolValues.AsSpan(1, 4)), [true, false, true, true]); + + byte[] byteValues = [0xFF, 0x01, 0x80, 0xFF]; + AssertBitArray( + new BitArray(byteValues.AsSpan(1, 2)), + [true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true]); + + int[] intValues = [-1, 1, int.MinValue, -1]; + bool[] expected = new bool[BitsPerInt32 * 2]; + expected[0] = true; + expected[^1] = true; + AssertBitArray(new BitArray(intValues.AsSpan(1, 2)), expected); + } + + [Fact] + public static void Ctor_StackAllocatedSpans() + { + ReadOnlySpan boolValues = stackalloc bool[] { true, false, true }; + AssertBitArray(new BitArray(boolValues), boolValues); + + ReadOnlySpan byteValues = stackalloc byte[] { 0x01, 0x80 }; + AssertBitArray( + new BitArray(byteValues), + [true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true]); + + ReadOnlySpan intValues = stackalloc int[] { 1, int.MinValue }; + bool[] expected = new bool[BitsPerInt32 * 2]; + expected[0] = true; + expected[^1] = true; + AssertBitArray(new BitArray(intValues), expected); + } + + [Fact] + public static void Ctor_SpansAreCopied() + { + bool[] boolValues = [true]; + BitArray boolBits = new BitArray(boolValues.AsSpan()); + boolValues[0] = false; + Assert.True(boolBits[0]); + + byte[] byteValues = [0x01]; + BitArray byteBits = new BitArray(byteValues.AsSpan()); + byteValues[0] = 0; + Assert.True(byteBits[0]); + + int[] intValues = [1]; + BitArray intBits = new BitArray(intValues.AsSpan()); + intValues[0] = 0; + Assert.True(intBits[0]); + } + + [Theory] + [InlineData(1)] + [InlineData(31)] + [InlineData(32)] + [InlineData(33)] + [InlineData(63)] + [InlineData(64)] + [InlineData(65)] + [InlineData(511)] + [InlineData(512)] + [InlineData(513)] + public static void Ctor_BoolSpan_DoesNotReadPastEnd(int length) + { + using BoundedMemory values = BoundedMemory.Allocate(length); + for (int i = 0; i < values.Length; i++) + { + values[i] = i % 3 == 0; + } + values.MakeReadonly(); + + BitArray bitArray = new BitArray(values.Span); + Assert.Equal(length, bitArray.Length); + for (int i = 0; i < length; i++) + { + Assert.Equal(i % 3 == 0, bitArray[i]); + } + } + + [Theory] + [InlineData(3)] + [InlineData(35)] + [InlineData(64)] + [InlineData(67)] + [InlineData(100)] + public static void Ctor_BoolSpan_NonCanonicalTrueValues(int length) + { + byte[] underlyingValues = new byte[length]; + for (int i = 0; i < underlyingValues.Length; i++) + { + underlyingValues[i] = (byte)(i % 4); + } + + BitArray bitArray = new BitArray(MemoryMarshal.Cast(underlyingValues)); + Assert.Equal(underlyingValues.Length, bitArray.Length); + for (int i = 0; i < underlyingValues.Length; i++) + { + Assert.Equal(underlyingValues[i] != 0, bitArray[i]); + } + } + [Fact] public static void Ctor_Simple_Method_Tests() { @@ -278,5 +389,19 @@ public static void Clone_LongLength_Works() Assert.Equal(bitArray.Length, clone.Length); } + + private static void AssertBitArray(BitArray bitArray, ReadOnlySpan expected) + { + Assert.Equal(expected.Length, bitArray.Length); + for (int i = 0; i < bitArray.Length; i++) + { + Assert.Equal(expected[i], bitArray[i]); + Assert.Equal(expected[i], bitArray.Get(i)); + } + + ICollection collection = bitArray; + Assert.Equal(expected.Length, collection.Count); + Assert.False(collection.IsSynchronized); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs index 39797ba0bb7f75..d203824b4c0d2b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs @@ -132,8 +132,18 @@ private void ClearHighExtraBits() /// is null. /// The length of in bits is greater than . public BitArray(byte[] bytes) + : this(new ReadOnlySpan(bytes ?? throw new ArgumentNullException(nameof(bytes)))) + { + } + + /// + /// Initializes a new instance of the class that contains bit values copied + /// from the specified read-only span of bytes. + /// + /// A read-only span of bytes containing the values to copy, where each byte represents eight consecutive bits. + /// The length of in bits is greater than . + public BitArray(ReadOnlySpan bytes) { - ArgumentNullException.ThrowIfNull(bytes); if (bytes.Length > int.MaxValue / BitsPerByte) { throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerByte), nameof(bytes)); @@ -142,7 +152,7 @@ public BitArray(byte[] bytes) _bitLength = bytes.Length * BitsPerByte; _array = AllocateByteArray(_bitLength); - Array.Copy(bytes, _array, bytes.Length); + bytes.CopyTo(_array); } /// @@ -152,9 +162,17 @@ public BitArray(byte[] bytes) /// An array of Booleans to copy. /// is null. public BitArray(bool[] values) + : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) { - ArgumentNullException.ThrowIfNull(values); + } + /// + /// Initializes a new instance of the class that contains bit values + /// copied from the specified read-only span of Booleans. + /// + /// A read-only span of Booleans to copy. + public BitArray(ReadOnlySpan values) + { _array = AllocateByteArray(values.Length); _bitLength = values.Length; @@ -167,10 +185,10 @@ public BitArray(bool[] values) // Comparing with 1s would get rid of the final negation, however this would not work for some CLR bools // (true for any non-zero values, false for 0) - any values between 2-255 will be interpreted as false. - // Instead, We compare with zeroes (== false) then negate the result to ensure compatibility. + // Instead, we compare with zeroes (== false) then negate the result to ensure compatibility. ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(_array); - ReadOnlySpan valuesAsBytes = MemoryMarshal.AsBytes(values.AsSpan()); + ReadOnlySpan valuesAsBytes = MemoryMarshal.AsBytes(values); if (Vector512.IsHardwareAccelerated) { while (valuesAsBytes.Length >= Vector512.Count) @@ -220,7 +238,7 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), Remainder: for (; i < (uint)values.Length; i++) { - if (values[i]) + if (values[(int)i]) { (uint byteIndex, uint bitOffset) = Math.DivRem(i, BitsPerByte); _array[byteIndex] |= (byte)(1 << (int)bitOffset); @@ -242,8 +260,24 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), /// "[0] & 4" represents bit 2, and so on. /// public BitArray(int[] values) + : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) + { + } + + /// + /// Initializes a new instance of the class that contains bit values + /// copied from the specified read-only span of 32-bit integers. + /// + /// A read-only span of 32-bit integers containing the values to copy, where each integer represents 32 consecutive bits. + /// The length of in bits is greater than . + /// + /// The number in the first span element represents bits 0 through 31, the second number in the span represents + /// bits 32 through 63, and so on. The least significant bit of each integer represents the lowest index value: + /// "[0] & 1" represents bit 0, "[0] & 2" represents bit 1, + /// "[0] & 4" represents bit 2, and so on. + /// + public BitArray(ReadOnlySpan values) { - ArgumentNullException.ThrowIfNull(values); if (values.Length > int.MaxValue / BitsPerInt32) { throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerInt32), nameof(values)); From 5f21107c4030dec205e2a5bc4dd204d1bc0e0a7c Mon Sep 17 00:00:00 2001 From: Joshua Yue Date: Thu, 30 Jul 2026 00:59:33 -0700 Subject: [PATCH 2/4] Document BitArray constructor behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b --- .../src/System/Collections/BitArray.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs index d203824b4c0d2b..b92cad321bc760 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs @@ -131,6 +131,14 @@ private void ClearHighExtraBits() /// An array of bytes containing the values to copy, where each byte represents eight consecutive bits. /// is null. /// The length of in bits is greater than . + /// + /// The first byte in the array represents bits 0 through 7, the second byte represents bits 8 through 15, and so on. + /// The least significant bit of each byte represents the lowest index value: + /// "[0] & 1" represents bit 0, "[0] & 2" represents bit 1, + /// "[0] & 4" represents bit 2, and so on. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . + /// public BitArray(byte[] bytes) : this(new ReadOnlySpan(bytes ?? throw new ArgumentNullException(nameof(bytes)))) { @@ -142,6 +150,14 @@ public BitArray(byte[] bytes) /// /// A read-only span of bytes containing the values to copy, where each byte represents eight consecutive bits. /// The length of in bits is greater than . + /// + /// The first byte in the span represents bits 0 through 7, the second byte represents bits 8 through 15, and so on. + /// The least significant bit of each byte represents the lowest index value: + /// "[0] & 1" represents bit 0, "[0] & 2" represents bit 1, + /// "[0] & 4" represents bit 2, and so on. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . + /// public BitArray(ReadOnlySpan bytes) { if (bytes.Length > int.MaxValue / BitsPerByte) @@ -161,6 +177,9 @@ public BitArray(ReadOnlySpan bytes) /// /// An array of Booleans to copy. /// is null. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . + /// public BitArray(bool[] values) : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) { @@ -171,6 +190,9 @@ public BitArray(bool[] values) /// copied from the specified read-only span of Booleans. /// /// A read-only span of Booleans to copy. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . + /// public BitArray(ReadOnlySpan values) { _array = AllocateByteArray(values.Length); @@ -258,6 +280,8 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), /// bits 32 through 63, and so on. The Least Significant Bit of each integer represents the lowest index value: /// "[0] & 1" represents bit 0, "[0] & 2" represents bit 1, /// "[0] & 4" represents bit 2, and so on. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(int[] values) : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) @@ -275,6 +299,8 @@ public BitArray(int[] values) /// bits 32 through 63, and so on. The least significant bit of each integer represents the lowest index value: /// "[0] & 1" represents bit 0, "[0] & 2" represents bit 1, /// "[0] & 4" represents bit 2, and so on. + /// + /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(ReadOnlySpan values) { From 92537f980d6e24bb374c22b37cdf3794b3db5c8e Mon Sep 17 00:00:00 2001 From: Joshua Yue Date: Thu, 30 Jul 2026 01:00:38 -0700 Subject: [PATCH 3/4] Preserve BitArray array constructor inlining Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b --- .../src/System/Collections/BitArray.cs | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs index b92cad321bc760..66ffc9fee7bcd7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs @@ -140,8 +140,10 @@ private void ClearHighExtraBits() /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(byte[] bytes) - : this(new ReadOnlySpan(bytes ?? throw new ArgumentNullException(nameof(bytes)))) { + ArgumentNullException.ThrowIfNull(bytes); + + _array = CreateArray(bytes, out _bitLength); } /// @@ -159,16 +161,22 @@ public BitArray(byte[] bytes) /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(ReadOnlySpan bytes) + { + _array = CreateArray(bytes, out _bitLength); + } + + private static byte[] CreateArray(ReadOnlySpan bytes, out int bitLength) { if (bytes.Length > int.MaxValue / BitsPerByte) { throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerByte), nameof(bytes)); } - _bitLength = bytes.Length * BitsPerByte; - _array = AllocateByteArray(_bitLength); + bitLength = bytes.Length * BitsPerByte; + byte[] array = AllocateByteArray(bitLength); - bytes.CopyTo(_array); + bytes.CopyTo(array); + return array; } /// @@ -181,8 +189,10 @@ public BitArray(ReadOnlySpan bytes) /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(bool[] values) - : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) { + ArgumentNullException.ThrowIfNull(values); + + _array = CreateArray(values, out _bitLength); } /// @@ -195,8 +205,13 @@ public BitArray(bool[] values) /// public BitArray(ReadOnlySpan values) { - _array = AllocateByteArray(values.Length); - _bitLength = values.Length; + _array = CreateArray(values, out _bitLength); + } + + private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) + { + bitLength = values.Length; + byte[] array = AllocateByteArray(bitLength); uint i = 0; @@ -209,7 +224,7 @@ public BitArray(ReadOnlySpan values) // (true for any non-zero values, false for 0) - any values between 2-255 will be interpreted as false. // Instead, we compare with zeroes (== false) then negate the result to ensure compatibility. - ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(_array); + ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(array); ReadOnlySpan valuesAsBytes = MemoryMarshal.AsBytes(values); if (Vector512.IsHardwareAccelerated) { @@ -263,9 +278,11 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), if (values[(int)i]) { (uint byteIndex, uint bitOffset) = Math.DivRem(i, BitsPerByte); - _array[byteIndex] |= (byte)(1 << (int)bitOffset); + array[byteIndex] |= (byte)(1 << (int)bitOffset); } } + + return array; } /// @@ -284,8 +301,10 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(int[] values) - : this(new ReadOnlySpan(values ?? throw new ArgumentNullException(nameof(values)))) { + ArgumentNullException.ThrowIfNull(values); + + _array = CreateArray(values, out _bitLength); } /// @@ -303,23 +322,30 @@ public BitArray(int[] values) /// This constructor is an O(n) operation, where n is the number of elements in . /// public BitArray(ReadOnlySpan values) + { + _array = CreateArray(values, out _bitLength); + } + + private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) { if (values.Length > int.MaxValue / BitsPerInt32) { throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerInt32), nameof(values)); } - _bitLength = values.Length * BitsPerInt32; - _array = AllocateByteArray(_bitLength); + bitLength = values.Length * BitsPerInt32; + byte[] array = AllocateByteArray(bitLength); if (BitConverter.IsLittleEndian) { - MemoryMarshal.AsBytes(values).CopyTo(_array); + MemoryMarshal.AsBytes(values).CopyTo(array); } else { - BinaryPrimitives.ReverseEndianness(values, MemoryMarshal.Cast((Span)_array)); + BinaryPrimitives.ReverseEndianness(values, MemoryMarshal.Cast((Span)array)); } + + return array; } /// From 3c85de41558471ba4d1693b1602d71b3b412c714 Mon Sep 17 00:00:00 2001 From: Joshua Yue Date: Thu, 30 Jul 2026 16:00:22 -0700 Subject: [PATCH 4/4] Clarify BitArray span constructor tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b --- .../tests/BitArray/BitArray_CtorTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs b/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs index 5c8e11a8ccf9e0..13d638e3ee8b32 100644 --- a/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs +++ b/src/libraries/System.Collections/tests/BitArray/BitArray_CtorTests.cs @@ -263,26 +263,26 @@ public static void Ctor_EmptySpans() } [Fact] - public static void Ctor_SpanSlices() + public static void Ctor_SpanSlices_IgnoreElementsOutsideSlice() { - bool[] boolValues = [false, true, false, true, true, false]; - AssertBitArray(new BitArray(boolValues.AsSpan(1, 4)), [true, false, true, true]); + bool[] boolValuesWithSentinels = [false, true, false, true, true, false]; + AssertBitArray(new BitArray(boolValuesWithSentinels.AsSpan(1, 4)), [true, false, true, true]); - byte[] byteValues = [0xFF, 0x01, 0x80, 0xFF]; + byte[] byteValuesWithSentinels = [0xFF, 0x01, 0x80, 0xFF]; AssertBitArray( - new BitArray(byteValues.AsSpan(1, 2)), + new BitArray(byteValuesWithSentinels.AsSpan(1, 2)), [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true]); - int[] intValues = [-1, 1, int.MinValue, -1]; + int[] intValuesWithSentinels = [-1, 1, int.MinValue, -1]; bool[] expected = new bool[BitsPerInt32 * 2]; expected[0] = true; expected[^1] = true; - AssertBitArray(new BitArray(intValues.AsSpan(1, 2)), expected); + AssertBitArray(new BitArray(intValuesWithSentinels.AsSpan(1, 2)), expected); } [Fact] - public static void Ctor_StackAllocatedSpans() + public static void Ctor_StackAllocatedSpans_CopyValuesFromStackMemory() { ReadOnlySpan boolValues = stackalloc bool[] { true, false, true }; AssertBitArray(new BitArray(boolValues), boolValues);