diff --git a/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift b/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift index 24cdec23d..1a1b92f5f 100644 --- a/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift +++ b/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift @@ -2,13 +2,40 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // //===----------------------------------------------------------------------===// +#if false +// FIXME: Bitwise operators disabled for now. I have two concerns: +// 1. We need to support bitwise operations over slices of bit arrays, not just +// whole arrays. +// 2. We need to put in-place mutations as the primary operation, and they +// need to avoid copy-on-write copies unless absolutely necessary. +// +// It seems unlikely that the operator syntax will survive these points. +// We have five (5!) separate cases: +// +// foo |= bar +// foo[i ..< j] |= bar +// foo |= bar[u ..< v] +// foo[i ..< j] |= bar[u ..< v] +// foo[i ..< j] |= foo[k ..< l] +// +// The last one where the array is ORed with itself is particularly problematic +// -- like memcpy, these operations can easily support overlapping inputs, but +// it doesn't seem likely we can implement that with this nice slicing syntax, +// unless we are okay with forcing a CoW copy. (Which we aren't.) +// +// Even ignoring that, I would not like to end up with four overloads for each +// operator, especially not for such niche operations. So we'll entirely disable +// these for now, to prevent any shipping API from interfering with an eventual +// redesign. (This is an active area of experimentation, as it will potentially +// also affect our non-copyable container design.) + extension BitArray { /// Stores the result of performing a bitwise OR operation on two /// equal-sized bit arrays in the left-hand-side variable. @@ -105,7 +132,9 @@ extension BitArray { result ^= right return result } +} +extension BitArray { /// Returns the complement of the given bit array. /// /// - Parameter value: A bit array. @@ -117,7 +146,10 @@ extension BitArray { result.toggleAll() return result } +} +#endif +extension BitArray { public mutating func toggleAll() { _update { handle in let w = handle._mutableWords diff --git a/Sources/BitCollections/BitCollections.docc/Extensions/BitArray.md b/Sources/BitCollections/BitCollections.docc/Extensions/BitArray.md index 141370019..85503869a 100644 --- a/Sources/BitCollections/BitCollections.docc/Extensions/BitArray.md +++ b/Sources/BitCollections/BitCollections.docc/Extensions/BitArray.md @@ -59,12 +59,16 @@ - ``replaceSubrange(_:with:)-2i7lu`` - ``replaceSubrange(_:with:)-b5ou`` -### Bitwise Operators - -- ``&(_:_:)`` -- ``_(_:_:)-1rhw`` -- ``_(_:_:)-56s54`` -- ``~(_:)`` -- ``&=(_:_:)`` -- ``_=(_:_:)-69yf0`` -- ``_=(_:_:)-icef`` +### Bitwise Operations + +- ``toggleAll()`` +- ``toggleAll(in:)-3duwn`` +- ``toggleAll(in:)-5hfhl`` + + + + + + + + diff --git a/Tests/BitCollectionsTests/BitArrayTests.swift b/Tests/BitCollectionsTests/BitArrayTests.swift index c50e0f0c7..6072132f7 100644 --- a/Tests/BitCollectionsTests/BitArrayTests.swift +++ b/Tests/BitCollectionsTests/BitArrayTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -822,6 +822,7 @@ final class BitArrayTests: CollectionTestCase { } } + #if false // FIXME: Bitwise operations disabled for now func test_bitwiseOr() { withSome("count", in: 0 ..< 512, maxSamples: 100) { count in withEvery("i", in: 0 ..< 10) { i in @@ -887,7 +888,42 @@ final class BitArrayTests: CollectionTestCase { } } } - + #endif + + func test_toggleAll() { + withSome("count", in: 0 ..< 512, maxSamples: 100) { count in + withEvery("i", in: 0 ..< 10) { i in + let a = randomBoolArray(count: count) + + var b = BitArray(a) + b.toggleAll() + + let expected = a.map { !$0 } + + expectEqualElements(b, expected) + } + } + } + + func test_toggleAll_range() { + withSome("count", in: 0 ..< 512, maxSamples: 50) { count in + let a = randomBoolArray(count: count) + + withSomeRanges("range", in: 0 ..< count, maxSamples: 100) { range in + withEvery("shared", in: [false, true]) { shared in + var expected = a + for i in range { expected[i].toggle() } + + var b = BitArray(a) + withHiddenCopies(if: shared, of: &b) { b in + b.toggleAll(in: range) + expectEqualElements(b, expected) + } + } + } + } + } + func test_truncateOrExtend() { withSome("oldCount", in: 0 ..< 512, maxSamples: 50) { oldCount in withSome("newCount", in: 0 ... 1024, maxSamples: 30) { newCount in