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 66ffc9fee7bcd7..faae4247c6fb02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs @@ -142,8 +142,15 @@ private void ClearHighExtraBits() public BitArray(byte[] bytes) { ArgumentNullException.ThrowIfNull(bytes); + if (bytes.Length > int.MaxValue / BitsPerByte) + { + throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerByte), nameof(bytes)); + } - _array = CreateArray(bytes, out _bitLength); + _bitLength = bytes.Length * BitsPerByte; + _array = AllocateByteArray(_bitLength); + + Array.Copy(bytes, _array, bytes.Length); } /// @@ -161,22 +168,16 @@ 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; - byte[] array = AllocateByteArray(bitLength); + _bitLength = bytes.Length * BitsPerByte; + _array = AllocateByteArray(_bitLength); - bytes.CopyTo(array); - return array; + bytes.CopyTo(_array); } /// @@ -192,7 +193,77 @@ public BitArray(bool[] values) { ArgumentNullException.ThrowIfNull(values); - _array = CreateArray(values, out _bitLength); + _array = AllocateByteArray(values.Length); + _bitLength = values.Length; + + uint i = 0; + + if (!BitConverter.IsLittleEndian || values.Length < Vector256.Count) + { + goto Remainder; + } + + // 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. + + ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(_array); + ReadOnlySpan valuesAsBytes = MemoryMarshal.AsBytes(values.AsSpan()); + if (Vector512.IsHardwareAccelerated) + { + while (valuesAsBytes.Length >= Vector512.Count) + { + Vector512 vector = Vector512.Create(valuesAsBytes); + Vector512 isFalse = Vector512.Equals(vector, Vector512.Zero); + + ulong result = isFalse.ExtractMostSignificantBits(); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(ulong) * (i / 64u)), ~result); + i += (uint)Vector512.Count; + valuesAsBytes = valuesAsBytes.Slice(Vector512.Count); + } + } + else if (Vector256.IsHardwareAccelerated) + { + while (valuesAsBytes.Length >= Vector256.Count) + { + Vector256 vector = Vector256.Create(valuesAsBytes); + Vector256 isFalse = Vector256.Equals(vector, Vector256.Zero); + + uint result = isFalse.ExtractMostSignificantBits(); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), ~result); + i += (uint)Vector256.Count; + valuesAsBytes = valuesAsBytes.Slice(Vector256.Count); + } + } + else if (Vector128.IsHardwareAccelerated) + { + while (valuesAsBytes.Length >= Vector128.Count * 2) + { + Vector128 lowerVector = Vector128.Create(valuesAsBytes); + Vector128 lowerIsFalse = Vector128.Equals(lowerVector, Vector128.Zero); + uint lowerResult = lowerIsFalse.ExtractMostSignificantBits(); + + Vector128 upperVector = Vector128.Create(valuesAsBytes.Slice(Vector128.Count)); + Vector128 upperIsFalse = Vector128.Equals(upperVector, Vector128.Zero); + uint upperResult = upperIsFalse.ExtractMostSignificantBits(); + + Unsafe.WriteUnaligned( + ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), + ~((upperResult << 16) | lowerResult)); + i += (uint)Vector128.Count * 2u; + valuesAsBytes = valuesAsBytes.Slice(Vector128.Count * 2); + } + } + + Remainder: + for (; i < (uint)values.Length; i++) + { + if (values[i]) + { + (uint byteIndex, uint bitOffset) = Math.DivRem(i, BitsPerByte); + _array[byteIndex] |= (byte)(1 << (int)bitOffset); + } + } } /// @@ -205,15 +276,10 @@ public BitArray(bool[] values) /// public BitArray(ReadOnlySpan values) { - _array = CreateArray(values, out _bitLength); - } - - private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) - { - bitLength = values.Length; - byte[] array = AllocateByteArray(bitLength); + _array = AllocateByteArray(values.Length); + _bitLength = values.Length; - uint i = 0; + int i = 0; if (!BitConverter.IsLittleEndian || values.Length < Vector256.Count) { @@ -224,7 +290,7 @@ private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) // (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) { @@ -234,8 +300,8 @@ private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) Vector512 isFalse = Vector512.Equals(vector, Vector512.Zero); ulong result = isFalse.ExtractMostSignificantBits(); - Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(ulong) * (i / 64u)), ~result); - i += (uint)Vector512.Count; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(ulong) * (i / 64)), ~result); + i += Vector512.Count; valuesAsBytes = valuesAsBytes.Slice(Vector512.Count); } } @@ -247,8 +313,8 @@ private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) Vector256 isFalse = Vector256.Equals(vector, Vector256.Zero); uint result = isFalse.ExtractMostSignificantBits(); - Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), ~result); - i += (uint)Vector256.Count; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32)), ~result); + i += Vector256.Count; valuesAsBytes = valuesAsBytes.Slice(Vector256.Count); } } @@ -265,24 +331,22 @@ private static byte[] CreateArray(ReadOnlySpan values, out int bitLength) uint upperResult = upperIsFalse.ExtractMostSignificantBits(); Unsafe.WriteUnaligned( - ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), + ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32)), ~((upperResult << 16) | lowerResult)); - i += (uint)Vector128.Count * 2u; + i += Vector128.Count * 2; valuesAsBytes = valuesAsBytes.Slice(Vector128.Count * 2); } } Remainder: - for (; i < (uint)values.Length; i++) + for (; i < values.Length; i++) { - if (values[(int)i]) + if (values[i]) { - (uint byteIndex, uint bitOffset) = Math.DivRem(i, BitsPerByte); - array[byteIndex] |= (byte)(1 << (int)bitOffset); + (int byteIndex, int bitOffset) = Math.DivRem(i, BitsPerByte); + _array[byteIndex] |= (byte)(1 << bitOffset); } } - - return array; } /// @@ -303,8 +367,22 @@ ref Unsafe.Add(ref arrayRef, sizeof(uint) * (i / 32u)), public BitArray(int[] values) { ArgumentNullException.ThrowIfNull(values); + if (values.Length > int.MaxValue / BitsPerInt32) + { + throw new ArgumentException(SR.Format(SR.Argument_ArrayTooLarge, BitsPerInt32), nameof(values)); + } - _array = CreateArray(values, out _bitLength); + _bitLength = values.Length * BitsPerInt32; + _array = AllocateByteArray(_bitLength); + + if (BitConverter.IsLittleEndian) + { + MemoryMarshal.AsBytes(values).CopyTo(_array); + } + else + { + BinaryPrimitives.ReverseEndianness(values, MemoryMarshal.Cast((Span)_array)); + } } /// @@ -322,30 +400,23 @@ 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; - byte[] array = AllocateByteArray(bitLength); + _bitLength = values.Length * BitsPerInt32; + _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; } ///