diff --git a/.gitignore b/.gitignore index 397ae000f..cb33e5fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ xcuserdata/ /Benchmarks/.swiftpm /Benchmarks/.build .docc-build +__pycache__ diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 000000000..2aecf0613 --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [Collections, BitCollections, DequeModule, HashTreeCollections, HeapModule, OrderedCollections] diff --git a/Package.swift b/Package.swift index 88222155b..130288daa 100644 --- a/Package.swift +++ b/Package.swift @@ -1,9 +1,9 @@ -// swift-tools-version:5.5 +// swift-tools-version:5.6 //===----------------------------------------------------------------------===// // // 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-2023 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 @@ -17,7 +17,7 @@ import PackageDescription // from the package manager command line: // // swift build -Xswiftc -DCOLLECTIONS_INTERNAL_CHECKS -var settings: [SwiftSetting] = [ +var defines: [String] = [ // Enables internal consistency checks at the end of initializers and // mutating operations. This can have very significant overhead, so enabling @@ -26,7 +26,7 @@ var settings: [SwiftSetting] = [ // This is mostly useful while debugging an issue with the implementation of // the hash table itself. This setting should never be enabled in production // code. -// .define("COLLECTIONS_INTERNAL_CHECKS"), +// "COLLECTIONS_INTERNAL_CHECKS", // Hashing collections provided by this package usually seed their hash // function with the address of the memory location of their storage, @@ -40,121 +40,281 @@ var settings: [SwiftSetting] = [ // This is mostly useful while debugging an issue with the implementation of // the hash table itself. This setting should never be enabled in production // code. -// .define("COLLECTIONS_DETERMINISTIC_HASHING"), +// "COLLECTIONS_DETERMINISTIC_HASHING", + // Enables randomized testing of some data structure implementations. + "COLLECTIONS_RANDOMIZED_TESTING", + + // Enable this to build the sources as a single, large module. + // This removes the distinct modules for each data structure, instead + // putting them all directly into the `Collections` module. + // Note: This is a source-incompatible variation of the default configuration. +// "COLLECTIONS_SINGLE_MODULE", ] -let package = Package( - name: "swift-collections", - products: [ +var _settings: [SwiftSetting] = defines.map { .define($0) } + +struct CustomTarget { + enum Kind { + case exported + case hidden + case test + case testSupport + } + + var kind: Kind + var name: String + var dependencies: [Target.Dependency] + var directory: String + var exclude: [String] +} + +extension CustomTarget.Kind { + func path(for name: String) -> String { + switch self { + case .exported, .hidden: return "Sources/\(name)" + case .test, .testSupport: return "Tests/\(name)" + } + } + + var isTest: Bool { + switch self { + case .exported, .hidden: return false + case .test, .testSupport: return true + } + } +} + +extension CustomTarget { + static func target( + kind: Kind, + name: String, + dependencies: [Target.Dependency] = [], + directory: String? = nil, + exclude: [String] = [] + ) -> CustomTarget { + CustomTarget( + kind: kind, + name: name, + dependencies: dependencies, + directory: directory ?? name, + exclude: exclude) + } + + func toTarget() -> Target { + var linkerSettings: [LinkerSetting] = [] + if kind == .testSupport { + linkerSettings.append( + .linkedFramework("XCTest", .when(platforms: [.macOS, .iOS, .watchOS, .tvOS]))) + } + switch kind { + case .exported, .hidden, .testSupport: + return Target.target( + name: name, + dependencies: dependencies, + path: kind.path(for: directory), + exclude: exclude, + swiftSettings: _settings, + linkerSettings: linkerSettings) + case .test: + return Target.testTarget( + name: name, + dependencies: dependencies, + path: kind.path(for: directory), + exclude: exclude, + swiftSettings: _settings, + linkerSettings: linkerSettings) + } + } +} + +extension Array where Element == CustomTarget { + func toMonolithicTarget( + name: String, + linkerSettings: [LinkerSetting] = [] + ) -> Target { + let targets = self.filter { !$0.kind.isTest } + return Target.target( + name: name, + path: "Sources", + exclude: [ + "CMakeLists.txt", + "BitCollections/BitCollections.docc", + "Collections/Collections.docc", + "DequeModule/DequeModule.docc", + "HashTreeCollections/HashTreeCollections.docc", + "HeapModule/HeapModule.docc", + "OrderedCollections/OrderedCollections.docc", + ] + targets.flatMap { t in + t.exclude.map { "\(t.name)/\($0)" } + }, + sources: targets.map { "\($0.directory)" }, + swiftSettings: _settings, + linkerSettings: linkerSettings) + } + + func toMonolithicTestTarget( + name: String, + dependencies: [Target.Dependency] = [], + linkerSettings: [LinkerSetting] = [] + ) -> Target { + let targets = self.filter { $0.kind.isTest } + return Target.testTarget( + name: name, + dependencies: dependencies, + path: "Tests", + exclude: [ + "README.md", + ] + targets.flatMap { t in + t.exclude.map { "\(t.name)/\($0)" } + }, + sources: targets.map { "\($0.name)" }, + swiftSettings: _settings, + linkerSettings: linkerSettings) + } +} + +let targets: [CustomTarget] = [ + .target( + kind: .testSupport, + name: "_CollectionsTestSupport", + dependencies: ["_CollectionsUtilities"]), + .target( + kind: .test, + name: "CollectionsTestSupportTests", + dependencies: ["_CollectionsTestSupport"]), + .target( + kind: .hidden, + name: "_CollectionsUtilities", + exclude: [ + "CMakeLists.txt", + "Compatibility/Array+WithContiguousStorage Compatibility.swift.gyb", + "Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb", + "Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb", + "Compatibility/UnsafeRawPointer extensions.swift.gyb", + "Debugging.swift.gyb", + "Descriptions.swift.gyb", + "IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb", + "IntegerTricks/Integer rank.swift.gyb", + "IntegerTricks/UInt+first and last set bit.swift.gyb", + "IntegerTricks/UInt+reversed.swift.gyb", + "RandomAccessCollection+Offsets.swift.gyb", + "UnsafeBitSet/_UnsafeBitSet+Index.swift.gyb", + "UnsafeBitSet/_UnsafeBitSet+_Word.swift.gyb", + "UnsafeBitSet/_UnsafeBitSet.swift.gyb", + "UnsafeBufferPointer+Extras.swift.gyb", + "UnsafeMutableBufferPointer+Extras.swift.gyb", + ]), + + .target( + kind: .exported, + name: "BitCollections", + dependencies: ["_CollectionsUtilities"], + exclude: ["CMakeLists.txt"]), + .target( + kind: .test, + name: "BitCollectionsTests", + dependencies: [ + "BitCollections", "_CollectionsTestSupport", "OrderedCollections" + ]), + + .target( + kind: .exported, + name: "DequeModule", + dependencies: ["_CollectionsUtilities"], + exclude: ["CMakeLists.txt"]), + .target( + kind: .test, + name: "DequeTests", + dependencies: ["DequeModule", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "HashTreeCollections", + dependencies: ["_CollectionsUtilities"], + exclude: ["CMakeLists.txt"]), + .target( + kind: .test, + name: "HashTreeCollectionsTests", + dependencies: ["HashTreeCollections", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "HeapModule", + dependencies: ["_CollectionsUtilities"], + exclude: ["CMakeLists.txt"]), + .target( + kind: .test, + name: "HeapTests", + dependencies: ["HeapModule", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "OrderedCollections", + dependencies: ["_CollectionsUtilities"], + exclude: ["CMakeLists.txt"]), + .target( + kind: .test, + name: "OrderedCollectionsTests", + dependencies: ["OrderedCollections", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "_RopeModule", + dependencies: ["_CollectionsUtilities"], + directory: "RopeModule"), + .target( + kind: .test, + name: "RopeModuleTests", + dependencies: ["_RopeModule", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "SortedCollections", + dependencies: ["_CollectionsUtilities"], + directory: "SortedCollections"), + .target( + kind: .test, + name: "SortedCollectionsTests", + dependencies: ["SortedCollections", "_CollectionsTestSupport"]), + + .target( + kind: .exported, + name: "Collections", + dependencies: [ + "BitCollections", + "DequeModule", + "HashTreeCollections", + "HeapModule", + "OrderedCollections", + "_RopeModule", + "SortedCollections", + ], + exclude: ["CMakeLists.txt"]) +] + +var _products: [Product] = [] +var _targets: [Target] = [] +if defines.contains("COLLECTIONS_SINGLE_MODULE") { + _products = [ .library(name: "Collections", targets: ["Collections"]), - .library(name: "BitCollections", targets: ["BitCollections"]), - .library(name: "DequeModule", targets: ["DequeModule"]), - .library(name: "HeapModule", targets: ["HeapModule"]), - .library(name: "OrderedCollections", targets: ["OrderedCollections"]), - .library(name: "HashTreeCollections", targets: ["HashTreeCollections"]), - .library(name: "SortedCollections", targets: ["SortedCollections"]), - ], - targets: [ - .target( - name: "Collections", - dependencies: [ - "BitCollections", - "DequeModule", - "HeapModule", - "OrderedCollections", - "HashTreeCollections", - "SortedCollections", - ], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - - // Testing support module - .target( - name: "_CollectionsTestSupport", - dependencies: ["_CollectionsUtilities"], - swiftSettings: settings, - linkerSettings: [ - .linkedFramework( - "XCTest", - .when(platforms: [.macOS, .iOS, .watchOS, .tvOS])), - ] - ), - .testTarget( - name: "CollectionsTestSupportTests", - dependencies: ["_CollectionsTestSupport"], - swiftSettings: settings), - - .target( - name: "_CollectionsUtilities", - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - - // BitSet, BitArray - .target( - name: "BitCollections", - dependencies: ["_CollectionsUtilities"], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - .testTarget( - name: "BitCollectionsTests", - dependencies: [ - "BitCollections", "_CollectionsTestSupport", "OrderedCollections" - ], - swiftSettings: settings), - - // Deque - .target( - name: "DequeModule", - dependencies: ["_CollectionsUtilities"], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - .testTarget( - name: "DequeTests", - dependencies: ["DequeModule", "_CollectionsTestSupport"], - swiftSettings: settings), - - // Heap - .target( - name: "HeapModule", - dependencies: ["_CollectionsUtilities"], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - .testTarget( - name: "HeapTests", - dependencies: ["HeapModule", "_CollectionsTestSupport"], - swiftSettings: settings), - - // OrderedSet, OrderedDictionary - .target( - name: "OrderedCollections", - dependencies: ["_CollectionsUtilities"], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - .testTarget( - name: "OrderedCollectionsTests", - dependencies: ["OrderedCollections", "_CollectionsTestSupport"], - swiftSettings: settings), - - // TreeSet, TreeDictionary - .target( - name: "HashTreeCollections", - dependencies: ["_CollectionsUtilities"], - exclude: ["CMakeLists.txt"], - swiftSettings: settings), - .testTarget( - name: "HashTreeCollectionsTests", - dependencies: ["HashTreeCollections", "_CollectionsTestSupport"], - swiftSettings: settings), - - // SortedSet, SortedDictionary - .target( - name: "SortedCollections", - dependencies: ["_CollectionsUtilities"], - swiftSettings: settings), - .testTarget( - name: "SortedCollectionsTests", - dependencies: ["SortedCollections", "_CollectionsTestSupport"], - swiftSettings: settings), ] + _targets = [ + targets.toMonolithicTarget(name: "Collections"), + targets.toMonolithicTestTarget( + name: "CollectionsTests", + dependencies: ["Collections"]), + ] +} else { + _products = targets.compactMap { t in + guard t.kind == .exported else { return nil } + return .library(name: t.name, targets: [t.name]) + } + _targets = targets.map { $0.toTarget() } +} + +let package = Package( + name: "swift-collections", + products: _products, + targets: _targets ) diff --git a/README.md b/README.md index 97e664816..392393be5 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ The following table maps existing package releases to their minimum required Swi | Package version | Swift version | Xcode release | | ----------------------- | ------------- | ------------- | | swift-collections 1.0.x | >= Swift 5.3 | >= Xcode 12 | -| swift-collections 1.1.x | >= Swift 5.5 | >= Xcode 13 | +| swift-collections 1.1.x | >= Swift 5.6 | >= Xcode 13.3 | (Note: the package has no minimum deployment target, so while it does require clients to use a recent Swift toolchain to build it, the code itself is able to run on any OS release that supports running Swift code.) diff --git a/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift b/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift index 770a9bbcf..24cdec23d 100644 --- a/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.swift +++ b/Sources/BitCollections/BitArray/BitArray+BitwiseOperations.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-2023 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 @@ -114,16 +114,50 @@ extension BitArray { /// - Complexity: O(value.count) public static prefix func ~(value: Self) -> Self { var result = value - result._update { handle in + result.toggleAll() + return result + } + + public mutating func toggleAll() { + _update { handle in + let w = handle._mutableWords for i in 0 ..< handle._words.count { - handle._mutableWords[i].formComplement() + w[i].formComplement() } - let p = _BitPosition(handle._count) + let p = handle.end if p.bit > 0 { - handle._mutableWords[p.word].subtract(_Word(upTo: p.bit).complement()) + w[p.word].subtract(_Word(upTo: p.bit).complement()) } } - result._checkInvariants() - return result + _checkInvariants() + } + + public mutating func toggleAll(in range: Range) { + precondition(range.upperBound <= count, "Range out of bounds") + _update { handle in + let words = handle._mutableWords + let start = _BitPosition(range.lowerBound) + let end = _BitPosition(range.upperBound) + if start.word == end.word { + let bits = _Word(from: start.bit, to: end.bit) + words[start.word].formSymmetricDifference(bits) + return + } + words[start.word].formSymmetricDifference( + _Word(upTo: start.bit).complement()) + for i in stride(from: start.word + 1, to: end.word, by: 1) { + words[i].formComplement() + } + if end.bit > 0 { + words[end.word].formSymmetricDifference(_Word(upTo: end.bit)) + } + } + } + + @inlinable + public mutating func toggleAll( + in range: R + ) where R.Bound == Int { + toggleAll(in: range.relative(to: self)) } } diff --git a/Sources/BitCollections/BitArray/BitArray+ChunkedBitsIterators.swift b/Sources/BitCollections/BitArray/BitArray+ChunkedBitsIterators.swift index 2f50b9214..8771d6a0b 100644 --- a/Sources/BitCollections/BitArray/BitArray+ChunkedBitsIterators.swift +++ b/Sources/BitCollections/BitArray/BitArray+ChunkedBitsIterators.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif internal struct _ChunkedBitsForwardIterator { internal typealias _BitPosition = _UnsafeBitSet.Index diff --git a/Sources/BitCollections/BitArray/BitArray+Codable.swift b/Sources/BitCollections/BitArray/BitArray+Codable.swift index f8a0a8b5e..1d2c78571 100644 --- a/Sources/BitCollections/BitArray/BitArray+Codable.swift +++ b/Sources/BitCollections/BitArray/BitArray+Codable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+Collection.swift b/Sources/BitCollections/BitArray/BitArray+Collection.swift index 728909c3e..0c7385a88 100644 --- a/Sources/BitCollections/BitArray/BitArray+Collection.swift +++ b/Sources/BitCollections/BitArray/BitArray+Collection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+Copy.swift b/Sources/BitCollections/BitArray/BitArray+Copy.swift index 1f339c333..b275434ca 100644 --- a/Sources/BitCollections/BitArray/BitArray+Copy.swift +++ b/Sources/BitCollections/BitArray/BitArray+Copy.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitArray { internal mutating func _copy( diff --git a/Sources/BitCollections/BitArray/BitArray+CustomReflectable.swift b/Sources/BitCollections/BitArray/BitArray+CustomReflectable.swift index a61d3b77a..89cbe6e57 100644 --- a/Sources/BitCollections/BitArray/BitArray+CustomReflectable.swift +++ b/Sources/BitCollections/BitArray/BitArray+CustomReflectable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+CustomStringConvertible.swift b/Sources/BitCollections/BitArray/BitArray+CustomStringConvertible.swift deleted file mode 100644 index 91357755e..000000000 --- a/Sources/BitCollections/BitArray/BitArray+CustomStringConvertible.swift +++ /dev/null @@ -1,41 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 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 -// -//===----------------------------------------------------------------------===// - -extension BitArray: CustomStringConvertible { - // A textual representation of this instance. - public var description: String { - _bitString - } - - internal var _bitString: String { - var result: String - if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { - result = String(unsafeUninitializedCapacity: self.count) { target in - let b0: UInt8 = 48 // ASCII 0 - let b1: UInt8 = 49 // ASCII 1 - var i = 0 - for v in self { - target.initializeElement(at: i, to: v ? b1 : b0) - i &+= 1 - } - return i - } - } else { - result = "" - result.reserveCapacity(self.count) - for v in self { - result.append(v ? "1" : "0") - } - } - if result.isEmpty { result = "0" } - return result - } -} diff --git a/Sources/BitCollections/BitArray/BitArray+Descriptions.swift b/Sources/BitCollections/BitArray/BitArray+Descriptions.swift new file mode 100644 index 000000000..be4f04219 --- /dev/null +++ b/Sources/BitCollections/BitArray/BitArray+Descriptions.swift @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022-2023 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 +// +//===----------------------------------------------------------------------===// + +extension UInt8 { + @inline(__always) + internal static var _ascii0: Self { 48 } + + @inline(__always) + internal static var _ascii1: Self { 49 } +} + +extension BitArray: CustomStringConvertible { + /// A textual representation of this instance. + /// Bit arrays print themselves as a string of binary bits, with the highest-indexed elements + /// appearing first, as in the binary representation of integers: + /// + /// let bits: BitArray = [false, false, false, true, true] + /// print(bits) // "11000" + /// + /// The description of an empty bit array is an empty string. + public var description: String { + _bitString + } +} + +extension BitArray: CustomDebugStringConvertible { + /// A textual representation of this instance, suitable for debugging. + public var debugDescription: String { + "BitArray(\(_bitString))" + } +} + +extension BitArray { + internal var _bitString: String { + guard !isEmpty else { return "" } + var result: String + if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + result = String(unsafeUninitializedCapacity: self.count) { target in + var i = count - 1 + for v in self { + target.initializeElement(at: i, to: v ? ._ascii1 : ._ascii0) + i &-= 1 + } + return count + } + } else { + result = "" + result.reserveCapacity(self.count) + for v in self.reversed() { + result.append(v ? "1" : "0") + } + } + return result + } +} diff --git a/Sources/BitCollections/BitArray/BitArray+Equatable.swift b/Sources/BitCollections/BitArray/BitArray+Equatable.swift index d7926a9e2..5741ce6b4 100644 --- a/Sources/BitCollections/BitArray/BitArray+Equatable.swift +++ b/Sources/BitCollections/BitArray/BitArray+Equatable.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-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+ExpressibleByArrayLiteral.swift b/Sources/BitCollections/BitArray/BitArray+ExpressibleByArrayLiteral.swift index e10c0e3b6..0271097bb 100644 --- a/Sources/BitCollections/BitArray/BitArray+ExpressibleByArrayLiteral.swift +++ b/Sources/BitCollections/BitArray/BitArray+ExpressibleByArrayLiteral.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+ExpressibleByStringLiteral.swift b/Sources/BitCollections/BitArray/BitArray+ExpressibleByStringLiteral.swift new file mode 100644 index 000000000..a886a557d --- /dev/null +++ b/Sources/BitCollections/BitArray/BitArray+ExpressibleByStringLiteral.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension BitArray: ExpressibleByStringLiteral { + /// Creates an instance initialized with the given elements. + @inlinable + public init(stringLiteral value: String) { + guard let bits = Self(value) else { + fatalError("Invalid bit array literal") + } + self = bits + } +} diff --git a/Sources/BitCollections/BitArray/BitArray+Extras.swift b/Sources/BitCollections/BitArray/BitArray+Extras.swift index a6554509d..6bf4c0b15 100644 --- a/Sources/BitCollections/BitArray/BitArray+Extras.swift +++ b/Sources/BitCollections/BitArray/BitArray+Extras.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2023 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 @@ -28,3 +28,24 @@ extension BitArray { } } } + +extension BitArray { + public mutating func insert( + repeating value: Bool, + count: Int, + at index: Int + ) { + precondition(count >= 0, "Can't insert a negative number of values") + precondition(index >= 0 && index <= count, "Index out of bounds") + guard count > 0 else { return } + _extend(by: count, with: false) + _copy(from: index ..< self.count - count, to: index + count) + fill(in: index ..< index + count, with: value) + } + + public mutating func append(repeating value: Bool, count: Int) { + precondition(count >= 0, "Can't append a negative number of values") + guard count > 0 else { return } + _extend(by: count, with: value) + } +} diff --git a/Sources/BitCollections/BitArray/BitArray+Fill.swift b/Sources/BitCollections/BitArray/BitArray+Fill.swift index bf42c3388..6c8223f6a 100644 --- a/Sources/BitCollections/BitArray/BitArray+Fill.swift +++ b/Sources/BitCollections/BitArray/BitArray+Fill.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 @@ -31,4 +31,11 @@ extension BitArray { } } } + + public mutating func fill( + in range: R, + with value: Bool = true + ) where R.Bound == Int { + fill(in: range.relative(to: self), with: value) + } } diff --git a/Sources/BitCollections/BitArray/BitArray+Hashable.swift b/Sources/BitCollections/BitArray/BitArray+Hashable.swift index d8ff3da86..2e5726ed8 100644 --- a/Sources/BitCollections/BitArray/BitArray+Hashable.swift +++ b/Sources/BitCollections/BitArray/BitArray+Hashable.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-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray+Initializers.swift b/Sources/BitCollections/BitArray/BitArray+Initializers.swift index 155740c73..5269a7362 100644 --- a/Sources/BitCollections/BitArray/BitArray+Initializers.swift +++ b/Sources/BitCollections/BitArray/BitArray+Initializers.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 @@ -12,6 +12,16 @@ extension BitArray { /// Initialize a bit array from a bit set. /// + /// The result contains exactly as many bits as the maximum item in + /// the source set, plus one. If the set is empty, the result will + /// be empty, too. + /// + /// BitArray([] as BitSet) // (empty) + /// BitArray([0] as BitSet) // 1 + /// BitArray([1] as BitSet) // 10 + /// BitArray([1, 2, 4] as BitSet) // 1011 + /// BitArray([7] as BitSet) // 1000000 + /// /// - Complexity: O(1) public init(_ set: BitSet) { guard let l = set.last else { self.init(); return } @@ -19,11 +29,243 @@ extension BitArray { } /// Initialize a bit array from the binary representation of an integer. + /// The result will contain exactly `value.bitWidth` bits. + /// + /// BitArray(bitPattern: 3 as UInt8) // 00000011 + /// BitArray(bitPattern: 42 as Int8) // 00101010 + /// BitArray(bitPattern: -1 as Int8) // 11111111 + /// BitArray(bitPattern: 3 as Int16) // 0000000000000111 + /// BitArray(bitPattern: 42 as Int16) // 0000000000101010 + /// BitArray(bitPattern: -1 as Int16) // 1111111111111111 + /// BitArray(bitPattern: 3 as Int) // 0000000000...0000000111 + /// BitArray(bitPattern: 42 as Int) // 0000000000...0000101010 + /// BitArray(bitPattern: -1 as Int) // 1111111111...1111111111 /// /// - Complexity: O(value.bitWidth) public init(bitPattern value: I) { - let words = value.words.map { _Word($0) } + var words = value.words.map { _Word($0) } let count = value.bitWidth + if words.isEmpty { + precondition(count == 0, "Inconsistent bitWidth") + } else { + let (w, b) = _UnsafeHandle._BitPosition(count).endSplit + precondition(words.count == w + 1, "Inconsistent bitWidth") + words[w].formIntersection(_Word(upTo: b)) + } self.init(_storage: words, count: count) } + + /// Creates a new, empty bit array with preallocated space for at least the + /// specified number of elements. + public init(minimumCapacity: Int) { + self.init() + reserveCapacity(minimumCapacity) + } +} + +extension BinaryInteger { + @inlinable + internal static func _convert( + _ source: BitArray + ) -> (value: Self, isNegative: Bool) { + var value: Self = .zero + let isNegative = source._foreachTwosComplementWordDownward( + isSigned: Self.isSigned + ) { _, word in + value <<= UInt.bitWidth + value |= Self(truncatingIfNeeded: word) + return true + } + return (value, isNegative) + } + + /// Creates a new instance by truncating or extending the bits in the given + /// bit array, as needed. The bit at position 0 in `source` will correspond + /// to the least-significant bit in the result. + /// + /// If `Self` is an unsigned integer type, then the result will contain as + /// many bits from `source` it can accommodate, truncating off any extras. + /// + /// UInt8(truncatingIfNeeded: "" as BitArray) // 0 + /// UInt8(truncatingIfNeeded: "0" as BitArray) // 0 + /// UInt8(truncatingIfNeeded: "1" as BitArray) // 1 + /// UInt8(truncatingIfNeeded: "11" as BitArray) // 3 + /// UInt8(truncatingIfNeeded: "11111111" as BitArray) // 255 + /// UInt8(truncatingIfNeeded: "1100000001" as BitArray) // 1 + /// UInt8(truncatingIfNeeded: "1100000101" as BitArray) // 5 + /// + /// If `Self` is a signed integer type, then the contents of the bit array + /// are interpreted to be a two's complement representation of a signed + /// integer value, with the last bit in the array representing the sign of + /// the result. + /// + /// Int8(truncatingIfNeeded: "" as BitArray) // 0 + /// Int8(truncatingIfNeeded: "0" as BitArray) // 0 + /// Int8(truncatingIfNeeded: "1" as BitArray) // -1 + /// Int8(truncatingIfNeeded: "01" as BitArray) // 1 + /// Int8(truncatingIfNeeded: "101" as BitArray) // -3 + /// Int8(truncatingIfNeeded: "0101" as BitArray) // 5 + /// + /// Int8(truncatingIfNeeded: "00000001" as BitArray) // 1 + /// Int8(truncatingIfNeeded: "00000101" as BitArray) // 5 + /// Int8(truncatingIfNeeded: "01111111" as BitArray) // 127 + /// Int8(truncatingIfNeeded: "10000000" as BitArray) // -128 + /// Int8(truncatingIfNeeded: "11111111" as BitArray) // -1 + /// + /// Int8(truncatingIfNeeded: "000011111111" as BitArray) // -1 + /// Int8(truncatingIfNeeded: "111100000000" as BitArray) // 0 + /// Int8(truncatingIfNeeded: "111100000001" as BitArray) // 1 + @inlinable + public init(truncatingIfNeeded source: BitArray) { + self = Self._convert(source).value + } + + /// Creates a new instance from the bits in the given bit array, if the + /// corresponding integer value can be represented exactly. + /// If the value is not representable exactly, then the result is `nil`. + /// + /// If `Self` is an unsigned integer type, then the contents of the bit array + /// are interpreted to be the binary representation of a nonnegative + /// integer value. The bit array is allowed to contain bits in unrepresentable + /// positions, as long as they are all cleared. + /// + /// UInt8(exactly: "" as BitArray) // 0 + /// UInt8(exactly: "0" as BitArray) // 0 + /// UInt8(exactly: "1" as BitArray) // 1 + /// UInt8(exactly: "10" as BitArray) // 2 + /// UInt8(exactly: "00000000" as BitArray) // 0 + /// UInt8(exactly: "11111111" as BitArray) // 255 + /// UInt8(exactly: "0000000000000" as BitArray) // 0 + /// UInt8(exactly: "0000011111111" as BitArray) // 255 + /// UInt8(exactly: "0000100000000" as BitArray) // nil + /// UInt8(exactly: "1111111111111" as BitArray) // nil + /// + /// If `Self` is a signed integer type, then the contents of the bit array + /// are interpreted to be a two's complement representation of a signed + /// integer value, with the last bit in the array representing the sign of + /// the result. + /// + /// Int8(exactly: "" as BitArray) // 0 + /// Int8(exactly: "0" as BitArray) // 0 + /// Int8(exactly: "1" as BitArray) // -1 + /// Int8(exactly: "01" as BitArray) // 1 + /// Int8(exactly: "101" as BitArray) // -3 + /// Int8(exactly: "0101" as BitArray) // 5 + /// + /// Int8(exactly: "00000001" as BitArray) // 1 + /// Int8(exactly: "00000101" as BitArray) // 5 + /// Int8(exactly: "01111111" as BitArray) // 127 + /// Int8(exactly: "10000000" as BitArray) // -128 + /// Int8(exactly: "11111111" as BitArray) // -1 + /// + /// Int8(exactly: "00000000000" as BitArray) // 0 + /// Int8(exactly: "00001111111" as BitArray) // 127 + /// Int8(exactly: "00010000000" as BitArray) // nil + /// Int8(exactly: "11101111111" as BitArray) // nil + /// Int8(exactly: "11110000000" as BitArray) // -128 + /// Int8(exactly: "11111111111" as BitArray) // -1 + @inlinable + public init?(exactly source: BitArray) { + let (value, isNegative) = Self._convert(source) + guard isNegative == (value < 0) else { return nil } + let words = value.words + var equal = true + _ = source._foreachTwosComplementWordDownward(isSigned: Self.isSigned) { i, word in + assert(equal) + let w = ( + i < words.count ? words[i] + : isNegative ? UInt.max + : UInt.zero) + equal = (w == word) + return equal + } + guard equal else { return nil } + self = value + } + + /// Creates a new instance from the bits in the given bit array, if the + /// corresponding integer value can be represented exactly. + /// If the value is not representable exactly, then a runtime error will + /// occur. + /// + /// If `Self` is an unsigned integer type, then the contents of the bit array + /// are interpreted to be the binary representation of a nonnegative + /// integer value. The bit array is allowed to contain bits in unrepresentable + /// positions, as long as they are all cleared. + /// + /// UInt8("" as BitArray) // 0 + /// UInt8("0" as BitArray) // 0 + /// UInt8("1" as BitArray) // 1 + /// UInt8("10" as BitArray) // 2 + /// UInt8("00000000" as BitArray) // 0 + /// UInt8("11111111" as BitArray) // 255 + /// UInt8("0000000000000" as BitArray) // 0 + /// UInt8("0000011111111" as BitArray) // 255 + /// UInt8("0000100000000" as BitArray) // ERROR + /// UInt8("1111111111111" as BitArray) // ERROR + /// + /// If `Self` is a signed integer type, then the contents of the bit array + /// are interpreted to be a two's complement representation of a signed + /// integer value, with the last bit in the array representing the sign of + /// the result. + /// + /// Int8("" as BitArray) // 0 + /// Int8("0" as BitArray) // 0 + /// Int8("1" as BitArray) // -1 + /// Int8("01" as BitArray) // 1 + /// Int8("101" as BitArray) // -3 + /// Int8("0101" as BitArray) // 5 + /// + /// Int8("00000001" as BitArray) // 1 + /// Int8("00000101" as BitArray) // 5 + /// Int8("01111111" as BitArray) // 127 + /// Int8("10000000" as BitArray) // -128 + /// Int8("11111111" as BitArray) // -1 + /// + /// Int8("00000000000" as BitArray) // 0 + /// Int8("00001111111" as BitArray) // 127 + /// Int8("00010000000" as BitArray) // ERROR + /// Int8("11101111111" as BitArray) // ERROR + /// Int8("11110000000" as BitArray) // -128 + /// Int8("11111111111" as BitArray) // -1 + @inlinable + public init(_ source: BitArray) { + guard let value = Self(exactly: source) else { + fatalError(""" + BitArray value cannot be converted to \(Self.self) because it is \ + outside the representable range + """) + } + self = value + } +} + +extension BitArray { + @usableFromInline + internal func _foreachTwosComplementWordDownward( + isSigned: Bool, + body: (Int, UInt) -> Bool + ) -> Bool { + self._read { + guard $0._words.count > 0 else { return false } + + var isNegative = false + let end = $0.end.endSplit + assert(end.bit > 0) + let last = $0._words[end.word] + if isSigned, last.contains(end.bit - 1) { + // Sign extend last word + isNegative = true + if !body(end.word, last.union(_Word(upTo: end.bit).complement()).value) { + return isNegative + } + } else if !body(end.word, last.value) { + return isNegative + } + for i in stride(from: end.word - 1, through: 0, by: -1) { + if !body(i, $0._words[i].value) { return isNegative } + } + return isNegative + } + } } diff --git a/Sources/BitCollections/BitArray/BitArray+Invariants.swift b/Sources/BitCollections/BitArray/BitArray+Invariants.swift index d194ea0b4..280bfa4eb 100644 --- a/Sources/BitCollections/BitArray/BitArray+Invariants.swift +++ b/Sources/BitCollections/BitArray/BitArray+Invariants.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitArray { /// True if consistency checking is enabled in the implementation of this diff --git a/Sources/BitCollections/BitArray/BitArray+LosslessStringConvertible.swift b/Sources/BitCollections/BitArray/BitArray+LosslessStringConvertible.swift new file mode 100644 index 000000000..2166b166b --- /dev/null +++ b/Sources/BitCollections/BitArray/BitArray+LosslessStringConvertible.swift @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension BitArray: LosslessStringConvertible { + /// Initializes a new bit array from a string representation. + /// + /// The string given is interpreted as if it was the binary representation + /// of an integer value, consisting of the digits `0` and `1`, with the + /// highest digits appearing first. + /// + /// BitArray("") // [] + /// BitArray("001") // [true, false, false] + /// BitArray("1110") // [false, true, true, true] + /// BitArray("42") // nil + /// BitArray("Foo") // nil + /// + public init?(_ description: String) { + let bits: BitArray? = description.utf8.withContiguousStorageIfAvailable { buffer in + Self(_utf8: buffer) + } ?? Self(_utf8: description.utf8) + guard let bits = bits else { + return nil + } + self = bits + } + + internal init?(_utf8 utf8: C) where C.Element == UInt8 { + let c = utf8.count + self.init(repeating: false, count: c) + var i = c &- 1 + let success = _update { handle in + for byte in utf8 { + if byte == ._ascii1 { + handle.set(at: i) + } else { + guard byte == ._ascii0 else { return false } + } + i &-= 1 + } + return true + } + guard success else { return nil } + } +} diff --git a/Sources/BitCollections/BitArray/BitArray+Random.swift b/Sources/BitCollections/BitArray/BitArray+RandomBits.swift similarity index 77% rename from Sources/BitCollections/BitArray/BitArray+Random.swift rename to Sources/BitCollections/BitArray/BitArray+RandomBits.swift index 5c58cf781..9a13e78c1 100644 --- a/Sources/BitCollections/BitArray/BitArray+Random.swift +++ b/Sources/BitCollections/BitArray/BitArray+RandomBits.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2023 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 @@ -10,12 +10,13 @@ //===----------------------------------------------------------------------===// extension BitArray { - /// Create and return a new bit array consisting of `count` random bits. + /// Create and return a new bit array consisting of `count` random bits, + /// using the system random number generator. /// /// - Parameter count: The number of random bits to generate. - public static func random(count: Int) -> BitArray { + public static func randomBits(count: Int) -> BitArray { var rng = SystemRandomNumberGenerator() - return random(count: count, using: &rng) + return randomBits(count: count, using: &rng) } /// Create and return a new bit array consisting of `count` random bits, @@ -23,11 +24,11 @@ extension BitArray { /// /// - Parameter count: The number of random bits to generate. /// - Parameter rng: The random number generator to use. - public static func random( + public static func randomBits( count: Int, using rng: inout R ) -> BitArray { - precondition(count >= 0, "Invalid capacity value") + precondition(count >= 0, "Invalid count") guard count > 0 else { return BitArray() } let (w, b) = _BitPosition(count).endSplit var words = (0 ... w).map { _ in _Word(rng.next() as UInt) } diff --git a/Sources/BitCollections/BitArray/BitArray+RangeReplaceableCollection.swift b/Sources/BitCollections/BitArray/BitArray+RangeReplaceableCollection.swift index 5ab87d7d0..755210cfa 100644 --- a/Sources/BitCollections/BitArray/BitArray+RangeReplaceableCollection.swift +++ b/Sources/BitCollections/BitArray/BitArray+RangeReplaceableCollection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 @@ -29,7 +29,7 @@ extension BitArray { public init() { self.init(_storage: [], count: 0) } - + /// Creates a new bit array containing the specified number of a single, /// repeated Boolean value. /// diff --git a/Sources/BitCollections/BitArray/BitArray+Shifts.swift b/Sources/BitCollections/BitArray/BitArray+Shifts.swift new file mode 100644 index 000000000..7eb6601d4 --- /dev/null +++ b/Sources/BitCollections/BitArray/BitArray+Shifts.swift @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022-2023 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 +// +//===----------------------------------------------------------------------===// + +extension BitArray { + /// Shift the bits in this array by the specified number of places to the + /// left (towards the end of the array), by inserting `amount` false values + /// at the beginning. + /// + /// If `amount` is negative, this is equivalent to shifting `-amount` + /// places to the right. + /// + /// var bits: BitArray = "1110110" + /// bits.maskingShiftLeft(by: 2) + /// // bits is now 111011000 + /// bits.maskingShiftLeft(by: -4) + /// // bits is now 11101 + /// bits.maskingShiftLeft(by: 8) + /// // bits is now 111010000000 + public mutating func resizingShiftLeft(by amount: Int) { + guard amount != 0 else { return } + if amount > 0 { + _resizingShiftLeft(by: amount) + } else { + _resizingShiftRight(by: -amount) + } + } + + /// Shift the bits in this array by the specified number of places to the + /// right (towards the start of the array), by removing `amount` existing + /// values from the front of the array. + /// + /// If `amount` is negative, then this is equivalent to shifting `-amount` + /// places to the left. If amount is greater than or equal to `count`, + /// then the resulting bit array will be empty. + /// + /// var bits: BitArray = "1110110" + /// bits.maskingShiftRight(by: 2) + /// // bits is now 11101 + /// bits.maskingShiftRight(by: -4) + /// // bits is now 111010000 + /// bits.maskingShiftRight(by: 10) + /// // bits is now empty + /// + /// If `amount` is between 0 and `count`, then this has the same effect as + /// `removeFirst(amount)`. + public mutating func resizingShiftRight(by amount: Int) { + guard amount != 0 else { return } + if amount > 0 { + _resizingShiftRight(by: amount) + } else { + _resizingShiftLeft(by: -amount) + } + } + + internal mutating func _resizingShiftLeft(by amount: Int) { + assert(amount > 0) + _extend(by: amount, with: false) + maskingShiftLeft(by: amount) + } + + internal mutating func _resizingShiftRight(by amount: Int) { + assert(amount > 0) + guard amount < count else { + self = .init() + return + } + self._copy(from: Range(uncheckedBounds: (amount, count)), to: 0) + self._removeLast(count &- amount) + } +} + +extension BitArray { + // FIXME: Add maskingShiftRight(by:in:) and maskingShiftLeft(by:in:) for shifting slices. + + /// Shift the bits in this array by the specified number of places to the + /// left (towards the end of the array), without changing + /// its count. + /// + /// Values that are shifted off the array are discarded. Values that are + /// shifted in are all set to false. + /// + /// If `amount` is negative, this is equivalent to shifting `-amount` + /// places to the right. If `amount` is greater than or equal to `count`, + /// then all values are set to false. + /// + /// var bits: BitArray = "1110110" + /// bits.maskingShiftLeft(by: 2) + /// // bits is now 1011000 + /// bits.maskingShiftLeft(by: -4) + /// // bits is now 0000101 + /// bits.maskingShiftLeft(by: 8) + /// // bits is now 0000000 + public mutating func maskingShiftLeft(by amount: Int) { + guard amount != 0 else { return } + _update { + if amount > 0 { + $0._maskingShiftLeft(by: amount) + } else { + $0._maskingShiftRight(by: -amount) + } + } + } + + /// Shift the bits in this array by the specified number of places to the + /// right (towards the beginning of the array), without changing + /// its count. + /// + /// Values that are shifted off the array are discarded. Values that are + /// shifted in are all set to false. + /// + /// If `amount` is negative, this is equivalent to shifting `-amount` + /// places to the left. If `amount` is greater than or equal to `count`, + /// then all values are set to false. + /// + /// var bits: BitArray = "1110110" + /// bits.maskingShiftRight(by: 2) + /// // bits is now 0011101 + /// bits.maskingShiftRight(by: -3) + /// // bits is now 1101000 + /// bits.maskingShiftRight(by: 8) + /// // bits is now 0000000 + public mutating func maskingShiftRight(by amount: Int) { + guard amount != 0 else { return } + _update { + if amount > 0 { + $0._maskingShiftRight(by: amount) + } else { + $0._maskingShiftLeft(by: -amount) + } + } + } +} + +extension BitArray._UnsafeHandle { + internal mutating func _maskingShiftLeft(by amount: Int) { + assert(amount > 0) + let d = Swift.min(amount, self.count) + if d == amount { + let range = Range(uncheckedBounds: (0, self.count &- d)) + self.copy(from: range, to: d) + } + self.clear(in: Range(uncheckedBounds: (0, d))) + } + + internal mutating func _maskingShiftRight(by amount: Int) { + assert(amount > 0) + let d = Swift.min(amount, self.count) + if d == amount { + let range = Range(uncheckedBounds: (d, self.count)) + self.copy(from: range, to: 0) + } + self.clear(in: Range(uncheckedBounds: (self.count &- d, self.count))) + } +} diff --git a/Sources/BitCollections/BitArray/BitArray+Testing.swift b/Sources/BitCollections/BitArray/BitArray+Testing.swift index 3269ce30b..54afb107f 100644 --- a/Sources/BitCollections/BitArray/BitArray+Testing.swift +++ b/Sources/BitCollections/BitArray/BitArray+Testing.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-2023 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 diff --git a/Sources/BitCollections/BitArray/BitArray._UnsafeHandle.swift b/Sources/BitCollections/BitArray/BitArray._UnsafeHandle.swift index ba69f1e7c..976df76ca 100644 --- a/Sources/BitCollections/BitArray/BitArray._UnsafeHandle.swift +++ b/Sources/BitCollections/BitArray/BitArray._UnsafeHandle.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitArray { /// An unsafe-unowned bitarray view over `UInt` storage, providing bit array @@ -81,6 +83,24 @@ extension BitArray._UnsafeHandle { Int(_count) } + internal var end: _BitPosition { + _BitPosition(_count) + } + + internal func set(at position: Int) { + ensureMutable() + assert(position >= 0 && position < _count) + let (word, bit) = _BitPosition(UInt(position)).split + _mutableWords[word].insert(bit) + } + + internal func clear(at position: Int) { + ensureMutable() + assert(position >= 0 && position < _count) + let (word, bit) = _BitPosition(UInt(position)).split + _mutableWords[word].remove(bit) + } + internal subscript(position: Int) -> Bool { get { assert(position >= 0 && position < _count) diff --git a/Sources/BitCollections/BitArray/BitArray.swift b/Sources/BitCollections/BitArray/BitArray.swift index e40cc0e28..99e26141a 100644 --- a/Sources/BitCollections/BitArray/BitArray.swift +++ b/Sources/BitCollections/BitArray/BitArray.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// An ordered, random-access collection of `Bool` values, implemented as an /// uncompressed bitmap of as many bits as the count of the array. @@ -46,9 +48,7 @@ public struct BitArray { } } -#if swift(>=5.5) extension BitArray: Sendable {} -#endif extension BitArray { @inline(__always) diff --git a/Sources/BitCollections/BitSet/BitSet+BidirectionalCollection.swift b/Sources/BitCollections/BitSet/BitSet+BidirectionalCollection.swift index f910e28d9..fdd13a871 100644 --- a/Sources/BitCollections/BitSet/BitSet+BidirectionalCollection.swift +++ b/Sources/BitCollections/BitSet/BitSet+BidirectionalCollection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 @@ -88,9 +88,7 @@ extension BitSet: Sequence { } } -#if swift(>=5.5) extension BitSet.Iterator: Sendable {} -#endif extension BitSet: Collection, BidirectionalCollection { /// A Boolean value indicating whether the collection is empty. diff --git a/Sources/BitCollections/BitSet/BitSet+Extras.swift b/Sources/BitCollections/BitSet/BitSet+Extras.swift index 5a6ba9d79..181aaad34 100644 --- a/Sources/BitCollections/BitSet/BitSet+Extras.swift +++ b/Sources/BitCollections/BitSet/BitSet+Extras.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet: _UniqueCollection {} @@ -69,7 +71,7 @@ extension BitSet { } if newValue { _ensureCapacity(forValue: member) - } else if member > _capacity { + } else if member >= _capacity { return } _updateThenShrink { handle, shrink in diff --git a/Sources/BitCollections/BitSet/BitSet+Initializers.swift b/Sources/BitCollections/BitSet/BitSet+Initializers.swift index 4508d40ff..cf8b99871 100644 --- a/Sources/BitCollections/BitSet/BitSet+Initializers.swift +++ b/Sources/BitCollections/BitSet/BitSet+Initializers.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { /// Initializes a new, empty bit set. diff --git a/Sources/BitCollections/BitSet/BitSet+Invariants.swift b/Sources/BitCollections/BitSet/BitSet+Invariants.swift index c11ecf2b8..1d9811c6d 100644 --- a/Sources/BitCollections/BitSet/BitSet+Invariants.swift +++ b/Sources/BitCollections/BitSet/BitSet+Invariants.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { /// True if consistency checking is enabled in the implementation of this diff --git a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isEqualSet.swift b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isEqualSet.swift index 3c85b2c25..e0f4e2bc6 100644 --- a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isEqualSet.swift +++ b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isEqualSet.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // FIXME: These are non-standard extensions generalizing ==. extension BitSet { diff --git a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSubset.swift b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSubset.swift index 4fb251a9e..fdfa7db95 100644 --- a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSubset.swift +++ b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSubset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { /// Returns a Boolean value that indicates whether this bit set is a strict diff --git a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSuperset.swift b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSuperset.swift index 936dc8d99..c84f8f18f 100644 --- a/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSuperset.swift +++ b/Sources/BitCollections/BitSet/BitSet+SetAlgebra isStrictSuperset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { /// Returns a Boolean value that indicates whether this set is a strict diff --git a/Sources/BitCollections/BitSet/BitSet+Sorted Collection APIs.swift b/Sources/BitCollections/BitSet/BitSet+Sorted Collection APIs.swift index 191afcf79..4b3720fdf 100644 --- a/Sources/BitCollections/BitSet/BitSet+Sorted Collection APIs.swift +++ b/Sources/BitCollections/BitSet/BitSet+Sorted Collection APIs.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet: _SortedCollection { /// Returns the current set (already sorted). diff --git a/Sources/BitCollections/BitSet/BitSet.Counted.swift b/Sources/BitCollections/BitSet/BitSet.Counted.swift index 061e0e958..bcbbf476e 100644 --- a/Sources/BitCollections/BitSet/BitSet.Counted.swift +++ b/Sources/BitCollections/BitSet/BitSet.Counted.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { public struct Counted { @@ -26,9 +28,7 @@ extension BitSet { } } -#if swift(>=5.5) extension BitSet.Counted: Sendable {} -#endif extension BitSet.Counted { #if COLLECTIONS_INTERNAL_CHECKS diff --git a/Sources/BitCollections/BitSet/BitSet.Index.swift b/Sources/BitCollections/BitSet/BitSet.Index.swift index 5c06411cd..433dd37dc 100644 --- a/Sources/BitCollections/BitSet/BitSet.Index.swift +++ b/Sources/BitCollections/BitSet/BitSet.Index.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { /// An opaque type that represents a position in a bit set. @@ -41,9 +43,7 @@ extension BitSet { } } -#if swift(>=5.5) extension BitSet.Index: Sendable {} -#endif extension BitSet.Index: CustomStringConvertible { // A textual representation of this instance. diff --git a/Sources/BitCollections/BitSet/BitSet._UnsafeHandle.swift b/Sources/BitCollections/BitSet/BitSet._UnsafeHandle.swift index 545c8fe8b..06d48e0e5 100644 --- a/Sources/BitCollections/BitSet/BitSet._UnsafeHandle.swift +++ b/Sources/BitCollections/BitSet/BitSet._UnsafeHandle.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension BitSet { @usableFromInline diff --git a/Sources/BitCollections/BitSet/BitSet.swift b/Sources/BitCollections/BitSet/BitSet.swift index 9c16064d0..dfb097421 100644 --- a/Sources/BitCollections/BitSet/BitSet.swift +++ b/Sources/BitCollections/BitSet/BitSet.swift @@ -2,14 +2,16 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// A sorted collection of small nonnegative integers, implemented as an /// uncompressed bitmap of as many bits as the value of the largest member. @@ -31,9 +33,7 @@ public struct BitSet { } } -#if swift(>=5.5) extension BitSet: Sendable {} -#endif extension BitSet { @inline(__always) diff --git a/Sources/BitCollections/Shared/UInt+Tricks.swift b/Sources/BitCollections/Shared/UInt+Tricks.swift index 5ef5fc722..09a46b98a 100644 --- a/Sources/BitCollections/Shared/UInt+Tricks.swift +++ b/Sources/BitCollections/Shared/UInt+Tricks.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension UInt { /// Returns the position of the `n`th set bit in `self`. diff --git a/Sources/BitCollections/Shared/_Word.swift b/Sources/BitCollections/Shared/_Word.swift index 9b856c070..82d731efc 100644 --- a/Sources/BitCollections/Shared/_Word.swift +++ b/Sources/BitCollections/Shared/_Word.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif @usableFromInline internal typealias _Word = _UnsafeBitSet._Word diff --git a/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Elements.md b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Elements.md new file mode 100644 index 000000000..8e77d4269 --- /dev/null +++ b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Elements.md @@ -0,0 +1,40 @@ +# ``Collections/OrderedDictionary/Elements-swift.struct`` + + + + +## Topics + +### Inspecting an Elements View + +- ``isEmpty`` +- ``count`` + +### Accessing Elements + +- ``subscript(_:)-4xwc2`` +- ``keys`` +- ``values`` +- ``index(forKey:)`` + +### Removing Elements + +- ``remove(at:)`` +- ``removeSubrange(_:)-5x7oo`` +- ``removeSubrange(_:)-7wdak`` +- ``removeAll(keepingCapacity:)`` +- ``removeAll(where:)`` +- ``removeFirst()`` +- ``removeFirst(_:)`` +- ``removeLast()`` +- ``removeLast(_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)`` +- ``reverse()`` +- ``sort()`` +- ``sort(by:)`` +- ``partition(by:)`` +- ``shuffle()`` +- ``shuffle(using:)`` diff --git a/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Values.md b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Values.md new file mode 100644 index 000000000..3ef31e8b1 --- /dev/null +++ b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.Values.md @@ -0,0 +1,27 @@ +# ``Collections/OrderedDictionary/Values-swift.struct`` + + + + +## Topics + +### Inspecting a Values Collection + +- ``isEmpty`` +- ``count`` + +### Accessing Elements + +- ``subscript(_:)-25vfz`` +- ``elements`` +- ``withUnsafeBufferPointer(_:)`` +- ``withUnsafeMutableBufferPointer(_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)-77eiy`` +- ``partition(by:)-9x0i5`` +- ``sort()`` +- ``sort(by:)`` +- ``shuffle()`` +- ``shuffle(using:)`` diff --git a/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.md b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.md new file mode 100644 index 000000000..1db1199db --- /dev/null +++ b/Sources/Collections/Collections.docc/Extensions/OrderedDictionary.md @@ -0,0 +1,97 @@ +# ``Collections/OrderedDictionary`` + + + + +## Topics + +### Creating a Dictionary + +- ``init()`` +- ``init(minimumCapacity:persistent:)`` +- ``init(uniqueKeysWithValues:)-5ux9r`` +- ``init(uniqueKeysWithValues:)-88mzi`` +- ``init(uncheckedUniqueKeysWithValues:)-6gxhj`` +- ``init(uncheckedUniqueKeysWithValues:)-2j0dw`` +- ``init(uncheckedUniqueKeysWithValues:)-6gxhj`` +- ``init(uniqueKeys:values:)`` +- ``init(uncheckedUniqueKeys:values:)`` +- ``init(_:uniquingKeysWith:)-2y39b`` +- ``init(_:uniquingKeysWith:)-zhfp`` +- ``init(grouping:by:)-6mahw`` +- ``init(grouping:by:)-6m2zw`` + +### Inspecting a Dictionary + +- ``isEmpty`` +- ``count`` + +### Accessing Keys and Values + +- ``subscript(_:)`` +- ``subscript(_:default:)`` +- ``index(forKey:)`` + +### Collection Views + +- ``keys`` +- ``values-swift.property`` +- ``elements-swift.property`` + +### Updating Values + +- ``updateValue(_:forKey:)`` +- ``updateValue(_:forKey:insertingAt:)`` +- ``updateValue(forKey:default:with:)`` +- ``updateValue(forKey:insertingDefault:at:with:)`` + +### Removing Keys and Values + +- ``removeValue(forKey:)`` +- ``remove(at:)`` +- ``filter(_:)`` +- ``removeAll(where:)`` +- ``removeAll(keepingCapacity:)`` +- ``removeFirst()`` +- ``removeLast()`` +- ``removeFirst(_:)`` +- ``removeLast(_:)`` +- ``removeSubrange(_:)-512n3`` +- ``removeSubrange(_:)-8rmzx`` + +### Combining Dictionaries + +- ``merge(_:uniquingKeysWith:)-6ka2i`` +- ``merge(_:uniquingKeysWith:)-9wkad`` +- ``merging(_:uniquingKeysWith:)-4z49c`` +- ``merging(_:uniquingKeysWith:)-2e0xa`` + +### Comparing Dictionaries + +- ``==(_:_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)`` +- ``reverse()`` +- ``sort()`` +- ``sort(by:)`` +- ``reverse()`` +- ``shuffle()`` +- ``shuffle(using:)`` +- ``partition(by:)`` + +### Transforming a Dictionary + +- ``mapValues(_:)`` +- ``compactMapValues(_:)`` + +### Memory Management + +- ``reserveCapacity(_:)`` + +### Supporting Types + +- ``Index`` +- ``Values-swift.struct`` +- ``Elements-swift.struct`` diff --git a/Sources/Collections/Collections.swift b/Sources/Collections/Collections.swift index 99ffc85c8..8e6a9c29b 100644 --- a/Sources/Collections/Collections.swift +++ b/Sources/Collections/Collections.swift @@ -2,15 +2,18 @@ // // 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-2023 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 !COLLECTIONS_SINGLE_MODULE @_exported import BitCollections @_exported import DequeModule +@_exported import HashTreeCollections @_exported import HeapModule @_exported import OrderedCollections -@_exported import HashTreeCollections +//@_exported import _RopeModule +#endif diff --git a/Sources/DequeModule/Deque+Collection.swift b/Sources/DequeModule/Deque+Collection.swift index bb4c29f75..6dcdc28ec 100644 --- a/Sources/DequeModule/Deque+Collection.swift +++ b/Sources/DequeModule/Deque+Collection.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension Deque: Sequence { // Implementation note: we could also use the default `IndexingIterator` here. @@ -162,9 +164,7 @@ extension Deque: Sequence { } } -#if swift(>=5.5) extension Deque.Iterator: Sendable where Element: Sendable {} -#endif extension Deque: RandomAccessCollection { public typealias Index = Int diff --git a/Sources/DequeModule/Deque+Descriptions.swift b/Sources/DequeModule/Deque+Descriptions.swift index 532d5912c..9e27a2f6d 100644 --- a/Sources/DequeModule/Deque+Descriptions.swift +++ b/Sources/DequeModule/Deque+Descriptions.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension Deque: CustomStringConvertible { /// A textual representation of this instance. diff --git a/Sources/DequeModule/Deque+Extras.swift b/Sources/DequeModule/Deque+Extras.swift index 93d64cc44..8467f8a3f 100644 --- a/Sources/DequeModule/Deque+Extras.swift +++ b/Sources/DequeModule/Deque+Extras.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension Deque { /// Creates a deque with the specified capacity, then calls the given diff --git a/Sources/DequeModule/Deque+Sendable.swift b/Sources/DequeModule/Deque+Sendable.swift index 0ebf4bb06..df3fdd543 100644 --- a/Sources/DequeModule/Deque+Sendable.swift +++ b/Sources/DequeModule/Deque+Sendable.swift @@ -2,13 +2,11 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 swift(>=5.5) extension Deque: @unchecked Sendable where Element: Sendable {} -#endif diff --git a/Sources/DequeModule/Deque+Testing.swift b/Sources/DequeModule/Deque+Testing.swift index bbc1fbf92..7575472c2 100644 --- a/Sources/DequeModule/Deque+Testing.swift +++ b/Sources/DequeModule/Deque+Testing.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // This file contains exported but non-public entry points to support clear box // testing. diff --git a/Sources/DequeModule/_UnsafeWrappedBuffer.swift b/Sources/DequeModule/_UnsafeWrappedBuffer.swift index cf86b2931..a798642be 100644 --- a/Sources/DequeModule/_UnsafeWrappedBuffer.swift +++ b/Sources/DequeModule/_UnsafeWrappedBuffer.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif @frozen @usableFromInline diff --git a/Sources/HashTreeCollections/CMakeLists.txt b/Sources/HashTreeCollections/CMakeLists.txt index c5690fbfd..96bc2ac0a 100644 --- a/Sources/HashTreeCollections/CMakeLists.txt +++ b/Sources/HashTreeCollections/CMakeLists.txt @@ -8,45 +8,45 @@ See https://swift.org/LICENSE.txt for license information #]] add_library(HashTreeCollections - "Node/_AncestorSlots.swift" - "Node/_Bitmap.swift" - "Node/_Bucket.swift" - "Node/_Hash.swift" - "Node/_HashTreeIterator.swift" - "Node/_HashTreeStatistics.swift" - "Node/_Level.swift" - "Node/_Node+Builder.swift" - "Node/_Node+Debugging.swift" - "Node/_Node+Initializers.swift" - "Node/_Node+Invariants.swift" - "Node/_Node+Lookups.swift" - "Node/_Node+Primitive Insertions.swift" - "Node/_Node+Primitive Removals.swift" - "Node/_Node+Primitive Replacement.swift" - "Node/_Node+Storage.swift" - "Node/_Node+Structural compactMapValues.swift" - "Node/_Node+Structural filter.swift" - "Node/_Node+Structural intersection.swift" - "Node/_Node+Structural isDisjoint.swift" - "Node/_Node+Structural isEqualSet.swift" - "Node/_Node+Structural isSubset.swift" - "Node/_Node+Structural mapValues.swift" - "Node/_Node+Structural merge.swift" - "Node/_Node+Structural subtracting.swift" - "Node/_Node+Structural symmetricDifference.swift" - "Node/_Node+Structural union.swift" - "Node/_Node+Subtree Insertions.swift" - "Node/_Node+Subtree Modify.swift" - "Node/_Node+Subtree Removals.swift" - "Node/_Node+UnsafeHandle.swift" - "Node/_Node.swift" - "Node/_RawNode+UnsafeHandle.swift" - "Node/_RawNode.swift" - "Node/_Slot.swift" - "Node/_Stack.swift" - "Node/_StorageHeader.swift" - "Node/_UnmanagedNode.swift" - "Node/_UnsafePath.swift" + "HashNode/_AncestorHashSlots.swift" + "HashNode/_Bitmap.swift" + "HashNode/_Bucket.swift" + "HashNode/_Hash.swift" + "HashNode/_HashTreeIterator.swift" + "HashNode/_HashTreeStatistics.swift" + "HashNode/_HashLevel.swift" + "HashNode/_HashNode+Builder.swift" + "HashNode/_HashNode+Debugging.swift" + "HashNode/_HashNode+Initializers.swift" + "HashNode/_HashNode+Invariants.swift" + "HashNode/_HashNode+Lookups.swift" + "HashNode/_HashNode+Primitive Insertions.swift" + "HashNode/_HashNode+Primitive Removals.swift" + "HashNode/_HashNode+Primitive Replacement.swift" + "HashNode/_HashNode+Storage.swift" + "HashNode/_HashNode+Structural compactMapValues.swift" + "HashNode/_HashNode+Structural filter.swift" + "HashNode/_HashNode+Structural intersection.swift" + "HashNode/_HashNode+Structural isDisjoint.swift" + "HashNode/_HashNode+Structural isEqualSet.swift" + "HashNode/_HashNode+Structural isSubset.swift" + "HashNode/_HashNode+Structural mapValues.swift" + "HashNode/_HashNode+Structural merge.swift" + "HashNode/_HashNode+Structural subtracting.swift" + "HashNode/_HashNode+Structural symmetricDifference.swift" + "HashNode/_HashNode+Structural union.swift" + "HashNode/_HashNode+Subtree Insertions.swift" + "HashNode/_HashNode+Subtree Modify.swift" + "HashNode/_HashNode+Subtree Removals.swift" + "HashNode/_HashNode+UnsafeHandle.swift" + "HashNode/_HashNode.swift" + "HashNode/_RawHashNode+UnsafeHandle.swift" + "HashNode/_RawHashNode.swift" + "HashNode/_HashSlot.swift" + "HashNode/_HashStack.swift" + "HashNode/_HashNodeHeader.swift" + "HashNode/_UnmanagedHashNode.swift" + "HashNode/_UnsafePath.swift" "TreeDictionary/TreeDictionary+Codable.swift" "TreeDictionary/TreeDictionary+Collection.swift" "TreeDictionary/TreeDictionary+CustomReflectable.swift" diff --git a/Sources/HashTreeCollections/Node/_AncestorSlots.swift b/Sources/HashTreeCollections/HashNode/_AncestorHashSlots.swift similarity index 79% rename from Sources/HashTreeCollections/Node/_AncestorSlots.swift rename to Sources/HashTreeCollections/HashNode/_AncestorHashSlots.swift index 54d12375f..c9e7872c1 100644 --- a/Sources/HashTreeCollections/Node/_AncestorSlots.swift +++ b/Sources/HashTreeCollections/HashNode/_AncestorHashSlots.swift @@ -12,14 +12,14 @@ /// A collection of slot values logically addressing a particular node in a /// hash tree. The collection is (logically) extended with zero slots up to /// the maximum depth of the tree -- to unambiguously address a single node, -/// this therefore needs to be augmented with a `_Level` value. +/// this therefore needs to be augmented with a `_HashLevel` value. /// /// This construct can only be used to identify a particular node in the tree; /// it does not necessarily have room to include an item offset in the addressed /// node. (See `_Path` if you need to address a particular item.) @usableFromInline @frozen -struct _AncestorSlots { +struct _AncestorHashSlots { @usableFromInline internal var path: UInt @@ -29,27 +29,27 @@ struct _AncestorSlots { } } -extension _AncestorSlots: Equatable { +extension _AncestorHashSlots: Equatable { @inlinable @inline(__always) internal static func ==(left: Self, right: Self) -> Bool { left.path == right.path } } -extension _AncestorSlots { +extension _AncestorHashSlots { @inlinable @inline(__always) internal static var empty: Self { Self(0) } } -extension _AncestorSlots { +extension _AncestorHashSlots { /// Return or set the slot value at the specified level. /// If this is used to mutate the collection, then the original value /// on the given level must be zero. @inlinable @inline(__always) - internal subscript(_ level: _Level) -> _Slot { + internal subscript(_ level: _HashLevel) -> _HashSlot { get { assert(level.shift < UInt.bitWidth) - return _Slot((path &>> level.shift) & _Bucket.bitMask) + return _HashSlot((path &>> level.shift) & _Bucket.bitMask) } set { assert(newValue._value < _Bitmap.capacity) @@ -59,7 +59,7 @@ extension _AncestorSlots { } @inlinable @inline(__always) - internal func appending(_ slot: _Slot, at level: _Level) -> Self { + internal func appending(_ slot: _HashSlot, at level: _HashLevel) -> Self { var result = self result[level] = slot return result @@ -67,14 +67,14 @@ extension _AncestorSlots { /// Clear the slot at the specified level, by setting it to zero. @inlinable - internal mutating func clear(_ level: _Level) { + internal mutating func clear(_ level: _HashLevel) { guard level.shift < UInt.bitWidth else { return } path &= ~(_Bucket.bitMask &<< level.shift) } /// Clear all slots at or below the specified level, by setting them to zero. @inlinable - internal mutating func clear(atOrBelow level: _Level) { + internal mutating func clear(atOrBelow level: _HashLevel) { guard level.shift < UInt.bitWidth else { return } path &= ~(UInt.max &<< level.shift) } @@ -82,16 +82,16 @@ extension _AncestorSlots { /// Truncate this path to the specified level. /// Slots at or beyond the specified level are cleared. @inlinable - internal func truncating(to level: _Level) -> _AncestorSlots { + internal func truncating(to level: _HashLevel) -> _AncestorHashSlots { assert(level.shift <= UInt.bitWidth) guard level.shift < UInt.bitWidth else { return self } - return _AncestorSlots(path & ((1 &<< level.shift) &- 1)) + return _AncestorHashSlots(path & ((1 &<< level.shift) &- 1)) } /// Returns true if this path contains non-zero slots at or beyond the /// specified level, otherwise returns false. @inlinable - internal func hasDataBelow(_ level: _Level) -> Bool { + internal func hasDataBelow(_ level: _HashLevel) -> Bool { guard level.shift < UInt.bitWidth else { return false } return (path &>> level.shift) != 0 } @@ -99,7 +99,7 @@ extension _AncestorSlots { /// Compares this path to `other` up to but not including the specified level. /// Returns true if the path prefixes compare equal, otherwise returns false. @inlinable - internal func isEqual(to other: Self, upTo level: _Level) -> Bool { + internal func isEqual(to other: Self, upTo level: _HashLevel) -> Bool { if level.isAtRoot { return true } if level.isAtBottom { return self == other } let s = UInt(UInt.bitWidth) - level.shift diff --git a/Sources/HashTreeCollections/Node/_Bitmap.swift b/Sources/HashTreeCollections/HashNode/_Bitmap.swift similarity index 94% rename from Sources/HashTreeCollections/Node/_Bitmap.swift rename to Sources/HashTreeCollections/HashNode/_Bitmap.swift index cb06f271d..9b7891785 100644 --- a/Sources/HashTreeCollections/Node/_Bitmap.swift +++ b/Sources/HashTreeCollections/HashNode/_Bitmap.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// A set of `_Bucket` values, represented by a 32-bit wide bitset. @usableFromInline @@ -138,12 +140,12 @@ extension _Bitmap { } @inlinable @inline(__always) - internal func slot(of bucket: _Bucket) -> _Slot { - _Slot(_value._rank(ofBit: bucket.value)) + internal func slot(of bucket: _Bucket) -> _HashSlot { + _HashSlot(_value._rank(ofBit: bucket.value)) } @inlinable @inline(__always) - internal func bucket(at slot: _Slot) -> _Bucket { + internal func bucket(at slot: _HashSlot) -> _Bucket { _Bucket(_value._bit(ranked: slot.value)!) } } @@ -182,7 +184,7 @@ extension _Bitmap { extension _Bitmap: Sequence { @usableFromInline - internal typealias Element = (bucket: _Bucket, slot: _Slot) + internal typealias Element = (bucket: _Bucket, slot: _HashSlot) @usableFromInline @frozen @@ -191,7 +193,7 @@ extension _Bitmap: Sequence { internal var bitmap: _Bitmap @usableFromInline - internal var slot: _Slot + internal var slot: _HashSlot @inlinable internal init(_ bitmap: _Bitmap) { diff --git a/Sources/HashTreeCollections/Node/_Bucket.swift b/Sources/HashTreeCollections/HashNode/_Bucket.swift similarity index 100% rename from Sources/HashTreeCollections/Node/_Bucket.swift rename to Sources/HashTreeCollections/HashNode/_Bucket.swift diff --git a/Sources/HashTreeCollections/Node/_Hash.swift b/Sources/HashTreeCollections/HashNode/_Hash.swift similarity index 90% rename from Sources/HashTreeCollections/Node/_Hash.swift rename to Sources/HashTreeCollections/HashNode/_Hash.swift index ad26ab1b8..975ba5616 100644 --- a/Sources/HashTreeCollections/Node/_Hash.swift +++ b/Sources/HashTreeCollections/HashNode/_Hash.swift @@ -41,7 +41,7 @@ extension _Hash: CustomStringConvertible { // Print hash values in radix 32 & reversed, so that the path in the hash // tree is readily visible. let p = String(value, radix: _Bitmap.capacity, uppercase: true) - let c = _Level.limit + let c = _HashLevel.limit let path = String(repeating: "0", count: Swift.max(0, c - p.count)) + p return String(path.reversed()) } @@ -55,7 +55,7 @@ extension _Hash { extension _Hash { @inlinable - internal subscript(_ level: _Level) -> _Bucket { + internal subscript(_ level: _HashLevel) -> _Bucket { get { assert(!level.isAtBottom) return _Bucket((value &>> level.shift) & _Bucket.bitMask) @@ -75,7 +75,7 @@ extension _Hash { } @inlinable - internal func appending(_ bucket: _Bucket, at level: _Level) -> Self { + internal func appending(_ bucket: _Bucket, at level: _HashLevel) -> Self { assert(value >> level.shift == 0) var copy = self copy[level] = bucket @@ -83,7 +83,7 @@ extension _Hash { } @inlinable - internal func isEqual(to other: _Hash, upTo level: _Level) -> Bool { + internal func isEqual(to other: _Hash, upTo level: _HashLevel) -> Bool { if level.isAtRoot { return true } if level.isAtBottom { return self == other } let s = UInt(UInt.bitWidth) - level.shift diff --git a/Sources/HashTreeCollections/Node/_Level.swift b/Sources/HashTreeCollections/HashNode/_HashLevel.swift similarity index 85% rename from Sources/HashTreeCollections/Node/_Level.swift rename to Sources/HashTreeCollections/HashNode/_HashLevel.swift index afda20c22..4519a54db 100644 --- a/Sources/HashTreeCollections/Node/_Level.swift +++ b/Sources/HashTreeCollections/HashNode/_HashLevel.swift @@ -15,7 +15,7 @@ /// the level always fits in an `UInt8` value. @usableFromInline @frozen -internal struct _Level { +internal struct _HashLevel { /// The bit position within a hash value that begins the hash slice that is /// associated with this level. For collision nodes, this can be larger than /// `UInt.bitWidth`. @@ -35,12 +35,12 @@ internal struct _Level { @inlinable @inline(__always) init(depth: Int) { - assert(depth > 0 && depth < _Level.limit) + assert(depth > 0 && depth < _HashLevel.limit) self.init(shift: UInt(bitPattern: depth * _Bitmap.bitWidth)) } } -extension _Level { +extension _HashLevel { @inlinable @inline(__always) internal static var limit: Int { (_Hash.bitWidth + _Bitmap.bitWidth - 1) / _Bitmap.bitWidth @@ -52,8 +52,8 @@ extension _Level { } @inlinable @inline(__always) - internal static var top: _Level { - _Level(shift: 0) + internal static var top: _HashLevel { + _HashLevel(shift: 0) } /// The bit position within a hash value that begins the hash slice that is @@ -74,26 +74,26 @@ extension _Level { } @inlinable @inline(__always) - internal func descend() -> _Level { + internal func descend() -> _HashLevel { // FIXME: Consider returning nil when we run out of bits - _Level(_shift: _shift &+ Self._step) + _HashLevel(_shift: _shift &+ Self._step) } @inlinable @inline(__always) - internal func ascend() -> _Level { + internal func ascend() -> _HashLevel { assert(!isAtRoot) - return _Level(_shift: _shift &- Self._step) + return _HashLevel(_shift: _shift &- Self._step) } } -extension _Level: Equatable { +extension _HashLevel: Equatable { @inlinable @inline(__always) internal static func ==(left: Self, right: Self) -> Bool { left._shift == right._shift } } -extension _Level: Comparable { +extension _HashLevel: Comparable { @inlinable @inline(__always) internal static func <(left: Self, right: Self) -> Bool { left._shift < right._shift diff --git a/Sources/HashTreeCollections/Node/_Node+Builder.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Builder.swift similarity index 78% rename from Sources/HashTreeCollections/Node/_Node+Builder.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Builder.swift index 56045ee56..13a0a3343 100644 --- a/Sources/HashTreeCollections/Node/_Node+Builder.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Builder.swift @@ -9,36 +9,36 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @usableFromInline @frozen internal struct Builder { - @usableFromInline typealias Element = _Node.Element + @usableFromInline internal typealias Element = _HashNode.Element @usableFromInline @frozen internal enum Kind { case empty case item(Element, at: _Bucket) - case node(_Node) - case collisionNode(_Node) + case node(_HashNode) + case collisionNode(_HashNode) } @usableFromInline - internal var level: _Level + internal var level: _HashLevel @usableFromInline internal var kind: Kind @inlinable - internal init(_ level: _Level, _ kind: Kind) { + internal init(_ level: _HashLevel, _ kind: Kind) { self.level = level self.kind = kind } } } -extension _Node.Builder { +extension _HashNode.Builder { @usableFromInline internal func dump() { let head = "Builder(level: \(level.depth), kind: " @@ -46,7 +46,7 @@ extension _Node.Builder { case .empty: print(head + "empty)") case .item(let item, at: let bucket): - print(head + "item(\(_Node._itemString(for: item)), at: \(bucket))") + print(head + "item(\(_HashNode._itemString(for: item)), at: \(bucket))") case .node(let node): print(head + "node)") node.dump() @@ -57,22 +57,22 @@ extension _Node.Builder { } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable @inline(__always) - internal static func empty(_ level: _Level) -> Self { + internal static func empty(_ level: _HashLevel) -> Self { Self(level, .empty) } @inlinable @inline(__always) internal static func item( - _ level: _Level, _ item: __owned Element, at bucket: _Bucket + _ level: _HashLevel, _ item: __owned Element, at bucket: _Bucket ) -> Self { Self(level, .item(item, at: bucket)) } @inlinable @inline(__always) internal static func node( - _ level: _Level, _ node: __owned _Node + _ level: _HashLevel, _ node: __owned _HashNode ) -> Self { assert(!node.isCollisionNode) return Self(level, .node(node)) @@ -80,14 +80,14 @@ extension _Node.Builder { @inlinable @inline(__always) internal static func collisionNode( - _ level: _Level, _ node: __owned _Node + _ level: _HashLevel, _ node: __owned _HashNode ) -> Self { assert(node.isCollisionNode) return Self(level, .collisionNode(node)) } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable internal var count: Int { switch kind { @@ -109,9 +109,9 @@ extension _Node.Builder { } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable - internal init(_ level: _Level, _ node: _Node) { + internal init(_ level: _HashLevel, _ node: _HashNode) { self.level = level if node.count == 0 { kind = .empty @@ -126,7 +126,7 @@ extension _Node.Builder { } @inlinable - internal __consuming func finalize(_ level: _Level) -> _Node { + internal __consuming func finalize(_ level: _HashLevel) -> _HashNode { assert(level.isAtRoot && self.level.isAtRoot) switch kind { case .empty: @@ -141,10 +141,10 @@ extension _Node.Builder { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func applyReplacement( - _ level: _Level, + _ level: _HashLevel, _ replacement: Builder ) -> Element? { assert(level == replacement.level) @@ -164,10 +164,10 @@ extension _Node { } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable internal mutating func addNewCollision( - _ level: _Level, _ newItem: __owned Element, _ hash: _Hash + _ level: _HashLevel, _ newItem: __owned Element, _ hash: _Hash ) { assert(level == self.level) switch kind { @@ -175,7 +175,7 @@ extension _Node.Builder { kind = .item(newItem, at: hash[level]) case .item(let oldItem, at: let bucket): assert(hash[level] == bucket) - let node = _Node._collisionNode(hash, oldItem, newItem) + let node = _HashNode._collisionNode(hash, oldItem, newItem) kind = .collisionNode(node) case .collisionNode(var node): kind = .empty @@ -190,7 +190,7 @@ extension _Node.Builder { @inlinable internal mutating func addNewItem( - _ level: _Level, _ newItem: __owned Element, at newBucket: _Bucket + _ level: _HashLevel, _ newItem: __owned Element, at newBucket: _Bucket ) { assert(level == self.level) switch kind { @@ -198,7 +198,7 @@ extension _Node.Builder { kind = .item(newItem, at: newBucket) case .item(let oldItem, let oldBucket): assert(oldBucket != newBucket) - let node = _Node._regularNode(oldItem, oldBucket, newItem, newBucket) + let node = _HashNode._regularNode(oldItem, oldBucket, newItem, newBucket) kind = .node(node) case .node(var node): kind = .empty @@ -209,7 +209,7 @@ extension _Node.Builder { // Expansion assert(!level.isAtBottom) self.kind = .empty - node = _Node._regularNode( + node = _HashNode._regularNode( newItem, newBucket, node, node.collisionHash[level]) kind = .node(node) } @@ -217,7 +217,7 @@ extension _Node.Builder { @inlinable internal mutating func addNewChildNode( - _ level: _Level, _ newChild: __owned _Node, at newBucket: _Bucket + _ level: _HashLevel, _ newChild: __owned _HashNode, at newBucket: _Bucket ) { assert(level == self.level) switch self.kind { @@ -230,20 +230,20 @@ extension _Node.Builder { self.kind = .node(._regularNode(newChild, newBucket)) } case let .item(oldItem, oldBucket): - let node = _Node._regularNode(oldItem, oldBucket, newChild, newBucket) + let node = _HashNode._regularNode(oldItem, oldBucket, newChild, newBucket) self.kind = .node(node) case .node(var node): self.kind = .empty let isUnique = node.isUnique() node.ensureUnique( - isUnique: isUnique, withFreeSpace: _Node.spaceForNewChild) + isUnique: isUnique, withFreeSpace: _HashNode.spaceForNewChild) node.insertChild(newChild, newBucket) self.kind = .node(node) case .collisionNode(var node): // Expansion self.kind = .empty assert(!level.isAtBottom) - node = _Node._regularNode( + node = _HashNode._regularNode( node, node.collisionHash[level], newChild, newBucket) self.kind = .node(node) } @@ -251,7 +251,7 @@ extension _Node.Builder { @inlinable internal mutating func addNewChildBranch( - _ level: _Level, _ newChild: __owned Self, at newBucket: _Bucket + _ level: _HashLevel, _ newChild: __owned Self, at newBucket: _Bucket ) { assert(level == self.level) assert(newChild.level == self.level.descend()) @@ -267,7 +267,7 @@ extension _Node.Builder { @inlinable internal static func childBranch( - _ level: _Level, _ child: Self, at bucket: _Bucket + _ level: _HashLevel, _ child: Self, at bucket: _Bucket ) -> Self { assert(child.level == level.descend()) switch child.kind { @@ -285,25 +285,25 @@ extension _Node.Builder { } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable internal mutating func copyCollisions( - from source: _Node.UnsafeHandle, - upTo end: _Slot + from source: _HashNode.UnsafeHandle, + upTo end: _HashSlot ) { assert(isEmpty) assert(source.isCollisionNode) assert(end < source.itemsEndSlot) let h = source.collisionHash - for slot: _Slot in stride(from: .zero, to: end, by: 1) { + for slot: _HashSlot in stride(from: .zero, to: end, by: 1) { self.addNewCollision(self.level, source[item: slot], h) } } @inlinable internal mutating func copyItems( - _ level: _Level, - from source: _Node.UnsafeHandle, + _ level: _HashLevel, + from source: _HashNode.UnsafeHandle, upTo end: _Bucket ) { assert(level == self.level) @@ -316,8 +316,8 @@ extension _Node.Builder { @inlinable internal mutating func copyItemsAndChildren( - _ level: _Level, - from source: _Node.UnsafeHandle, + _ level: _HashLevel, + from source: _HashNode.UnsafeHandle, upTo end: _Bucket ) { assert(level == self.level) @@ -332,11 +332,11 @@ extension _Node.Builder { } } -extension _Node.Builder { +extension _HashNode.Builder { @inlinable internal func mapValues( _ transform: (Element) -> Value2 - ) -> _Node.Builder { + ) -> _HashNode.Builder { switch kind { case .empty: return .empty(level) @@ -351,9 +351,9 @@ extension _Node.Builder { } @inlinable - internal func mapValuesToVoid() -> _Node.Builder { + internal func mapValuesToVoid() -> _HashNode.Builder { if Value.self == Void.self { - return unsafeBitCast(self, to: _Node.Builder.self) + return unsafeBitCast(self, to: _HashNode.Builder.self) } return mapValues { _ in () } } diff --git a/Sources/HashTreeCollections/Node/_Node+Debugging.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Debugging.swift similarity index 90% rename from Sources/HashTreeCollections/Node/_Node+Debugging.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Debugging.swift index 094a24766..3bbf6efc1 100644 --- a/Sources/HashTreeCollections/Node/_Node+Debugging.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Debugging.swift @@ -9,9 +9,11 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif -extension _Node { +extension _HashNode { @usableFromInline internal func dump( iterationOrder: Bool = false, @@ -32,23 +34,23 @@ extension _Node { } } -extension _Node.Storage { +extension _HashNode.Storage { @usableFromInline final internal func dump(iterationOrder: Bool = false) { UnsafeHandle.read(self) { $0.dump(iterationOrder: iterationOrder) } } } -extension _Node { +extension _HashNode { internal static func _itemString(for item: Element) -> String { let hash = _Hash(item.key).description return "hash: \(hash), key: \(item.key), value: \(item.value)" } } -extension _Node.UnsafeHandle { - internal func _itemString(at slot: _Slot) -> String { +extension _HashNode.UnsafeHandle { + internal func _itemString(at slot: _HashSlot) -> String { let item = self[item: slot] - return _Node._itemString(for: item) + return _HashNode._itemString(for: item) } @usableFromInline @@ -96,8 +98,8 @@ extension _Node.UnsafeHandle { print("\(restPrefix)[\(slot)] \(_itemString(at: slot))") } } else { - var itemSlot: _Slot = .zero - var childSlot: _Slot = .zero + var itemSlot: _HashSlot = .zero + var childSlot: _HashSlot = .zero for b in 0 ..< UInt(_Bitmap.capacity) { let bucket = _Bucket(b) let bucketStr = "#\(String(b, radix: _Bitmap.capacity, uppercase: true))" diff --git a/Sources/HashTreeCollections/Node/_Node+Initializers.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Initializers.swift similarity index 80% rename from Sources/HashTreeCollections/Node/_Node+Initializers.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Initializers.swift index c6a17c3bf..6dc9663e6 100644 --- a/Sources/HashTreeCollections/Node/_Node+Initializers.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Initializers.swift @@ -9,12 +9,14 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif -extension _Node { +extension _HashNode { @inlinable @inline(__always) - internal static func _emptyNode() -> _Node { - _Node(storage: _emptySingleton, count: 0) + internal static func _emptyNode() -> _HashNode { + _HashNode(storage: _emptySingleton, count: 0) } @inlinable @@ -22,8 +24,8 @@ extension _Node { _ hash: _Hash, _ item1: __owned Element, _ item2: __owned Element - ) -> _Node { - let node = _Node.allocateCollision(count: 2, hash) { items in + ) -> _HashNode { + let node = _HashNode.allocateCollision(count: 2, hash) { items in items.initializeElement(at: 1, to: item1) items.initializeElement(at: 0, to: item2) }.node @@ -36,8 +38,8 @@ extension _Node { _ hash: _Hash, _ item1: __owned Element, _ inserter2: (UnsafeMutablePointer) -> Void - ) -> _Node { - let node = _Node.allocateCollision(count: 2, hash) { items in + ) -> _HashNode { + let node = _HashNode.allocateCollision(count: 2, hash) { items in items.initializeElement(at: 1, to: item1) inserter2(items.baseAddress.unsafelyUnwrapped) }.node @@ -49,8 +51,8 @@ extension _Node { internal static func _regularNode( _ item: __owned Element, _ bucket: _Bucket - ) -> _Node { - let r = _Node.allocate( + ) -> _HashNode { + let r = _HashNode.allocate( itemMap: _Bitmap(bucket), childMap: .empty, count: 1 @@ -68,7 +70,7 @@ extension _Node { _ bucket1: _Bucket, _ item2: __owned Element, _ bucket2: _Bucket - ) -> _Node { + ) -> _HashNode { _regularNode( item1, bucket1, { $0.initialize(to: item2) }, bucket2).node @@ -80,19 +82,19 @@ extension _Node { _ bucket1: _Bucket, _ inserter2: (UnsafeMutablePointer) -> Void, _ bucket2: _Bucket - ) -> (node: _Node, slot1: _Slot, slot2: _Slot) { + ) -> (node: _HashNode, slot1: _HashSlot, slot2: _HashSlot) { assert(bucket1 != bucket2) - let r = _Node.allocate( + let r = _HashNode.allocate( itemMap: _Bitmap(bucket1, bucket2), childMap: .empty, count: 2 - ) { children, items -> (_Slot, _Slot) in + ) { children, items -> (_HashSlot, _HashSlot) in assert(items.count == 2 && children.count == 0) let i1 = bucket1 < bucket2 ? 1 : 0 let i2 = 1 &- i1 items.initializeElement(at: i1, to: item1) inserter2(items.baseAddress.unsafelyUnwrapped + i2) - return (_Slot(i2), _Slot(i1)) // Note: swapped + return (_HashSlot(i2), _HashSlot(i1)) // Note: swapped } r.node._invariantCheck() return (r.node, r.result.0, r.result.1) @@ -100,10 +102,10 @@ extension _Node { @inlinable internal static func _regularNode( - _ child: __owned _Node, + _ child: __owned _HashNode, _ bucket: _Bucket - ) -> _Node { - let r = _Node.allocate( + ) -> _HashNode { + let r = _HashNode.allocate( itemMap: .empty, childMap: _Bitmap(bucket), count: child.count @@ -119,9 +121,9 @@ extension _Node { internal static func _regularNode( _ item: __owned Element, _ itemBucket: _Bucket, - _ child: __owned _Node, + _ child: __owned _HashNode, _ childBucket: _Bucket - ) -> _Node { + ) -> _HashNode { _regularNode( { $0.initialize(to: item) }, itemBucket, child, childBucket) @@ -131,11 +133,11 @@ extension _Node { internal static func _regularNode( _ inserter: (UnsafeMutablePointer) -> Void, _ itemBucket: _Bucket, - _ child: __owned _Node, + _ child: __owned _HashNode, _ childBucket: _Bucket - ) -> _Node { + ) -> _HashNode { assert(itemBucket != childBucket) - let r = _Node.allocate( + let r = _HashNode.allocate( itemMap: _Bitmap(itemBucket), childMap: _Bitmap(childBucket), count: child.count &+ 1 @@ -150,13 +152,13 @@ extension _Node { @inlinable internal static func _regularNode( - _ child1: __owned _Node, + _ child1: __owned _HashNode, _ child1Bucket: _Bucket, - _ child2: __owned _Node, + _ child2: __owned _HashNode, _ child2Bucket: _Bucket - ) -> _Node { + ) -> _HashNode { assert(child1Bucket != child2Bucket) - let r = _Node.allocate( + let r = _HashNode.allocate( itemMap: .empty, childMap: _Bitmap(child1Bucket, child2Bucket), count: child1.count &+ child2.count @@ -170,19 +172,19 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal static func build( - level: _Level, + level: _HashLevel, item1: __owned Element, _ hash1: _Hash, item2 inserter2: (UnsafeMutablePointer) -> Void, _ hash2: _Hash - ) -> (top: _Node, leaf: _UnmanagedNode, slot1: _Slot, slot2: _Slot) { + ) -> (top: _HashNode, leaf: _UnmanagedHashNode, slot1: _HashSlot, slot2: _HashSlot) { assert(hash1.isEqual(to: hash2, upTo: level.ascend())) if hash1 == hash2 { let top = _collisionNode(hash1, item1, inserter2) - return (top, top.unmanaged, _Slot(0), _Slot(1)) + return (top, top.unmanaged, _HashSlot(0), _HashSlot(1)) } let r = _build( level: level, item1: item1, hash1, item2: inserter2, hash2) @@ -191,12 +193,12 @@ extension _Node { @inlinable internal static func _build( - level: _Level, + level: _HashLevel, item1: __owned Element, _ hash1: _Hash, item2 inserter2: (UnsafeMutablePointer) -> Void, _ hash2: _Hash - ) -> (top: _Node, leaf: _UnmanagedNode, slot1: _Slot, slot2: _Slot) { + ) -> (top: _HashNode, leaf: _UnmanagedHashNode, slot1: _HashSlot, slot2: _HashSlot) { assert(hash1 != hash2) let b1 = hash1[level] let b2 = hash2[level] @@ -213,12 +215,12 @@ extension _Node { @inlinable internal static func build( - level: _Level, + level: _HashLevel, item1 inserter1: (UnsafeMutablePointer) -> Void, _ hash1: _Hash, - child2: __owned _Node, + child2: __owned _HashNode, _ hash2: _Hash - ) -> (top: _Node, leaf: _UnmanagedNode, slot1: _Slot, slot2: _Slot) { + ) -> (top: _HashNode, leaf: _UnmanagedHashNode, slot1: _HashSlot, slot2: _HashSlot) { assert(child2.isCollisionNode) assert(hash1 != hash2) let b1 = hash1[level] @@ -236,12 +238,12 @@ extension _Node { @inlinable internal static func build( - level: _Level, - child1: __owned _Node, + level: _HashLevel, + child1: __owned _HashNode, _ hash1: _Hash, - child2: __owned _Node, + child2: __owned _HashNode, _ hash2: _Hash - ) -> _Node { + ) -> _HashNode { assert(child1.isCollisionNode) assert(child2.isCollisionNode) assert(hash1 != hash2) diff --git a/Sources/HashTreeCollections/Node/_Node+Invariants.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Invariants.swift similarity index 89% rename from Sources/HashTreeCollections/Node/_Node+Invariants.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Invariants.swift index 570df8cca..7b4884042 100644 --- a/Sources/HashTreeCollections/Node/_Node+Invariants.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Invariants.swift @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -extension _StorageHeader { +extension _HashNodeHeader { #if COLLECTIONS_INTERNAL_CHECKS @usableFromInline @inline(never) internal func _invariantCheck() { @@ -27,7 +27,7 @@ extension _StorageHeader { #endif } -extension _Node { +extension _HashNode { #if COLLECTIONS_INTERNAL_CHECKS @usableFromInline @inline(never) internal func _invariantCheck() { @@ -43,7 +43,7 @@ extension _Node { assert($0.collisionHash == _Hash($0[item: .zero].key)) } else { - let childBytes = $0.childCount * MemoryLayout<_Node>.stride + let childBytes = $0.childCount * MemoryLayout<_HashNode>.stride assert(itemBytes + $0.bytesFree + childBytes == $0.byteCapacity) } @@ -63,7 +63,7 @@ extension _Node { #if COLLECTIONS_INTERNAL_CHECKS @inlinable @inline(never) - internal func _fullInvariantCheck(_ level: _Level, _ path: _Hash) { + internal func _fullInvariantCheck(_ level: _HashLevel, _ path: _Hash) { _invariantCheck() read { precondition(level.isAtRoot || !hasSingletonItem) @@ -79,8 +79,8 @@ extension _Node { precondition(_Hash(item.key) == hash) } } - var itemSlot: _Slot = .zero - var childSlot: _Slot = .zero + var itemSlot: _HashSlot = .zero + var childSlot: _HashSlot = .zero for b in 0 ..< UInt(_Bitmap.capacity) { let bucket = _Bucket(b) let path = path.appending(bucket, at: level) @@ -101,7 +101,7 @@ extension _Node { } #else @inlinable @inline(__always) - internal func _fullInvariantCheck(_ level: _Level, _ path: _Hash) {} + internal func _fullInvariantCheck(_ level: _HashLevel, _ path: _Hash) {} #endif } diff --git a/Sources/HashTreeCollections/Node/_Node+Lookups.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Lookups.swift similarity index 86% rename from Sources/HashTreeCollections/Node/_Node+Lookups.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Lookups.swift index 7185509b4..88c3eca3b 100644 --- a/Sources/HashTreeCollections/Node/_Node+Lookups.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Lookups.swift @@ -11,20 +11,20 @@ // MARK: Node-level lookup operations -extension _Node { +extension _HashNode { @inlinable internal func find( - _ level: _Level, _ key: Key, _ hash: _Hash - ) -> (descend: Bool, slot: _Slot)? { + _ level: _HashLevel, _ key: Key, _ hash: _Hash + ) -> (descend: Bool, slot: _HashSlot)? { read { $0.find(level, key, hash) } } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable internal func find( - _ level: _Level, _ key: Key, _ hash: _Hash - ) -> (descend: Bool, slot: _Slot)? { + _ level: _HashLevel, _ key: Key, _ hash: _Hash + ) -> (descend: Bool, slot: _HashSlot)? { guard !isCollisionNode else { let r = _findInCollision(level, key, hash) guard r.code == 0 else { return nil } @@ -45,8 +45,8 @@ extension _Node.UnsafeHandle { @inlinable @inline(never) internal func _findInCollision( - _ level: _Level, _ key: Key, _ hash: _Hash - ) -> (code: Int, slot: _Slot) { + _ level: _HashLevel, _ key: Key, _ hash: _Hash + ) -> (code: Int, slot: _HashSlot) { assert(isCollisionNode) if !level.isAtBottom { if hash != self.collisionHash { return (2, .zero) } @@ -54,7 +54,7 @@ extension _Node.UnsafeHandle { // Note: this searches the items in reverse insertion order. guard let slot = reverseItems.firstIndex(where: { $0.key == key }) else { return (1, self.itemsEndSlot) } - return (0, _Slot(itemCount &- 1 &- slot)) + return (0, _HashSlot(itemCount &- 1 &- slot)) } } @@ -75,7 +75,7 @@ internal enum _FindResult { /// /// If the current node is a collision node, then the bucket value is /// set to `_Bucket.invalid`. - case found(_Bucket, _Slot) + case found(_Bucket, _HashSlot) /// The item we're looking for is not currently inside the subtree rooted at /// this node. @@ -85,7 +85,7 @@ internal enum _FindResult { /// /// When the node is a collision node, the `insertCollision` case is returned /// instead of this one. - case insert(_Bucket, _Slot) + case insert(_Bucket, _HashSlot) /// The item we're looking for is not currently inside the subtree rooted at /// this collision node. @@ -103,7 +103,7 @@ internal enum _FindResult { /// it with a new child node. /// /// (This case is never returned if the current node is a collision node.) - case spawnChild(_Bucket, _Slot) + case spawnChild(_Bucket, _HashSlot) /// The item we're looking for is not in this subtree. /// @@ -125,22 +125,22 @@ internal enum _FindResult { /// bucket & slot. /// /// (This case is never returned if the current node is a collision node.) - case descend(_Bucket, _Slot) + case descend(_Bucket, _HashSlot) } -extension _Node { +extension _HashNode { @inlinable internal func findForInsertion( - _ level: _Level, _ key: Key, _ hash: _Hash + _ level: _HashLevel, _ key: Key, _ hash: _Hash ) -> _FindResult { read { $0.findForInsertion(level, key, hash) } } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable internal func findForInsertion( - _ level: _Level, _ key: Key, _ hash: _Hash + _ level: _HashLevel, _ key: Key, _ hash: _Hash ) -> _FindResult { guard !isCollisionNode else { let r = _findInCollision(level, key, hash) @@ -172,9 +172,9 @@ extension _Node.UnsafeHandle { // MARK: Subtree-level lookup operations -extension _Node { +extension _HashNode { @inlinable - internal func get(_ level: _Level, _ key: Key, _ hash: _Hash) -> Value? { + internal func get(_ level: _HashLevel, _ key: Key, _ hash: _Hash) -> Value? { var node = unmanaged var level = level while true { @@ -191,10 +191,10 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal func containsKey( - _ level: _Level, _ key: Key, _ hash: _Hash + _ level: _HashLevel, _ key: Key, _ hash: _Hash ) -> Bool { var node = unmanaged var level = level @@ -208,11 +208,11 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal func lookup( - _ level: _Level, _ key: Key, _ hash: _Hash - ) -> (node: _UnmanagedNode, slot: _Slot)? { + _ level: _HashLevel, _ key: Key, _ hash: _Hash + ) -> (node: _UnmanagedHashNode, slot: _HashSlot)? { var node = unmanaged var level = level while true { @@ -229,10 +229,10 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal func position( - forKey key: Key, _ level: _Level, _ hash: _Hash + forKey key: Key, _ level: _HashLevel, _ hash: _Hash ) -> Int? { guard let r = find(level, key, hash) else { return nil } guard r.descend else { return r.slot.value } @@ -253,7 +253,7 @@ extension _Node { var itemsToSkip = position let itemCount = $0.itemCount if itemsToSkip < itemCount { - return $0[item: _Slot(itemsToSkip)] + return $0[item: _HashSlot(itemsToSkip)] } itemsToSkip -= itemCount let children = $0.children diff --git a/Sources/HashTreeCollections/Node/_Node+Primitive Insertions.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Insertions.swift similarity index 88% rename from Sources/HashTreeCollections/Node/_Node+Primitive Insertions.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Primitive Insertions.swift index d1f17675a..d5630eb1c 100644 --- a/Sources/HashTreeCollections/Node/_Node+Primitive Insertions.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Insertions.swift @@ -11,7 +11,7 @@ // MARK: Node-level insertion operations -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { /// Make room for a new item at `slot` corresponding to `bucket`. /// There must be enough free space in the node to fit the new item. /// @@ -23,7 +23,7 @@ extension _Node.UnsafeHandle { /// initialize this memory. @inlinable internal func _makeRoomForNewItem( - at slot: _Slot, _ bucket: _Bucket + at slot: _HashSlot, _ bucket: _Bucket ) -> UnsafeMutablePointer { assertMutable() let c = itemCount @@ -59,25 +59,25 @@ extension _Node.UnsafeHandle { /// `childMap` must not yet reflect the insertion at the time this /// function is called. This method does not update `childMap`. @inlinable - internal func _insertChild(_ child: __owned _Node, at slot: _Slot) { + internal func _insertChild(_ child: __owned _HashNode, at slot: _HashSlot) { assertMutable() assert(!isCollisionNode) let c = childMap.count assert(slot.value <= c) - let stride = MemoryLayout<_Node>.stride + let stride = MemoryLayout<_HashNode>.stride assert(bytesFree >= stride) bytesFree &-= stride - _memory.bindMemory(to: _Node.self, capacity: c &+ 1) + _memory.bindMemory(to: _HashNode.self, capacity: c &+ 1) let q = _childrenStart + slot.value (q + 1).moveInitialize(from: q, count: c &- slot.value) q.initialize(to: child) } } -extension _Node { +extension _HashNode { @inlinable @inline(__always) internal mutating func insertItem( _ item: __owned Element, at bucket: _Bucket @@ -88,7 +88,7 @@ extension _Node { @inlinable @inline(__always) internal mutating func insertItem( - _ item: __owned Element, at slot: _Slot, _ bucket: _Bucket + _ item: __owned Element, at slot: _HashSlot, _ bucket: _Bucket ) { self.count &+= 1 update { @@ -101,7 +101,7 @@ extension _Node { /// node to fit the new child. @inlinable internal mutating func insertChild( - _ child: __owned _Node, _ bucket: _Bucket + _ child: __owned _HashNode, _ bucket: _Bucket ) { count &+= child.count update { diff --git a/Sources/HashTreeCollections/Node/_Node+Primitive Removals.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Removals.swift similarity index 88% rename from Sources/HashTreeCollections/Node/_Node+Primitive Removals.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Primitive Removals.swift index 367bf7871..424a93340 100644 --- a/Sources/HashTreeCollections/Node/_Node+Primitive Removals.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Removals.swift @@ -11,7 +11,7 @@ // MARK: Node-level removal operations -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { /// Remove and return the item at `slot`, increasing the amount of free /// space available in the node. /// @@ -19,7 +19,7 @@ extension _Node.UnsafeHandle { /// function is called. This method does not update `itemMap`. @inlinable internal func _removeItem( - at slot: _Slot, + at slot: _HashSlot, by remover: (UnsafeMutablePointer) -> R ) -> R { assertMutable() @@ -46,13 +46,13 @@ extension _Node.UnsafeHandle { /// `childMap` must not yet reflect the removal at the time this /// function is called. This method does not update `childMap`. @inlinable - internal func _removeChild(at slot: _Slot) -> _Node { + internal func _removeChild(at slot: _HashSlot) -> _HashNode { assertMutable() assert(!isCollisionNode) let count = childCount assert(slot.value < count) - bytesFree &+= MemoryLayout<_Node>.stride + bytesFree &+= MemoryLayout<_HashNode>.stride let q = _childrenStart + slot.value let child = q.move() @@ -61,7 +61,7 @@ extension _Node.UnsafeHandle { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func removeItem( at bucket: _Bucket @@ -72,7 +72,7 @@ extension _Node { @inlinable internal mutating func removeItem( - at bucket: _Bucket, _ slot: _Slot + at bucket: _Bucket, _ slot: _HashSlot ) -> Element { removeItem(at: bucket, slot, by: { $0.move() }) } @@ -84,7 +84,7 @@ extension _Node { /// storage slot corresponding to the item to be removed. @inlinable internal mutating func removeItem( - at bucket: _Bucket, _ slot: _Slot, + at bucket: _Bucket, _ slot: _HashSlot, by remover: (UnsafeMutablePointer) -> R ) -> R { defer { _invariantCheck() } @@ -106,10 +106,10 @@ extension _Node { @inlinable internal mutating func removeChild( - at bucket: _Bucket, _ slot: _Slot - ) -> _Node { + at bucket: _Bucket, _ slot: _HashSlot + ) -> _HashNode { assert(!isCollisionNode) - let child: _Node = update { + let child: _HashNode = update { assert($0.childMap.contains(bucket)) assert($0.childMap.slot(of: bucket) == slot) let child = $0._removeChild(at: slot) @@ -135,9 +135,9 @@ extension _Node { } @inlinable - internal mutating func removeSingletonChild() -> _Node { + internal mutating func removeSingletonChild() -> _HashNode { defer { _invariantCheck() } - let child: _Node = update { + let child: _HashNode = update { assert($0.hasSingletonChild) let child = $0._removeChild(at: .zero) $0.childMap = .empty diff --git a/Sources/HashTreeCollections/Node/_Node+Primitive Replacement.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Replacement.swift similarity index 87% rename from Sources/HashTreeCollections/Node/_Node+Primitive Replacement.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Primitive Replacement.swift index a687a3cee..dee317d74 100644 --- a/Sources/HashTreeCollections/Node/_Node+Primitive Replacement.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Primitive Replacement.swift @@ -9,10 +9,10 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal mutating func replaceItem( - at bucket: _Bucket, _ slot: _Slot, with item: __owned Element + at bucket: _Bucket, _ slot: _HashSlot, with item: __owned Element ) { update { assert($0.isCollisionNode || $0.itemMap.contains(bucket)) @@ -24,7 +24,7 @@ extension _Node { @inlinable internal mutating func replaceChild( - at bucket: _Bucket, with child: __owned _Node + at bucket: _Bucket, with child: __owned _HashNode ) -> Int { let slot = read { $0.childMap.slot(of: bucket) } return replaceChild(at: bucket, slot, with: child) @@ -32,7 +32,7 @@ extension _Node { @inlinable internal mutating func replaceChild( - at bucket: _Bucket, _ slot: _Slot, with child: __owned _Node + at bucket: _Bucket, _ slot: _HashSlot, with child: __owned _HashNode ) -> Int { let delta: Int = update { assert(!$0.isCollisionNode) @@ -49,9 +49,9 @@ extension _Node { @inlinable internal func replacingChild( - _ level: _Level, + _ level: _HashLevel, at bucket: _Bucket, - _ slot: _Slot, + _ slot: _HashSlot, with child: __owned Builder ) -> Builder { assert(child.level == level.descend()) @@ -67,7 +67,7 @@ extension _Node { if hasSingletonChild { return .item(level, item, at: bucket) } - var node = self.copy(withFreeSpace: _Node.spaceForInlinedChild) + var node = self.copy(withFreeSpace: _HashNode.spaceForInlinedChild) _ = node.removeChild(at: bucket, slot) node.insertItem(item, at: bucket) node._invariantCheck() diff --git a/Sources/HashTreeCollections/Node/_Node+Storage.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift similarity index 81% rename from Sources/HashTreeCollections/Node/_Node+Storage.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift index 44308155a..82d9646a3 100644 --- a/Sources/HashTreeCollections/Node/_Node+Storage.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift @@ -9,35 +9,37 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// A base representation of a hash tree node, capturing functionality /// independent of the `Key` and `Value` types. @usableFromInline -internal typealias _RawStorage = ManagedBuffer<_StorageHeader, _RawNode> +internal typealias _RawHashStorage = ManagedBuffer<_HashNodeHeader, _RawHashNode> /// Type-punned storage for the singleton root node used in empty hash trees /// (of all `Key` and `Value` types). /// -/// `_Node` is carefully defined to use a `_RawStorage` reference as its +/// `_HashNode` is carefully defined to use a `_RawHashStorage` reference as its /// storage variable, so that this can work. (The only reason we need the -/// `_Node.Storage` subclass is to allow storage instances to properly clean up -/// after themselves in their `deinit` method.) +/// `_HashNode.Storage` subclass is to allow storage instances to properly +/// clean up after themselves in their `deinit` method.) @usableFromInline -internal let _emptySingleton: _RawStorage = _RawStorage.create( +internal let _emptySingleton: _RawHashStorage = _RawHashStorage.create( minimumCapacity: 0, - makingHeaderWith: { _ in _StorageHeader(byteCapacity: 0) }) + makingHeaderWith: { _ in _HashNodeHeader(byteCapacity: 0) }) -extension _Node { +extension _HashNode { /// Instances of this class hold (tail-allocated) storage for individual /// nodes in a hash tree. @usableFromInline - internal final class Storage: _RawStorage { + internal final class Storage: _RawHashStorage { @usableFromInline internal typealias Element = (key: Key, value: Value) @usableFromInline - internal typealias UnsafeHandle = _Node.UnsafeHandle + internal typealias UnsafeHandle = _HashNode.UnsafeHandle deinit { UnsafeHandle.update(self) { handle in @@ -48,13 +50,13 @@ extension _Node { } } -extension _Node.Storage { +extension _HashNode.Storage { @inlinable - internal static func allocate(byteCapacity: Int) -> _Node.Storage { + internal static func allocate(byteCapacity: Int) -> _HashNode.Storage { assert(byteCapacity >= 0) let itemStride = MemoryLayout.stride - let childStride = MemoryLayout<_Node>.stride + let childStride = MemoryLayout<_HashNode>.stride let unit = Swift.max(itemStride, childStride) // Round up request to nearest power-of-two number of units. @@ -69,16 +71,16 @@ extension _Node.Storage { var bytes = unit * capacityInUnits._roundUpToPowerOfTwo() let itemAlignment = MemoryLayout.alignment - let childAlignment = MemoryLayout<_Node>.alignment + let childAlignment = MemoryLayout<_HashNode>.alignment if itemAlignment > childAlignment { // Make sure we always have enough room to properly align trailing items. bytes += itemAlignment - childAlignment } - let object = _Node.Storage.create( + let object = _HashNode.Storage.create( minimumCapacity: (bytes &+ childStride &- 1) / childStride ) { buffer in - _StorageHeader(byteCapacity: buffer.capacity * childStride) + _HashNodeHeader(byteCapacity: buffer.capacity * childStride) } object.withUnsafeMutablePointers { header, elements in @@ -90,11 +92,11 @@ extension _Node.Storage { header.pointee._bytesFree = header.pointee._byteCapacity assert(byteCapacity <= header.pointee.byteCapacity) } - return unsafeDowncast(object, to: _Node.Storage.self) + return unsafeDowncast(object, to: _HashNode.Storage.self) } } -extension _Node { +extension _HashNode { @inlinable @inline(__always) internal static var spaceForNewItem: Int { MemoryLayout.stride @@ -102,7 +104,7 @@ extension _Node { @inlinable @inline(__always) internal static var spaceForNewChild: Int { - MemoryLayout<_Node>.stride + MemoryLayout<_HashNode>.stride } @inlinable @inline(__always) @@ -151,23 +153,23 @@ extension _Node { count: Int, extraBytes: Int = 0, initializingWith initializer: ( - UnsafeMutableBufferPointer<_Node>, UnsafeMutableBufferPointer + UnsafeMutableBufferPointer<_HashNode>, UnsafeMutableBufferPointer ) -> R - ) -> (node: _Node, result: R) { + ) -> (node: _HashNode, result: R) { assert(extraBytes >= 0) assert(itemMap.isDisjoint(with: childMap)) // No collisions let itemCount = itemMap.count let childCount = childMap.count let itemStride = MemoryLayout.stride - let childStride = MemoryLayout<_Node>.stride + let childStride = MemoryLayout<_HashNode>.stride let itemBytes = itemCount * itemStride let childBytes = childCount * childStride let occupiedBytes = itemBytes &+ childBytes let storage = Storage.allocate( byteCapacity: occupiedBytes &+ extraBytes) - var node = _Node(storage: storage, count: count) + var node = _HashNode(storage: storage, count: count) let result: R = node.update { $0.itemMap = itemMap $0.childMap = childMap @@ -176,7 +178,7 @@ extension _Node { $0.bytesFree &-= occupiedBytes let childStart = $0._memory - .bindMemory(to: _Node.self, capacity: childCount) + .bindMemory(to: _HashNode.self, capacity: childCount) let itemStart = ($0._memory + ($0.byteCapacity - itemBytes)) .bindMemory(to: Element.self, capacity: itemCount) @@ -193,15 +195,15 @@ extension _Node { _ hash: _Hash, extraBytes: Int = 0, initializingWith initializer: (UnsafeMutableBufferPointer) -> R - ) -> (node: _Node, result: R) { + ) -> (node: _HashNode, result: R) { assert(count >= 2) assert(extraBytes >= 0) let itemBytes = count * MemoryLayout.stride let hashBytes = MemoryLayout<_Hash>.stride let bytes = itemBytes &+ hashBytes - assert(MemoryLayout<_Hash>.alignment <= MemoryLayout<_RawNode>.alignment) + assert(MemoryLayout<_Hash>.alignment <= MemoryLayout<_RawHashNode>.alignment) let storage = Storage.allocate(byteCapacity: bytes &+ extraBytes) - var node = _Node(storage: storage, count: count) + var node = _HashNode(storage: storage, count: count) let result: R = node.update { $0.itemMap = _Bitmap(bitPattern: count) $0.childMap = $0.itemMap @@ -221,7 +223,7 @@ extension _Node { @inlinable @inline(never) - internal func copy(withFreeSpace space: Int = 0) -> _Node { + internal func copy(withFreeSpace space: Int = 0) -> _HashNode { assert(space >= 0) if isCollisionNode { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural compactMapValues.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural compactMapValues.swift similarity index 89% rename from Sources/HashTreeCollections/Node/_Node+Structural compactMapValues.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural compactMapValues.swift index a52b4384f..c5abe96c5 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural compactMapValues.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural compactMapValues.swift @@ -9,14 +9,14 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func compactMapValues( - _ level: _Level, + _ level: _HashLevel, _ transform: (Value) throws -> T? - ) rethrows -> _Node.Builder { + ) rethrows -> _HashNode.Builder { return try self.read { - var result: _Node.Builder = .empty(level) + var result: _HashNode.Builder = .empty(level) if isCollisionNode { let items = $0.reverseItems diff --git a/Sources/HashTreeCollections/Node/_Node+Structural filter.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural filter.swift similarity index 94% rename from Sources/HashTreeCollections/Node/_Node+Structural filter.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural filter.swift index 93cc75c9a..45b8293bd 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural filter.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural filter.swift @@ -9,10 +9,10 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func filter( - _ level: _Level, + _ level: _HashLevel, _ isIncluded: (Element) throws -> Bool ) rethrows -> Builder? { guard !isCollisionNode else { @@ -57,14 +57,14 @@ extension _Node { @inlinable @inline(never) internal func _filter_slow( - _ level: _Level, + _ level: _HashLevel, _ isIncluded: (Element) throws -> Bool ) rethrows -> Builder? { try self.read { var result: Builder = .empty(level) var removing = false - for slot: _Slot in stride(from: .zero, to: $0.itemsEndSlot, by: 1) { + for slot: _HashSlot in stride(from: .zero, to: $0.itemsEndSlot, by: 1) { let p = $0.itemPtr(at: slot) let include = try isIncluded(p.pointee) if include, removing { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural intersection.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural intersection.swift similarity index 95% rename from Sources/HashTreeCollections/Node/_Node+Structural intersection.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural intersection.swift index 72adda82c..511a6c2e3 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural intersection.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural intersection.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func intersection( - _ level: _Level, - _ other: _Node - ) -> _Node? { + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode? { assert(level.isAtRoot) let builder = _intersection(level, other) guard let builder = builder else { return nil } @@ -25,8 +25,8 @@ extension _Node { @inlinable internal func _intersection( - _ level: _Level, - _ other: _Node + _ level: _HashLevel, + _ other: _HashNode ) -> Builder? { if self.raw.storage === other.raw.storage { return nil } @@ -106,8 +106,8 @@ extension _Node { @inlinable @inline(never) internal func _intersection_slow( - _ level: _Level, - _ other: _Node + _ level: _HashLevel, + _ other: _HashNode ) -> Builder? { let lc = self.isCollisionNode let rc = other.isCollisionNode @@ -119,7 +119,7 @@ extension _Node { var removing = false let ritems = r.reverseItems - for lslot: _Slot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { + for lslot: _HashSlot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { let lp = l.itemPtr(at: lslot) let include = ritems.contains { $0.key == lp.pointee.key } if include, removing { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural isDisjoint.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isDisjoint.swift similarity index 96% rename from Sources/HashTreeCollections/Node/_Node+Structural isDisjoint.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural isDisjoint.swift index 80eb8ecfb..539d55b38 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural isDisjoint.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isDisjoint.swift @@ -9,13 +9,13 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { /// Returns true if `self` contains a disjoint set of keys than `other`. /// Otherwise, returns false. @inlinable @inline(never) internal func isDisjoint( - _ level: _Level, - with other: _Node + _ level: _HashLevel, + with other: _HashNode ) -> Bool { if self.count == 0 || other.count == 0 { return true } if self.raw.storage === other.raw.storage { return false } @@ -72,8 +72,8 @@ extension _Node { @inlinable @inline(never) internal func _isDisjointCollision( - _ level: _Level, - with other: _Node + _ level: _HashLevel, + with other: _HashNode ) -> Bool { assert(isCollisionNode) if other.isCollisionNode { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural isEqualSet.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isEqualSet.swift similarity index 97% rename from Sources/HashTreeCollections/Node/_Node+Structural isEqualSet.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural isEqualSet.swift index 530d417e2..90e376da0 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural isEqualSet.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isEqualSet.swift @@ -10,10 +10,10 @@ //===----------------------------------------------------------------------===// // TODO: `Equatable` needs more test coverage, apart from hash-collision smoke test -extension _Node { +extension _HashNode { @inlinable internal func isEqualSet( - to other: _Node, + to other: _HashNode, by areEquivalent: (Value, Value2) -> Bool ) -> Bool { if self.raw.storage === other.raw.storage { return true } diff --git a/Sources/HashTreeCollections/Node/_Node+Structural isSubset.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isSubset.swift similarity index 97% rename from Sources/HashTreeCollections/Node/_Node+Structural isSubset.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural isSubset.swift index 261f9cf0b..c217a0c28 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural isSubset.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural isSubset.swift @@ -9,13 +9,13 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { /// Returns true if `self` contains a subset of the keys in `other`. /// Otherwise, returns false. @inlinable @inline(never) internal func isSubset( - _ level: _Level, - of other: _Node + _ level: _HashLevel, + of other: _HashNode ) -> Bool { guard self.count > 0 else { return true } if self.raw.storage === other.raw.storage { return true } diff --git a/Sources/HashTreeCollections/Node/_Node+Structural mapValues.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural mapValues.swift similarity index 87% rename from Sources/HashTreeCollections/Node/_Node+Structural mapValues.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural mapValues.swift index abab98ec9..9c82114c3 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural mapValues.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural mapValues.swift @@ -9,23 +9,25 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif -extension _Node { +extension _HashNode { @inlinable internal func mapValues( _ transform: (Element) throws -> T - ) rethrows -> _Node { + ) rethrows -> _HashNode { let c = self.count return try read { source in - var result: _Node + var result: _HashNode if isCollisionNode { - result = _Node.allocateCollision( + result = _HashNode.allocateCollision( count: c, source.collisionHash, initializingWith: { _ in } ).node } else { - result = _Node.allocate( + result = _HashNode.allocate( itemMap: source.itemMap, childMap: source.childMap, count: c, @@ -75,9 +77,9 @@ extension _Node { @inlinable internal func mapValuesToVoid( copy: Bool = false, extraBytes: Int = 0 - ) -> _Node { + ) -> _HashNode { if Value.self == Void.self { - let node = unsafeBitCast(self, to: _Node.self) + let node = unsafeBitCast(self, to: _HashNode.self) guard copy || !node.hasFreeSpace(extraBytes) else { return node } return node.copy(withFreeSpace: extraBytes) } diff --git a/Sources/HashTreeCollections/Node/_Node+Structural merge.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural merge.swift similarity index 93% rename from Sources/HashTreeCollections/Node/_Node+Structural merge.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural merge.swift index 4d5daf3e7..e0d495ecd 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural merge.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural merge.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { /// - Returns: The number of new items added to `self`. @inlinable internal mutating func merge( - _ level: _Level, - _ other: _Node, + _ level: _HashLevel, + _ other: _HashNode, _ combine: (Value, Value) throws -> Value ) rethrows -> Int { guard other.count > 0 else { return 0 } @@ -52,8 +52,8 @@ extension _Node { @inlinable internal mutating func _merge( - _ level: _Level, - _ other: _Node, + _ level: _HashLevel, + _ other: _HashNode, _ combine: (Value, Value) throws -> Value ) rethrows -> Int { // Note: don't check storage identities -- we do need to merge the contents @@ -105,7 +105,7 @@ extension _Node { let rp = r.childPtr(at: rslot) self.ensureUnique( - isUnique: isUnique, withFreeSpace: _Node.spaceForSpawningChild) + isUnique: isUnique, withFreeSpace: _HashNode.spaceForSpawningChild) let item = self.removeItem(at: bucket) delta &-= 1 var child = rp.pointee @@ -182,7 +182,7 @@ extension _Node { for (bucket, _) in r.childMap.subtracting(seen) { let rslot = r.childMap.slot(of: bucket) self.ensureUnique( - isUnique: isUnique, withFreeSpace: _Node.spaceForNewChild) + isUnique: isUnique, withFreeSpace: _HashNode.spaceForNewChild) self.insertChild(r[child: rslot], bucket) delta &+= r[child: rslot].count isUnique = true @@ -195,15 +195,15 @@ extension _Node { @inlinable @inline(never) internal mutating func _merge_slow( - _ level: _Level, - _ other: _Node, + _ level: _HashLevel, + _ other: _HashNode, _ combine: (Value, Value) throws -> Value ) rethrows -> Int { let lc = self.isCollisionNode let rc = other.isCollisionNode if lc && rc { guard self.collisionHash == other.collisionHash else { - self = _Node.build( + self = _HashNode.build( level: level, child1: self, self.collisionHash, child2: other, other.collisionHash) @@ -213,14 +213,14 @@ extension _Node { var isUnique = self.isUnique() var delta = 0 let originalItemCount = self.count - for rs: _Slot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { + for rs: _HashSlot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { let rp = r.itemPtr(at: rs) - let lslot: _Slot? = self.read { l in + let lslot: _HashSlot? = self.read { l in let litems = l.reverseItems return litems .suffix(originalItemCount) .firstIndex { $0.key == rp.pointee.key } - .map { _Slot(litems.count &- 1 &- $0) } + .map { _HashSlot(litems.count &- 1 &- $0) } } if let lslot = lslot { self.ensureUnique(isUnique: isUnique) @@ -278,7 +278,7 @@ extension _Node { return self.count - originalCount } - var node = other.copy(withFreeSpace: _Node.spaceForNewChild) + var node = other.copy(withFreeSpace: _HashNode.spaceForNewChild) node.insertChild(self, bucket) self = node return other.count @@ -292,7 +292,7 @@ extension _Node { let bucket = r.collisionHash[level] if self.read({ $0.itemMap.contains(bucket) }) { self.ensureUnique( - isUnique: isUnique, withFreeSpace: _Node.spaceForSpawningChild) + isUnique: isUnique, withFreeSpace: _HashNode.spaceForSpawningChild) let item = self.removeItem(at: bucket) let h = _Hash(item.key) var copy = other @@ -321,7 +321,7 @@ extension _Node { return delta } self.ensureUnique( - isUnique: isUnique, withFreeSpace: _Node.spaceForNewChild) + isUnique: isUnique, withFreeSpace: _HashNode.spaceForNewChild) self.insertChild(other, bucket) return other.count } diff --git a/Sources/HashTreeCollections/Node/_Node+Structural subtracting.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural subtracting.swift similarity index 95% rename from Sources/HashTreeCollections/Node/_Node+Structural subtracting.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural subtracting.swift index 4540974b7..43b4e953d 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural subtracting.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural subtracting.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func subtracting( - _ level: _Level, - _ other: _Node - ) -> _Node? { + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode? { assert(level.isAtRoot) let builder = _subtracting(level, other) guard let builder = builder else { return nil } @@ -25,8 +25,8 @@ extension _Node { @inlinable internal func _subtracting( - _ level: _Level, - _ other: _Node + _ level: _HashLevel, + _ other: _HashNode ) -> Builder? { if self.raw.storage === other.raw.storage { return .empty(level) } @@ -109,8 +109,8 @@ extension _Node { @inlinable @inline(never) internal func _subtracting_slow( - _ level: _Level, - _ other: _Node + _ level: _HashLevel, + _ other: _HashNode ) -> Builder? { let lc = self.isCollisionNode let rc = other.isCollisionNode @@ -124,7 +124,7 @@ extension _Node { var removing = false let ritems = r.reverseItems - for lslot: _Slot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { + for lslot: _HashSlot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { let lp = l.itemPtr(at: lslot) let include = !ritems.contains { $0.key == lp.pointee.key } if include, removing { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural symmetricDifference.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural symmetricDifference.swift similarity index 91% rename from Sources/HashTreeCollections/Node/_Node+Structural symmetricDifference.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural symmetricDifference.swift index b236e3e52..5ea7810f9 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural symmetricDifference.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural symmetricDifference.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func symmetricDifference( - _ level: _Level, - _ other: _Node - ) -> _Node.Builder? { + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode.Builder? { guard self.count > 0 else { return .init(level, other.mapValuesToVoid()) } @@ -24,10 +24,10 @@ extension _Node { @inlinable internal func _symmetricDifference( - _ level: _Level, - _ other: _Node - ) -> _Node.Builder { - typealias VoidNode = _Node + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode.Builder { + typealias VoidNode = _HashNode assert(self.count > 0 && other.count > 0) @@ -130,9 +130,9 @@ extension _Node { @inlinable @inline(never) internal func _symmetricDifference_slow( - _ level: _Level, - _ other: _Node - ) -> _Node.Builder { + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode.Builder { switch (self.isCollisionNode, other.isCollisionNode) { case (true, true): return self._symmetricDifference_slow_both(level, other) @@ -145,10 +145,10 @@ extension _Node { @inlinable internal func _symmetricDifference_slow_both( - _ level: _Level, - _ other: _Node - ) -> _Node.Builder { - typealias VoidNode = _Node + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode.Builder { + typealias VoidNode = _HashNode return read { l in other.read { r in guard l.collisionHash == r.collisionHash else { @@ -160,7 +160,7 @@ extension _Node { } var result: VoidNode.Builder = .empty(level) let ritems = r.reverseItems - for ls: _Slot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { + for ls: _HashSlot in stride(from: .zero, to: l.itemsEndSlot, by: 1) { let lp = l.itemPtr(at: ls) let include = !ritems.contains(where: { $0.key == lp.pointee.key }) if include { @@ -170,7 +170,7 @@ extension _Node { // FIXME: Consider remembering slots of shared items in r by // caching them in a bitset. let litems = l.reverseItems - for rs: _Slot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { + for rs: _HashSlot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { let rp = r.itemPtr(at: rs) let include = !litems.contains(where: { $0.key == rp.pointee.key }) if include { @@ -184,10 +184,10 @@ extension _Node { @inlinable internal func _symmetricDifference_slow_left( - _ level: _Level, - _ other: _Node - ) -> _Node.Builder { - typealias VoidNode = _Node + _ level: _HashLevel, + _ other: _HashNode + ) -> _HashNode.Builder { + typealias VoidNode = _HashNode // `self` is a collision node on a compressed path. The other tree might // have the same set of collisions, just expanded a bit deeper. return read { l in @@ -218,7 +218,7 @@ extension _Node { with: (litems[1 &- li].key, ())) return .node(level, node) } - let lslot = _Slot(litems.count &- 1 &- li) + let lslot = _HashSlot(litems.count &- 1 &- li) var child = self.mapValuesToVoid(copy: true) _ = child.removeItem(at: .invalid, lslot) if other.hasSingletonItem { diff --git a/Sources/HashTreeCollections/Node/_Node+Structural union.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Structural union.swift similarity index 90% rename from Sources/HashTreeCollections/Node/_Node+Structural union.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Structural union.swift index 4f0b3b21d..8ef23befd 100644 --- a/Sources/HashTreeCollections/Node/_Node+Structural union.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Structural union.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { @inlinable internal func union( - _ level: _Level, - _ other: _Node - ) -> (copied: Bool, node: _Node) { + _ level: _HashLevel, + _ other: _HashNode + ) -> (copied: Bool, node: _HashNode) { guard self.count > 0 else { return (true, other.mapValuesToVoid()) } guard other.count > 0 else { return (false, self.mapValuesToVoid()) } if level.isAtRoot, self.hasSingletonItem { @@ -42,9 +42,9 @@ extension _Node { @inlinable internal func _union( - _ level: _Level, - _ other: _Node - ) -> (copied: Bool, node: _Node) { + _ level: _HashLevel, + _ other: _HashNode + ) -> (copied: Bool, node: _HashNode) { if self.raw.storage === other.raw.storage { return (false, self.mapValuesToVoid()) } @@ -88,7 +88,7 @@ extension _Node { let rp = r.childPtr(at: rslot) node.ensureUnique( - isUnique: copied, withFreeSpace: _Node.spaceForSpawningChild) + isUnique: copied, withFreeSpace: _HashNode.spaceForSpawningChild) let item = node.removeItem(at: bucket) let r = rp.pointee.mapValuesToVoid() .inserting(level.descend(), (item.key, ()), _Hash(item.key)) @@ -142,7 +142,7 @@ extension _Node { for (bucket, _) in r.childMap.subtracting(seen) { let rslot = r.childMap.slot(of: bucket) node.ensureUnique( - isUnique: copied, withFreeSpace: _Node.spaceForNewChild) + isUnique: copied, withFreeSpace: _HashNode.spaceForNewChild) copied = true node.insertChild(r[child: rslot].mapValuesToVoid(), bucket) } @@ -154,16 +154,16 @@ extension _Node { @inlinable @inline(never) internal func _union_slow( - _ level: _Level, - _ other: _Node - ) -> (copied: Bool, node: _Node) { + _ level: _HashLevel, + _ other: _HashNode + ) -> (copied: Bool, node: _HashNode) { let lc = self.isCollisionNode let rc = other.isCollisionNode if lc && rc { return read { l in other.read { r in guard l.collisionHash == r.collisionHash else { - let node = _Node.build( + let node = _HashNode.build( level: level, child1: self.mapValuesToVoid(), l.collisionHash, child2: other.mapValuesToVoid(), r.collisionHash) @@ -172,7 +172,7 @@ extension _Node { var copied = false var node = self.mapValuesToVoid() let litems = l.reverseItems - for rs: _Slot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { + for rs: _HashSlot in stride(from: .zero, to: r.itemsEndSlot, by: 1) { let p = r.itemPtr(at: rs) if !litems.contains(where: { $0.key == p.pointee.key }) { _ = node.ensureUniqueAndAppendCollision( @@ -218,7 +218,7 @@ extension _Node { } var node = other.mapValuesToVoid( - copy: true, extraBytes: _Node.spaceForNewChild) + copy: true, extraBytes: _HashNode.spaceForNewChild) node.insertChild(self.mapValuesToVoid(), bucket) return (true, node) } @@ -227,8 +227,8 @@ extension _Node { assert(rc) // `other` is a collision node on a compressed path. - return read { l -> (copied: Bool, node: _Node) in - other.read { r -> (copied: Bool, node: _Node) in + return read { l -> (copied: Bool, node: _HashNode) in + other.read { r -> (copied: Bool, node: _HashNode) in let bucket = r.collisionHash[level] if l.itemMap.contains(bucket) { let lslot = l.itemMap.slot(of: bucket) @@ -248,7 +248,7 @@ extension _Node { } var node = self.mapValuesToVoid( - copy: true, extraBytes: _Node.spaceForNewChild) + copy: true, extraBytes: _HashNode.spaceForNewChild) node.insertChild(other.mapValuesToVoid(), bucket) return (true, node) } diff --git a/Sources/HashTreeCollections/Node/_Node+Subtree Insertions.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Insertions.swift similarity index 88% rename from Sources/HashTreeCollections/Node/_Node+Subtree Insertions.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Subtree Insertions.swift index 1c131781c..915ed6d96 100644 --- a/Sources/HashTreeCollections/Node/_Node+Subtree Insertions.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Insertions.swift @@ -9,25 +9,27 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif -extension _Node { +extension _HashNode { @inlinable internal mutating func insert( - _ level: _Level, + _ level: _HashLevel, _ item: Element, _ hash: _Hash - ) -> (inserted: Bool, leaf: _UnmanagedNode, slot: _Slot) { + ) -> (inserted: Bool, leaf: _UnmanagedHashNode, slot: _HashSlot) { insert(level, item.key, hash) { $0.initialize(to: item) } } @inlinable internal mutating func insert( - _ level: _Level, + _ level: _HashLevel, _ key: Key, _ hash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (inserted: Bool, leaf: _UnmanagedNode, slot: _Slot) { + ) -> (inserted: Bool, leaf: _UnmanagedHashNode, slot: _HashSlot) { defer { _invariantCheck() } let isUnique = self.isUnique() if !isUnique { @@ -56,7 +58,7 @@ extension _Node { inserter) return (true, r.leaf, r.slot) case .expansion: - let r = _Node.build( + let r = _HashNode.build( level: level, item1: inserter, hash, child2: self, self.collisionHash) @@ -73,20 +75,24 @@ extension _Node { @inlinable internal func inserting( - _ level: _Level, + _ level: _HashLevel, _ item: __owned Element, _ hash: _Hash - ) -> (inserted: Bool, node: _Node, leaf: _UnmanagedNode, slot: _Slot) { + ) -> ( + inserted: Bool, node: _HashNode, leaf: _UnmanagedHashNode, slot: _HashSlot + ) { inserting(level, item.key, hash, { $0.initialize(to: item) }) } @inlinable internal func inserting( - _ level: _Level, + _ level: _HashLevel, _ key: Key, _ hash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (inserted: Bool, node: _Node, leaf: _UnmanagedNode, slot: _Slot) { + ) -> ( + inserted: Bool, node: _HashNode, leaf: _UnmanagedHashNode, slot: _HashSlot + ) { defer { _invariantCheck() } let r = findForInsertion(level, key, hash) switch r { @@ -109,7 +115,7 @@ extension _Node { inserter) return (true, r.node, r.leaf, r.slot) case .expansion: - let r = _Node.build( + let r = _HashNode.build( level: level, item1: inserter, hash, child2: self, self.collisionHash) @@ -130,11 +136,11 @@ extension _Node { @inlinable internal mutating func updateValue( - _ level: _Level, + _ level: _HashLevel, forKey key: Key, _ hash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (inserted: Bool, leaf: _UnmanagedNode, slot: _Slot) { + ) -> (inserted: Bool, leaf: _UnmanagedHashNode, slot: _HashSlot) { defer { _invariantCheck() } let isUnique = self.isUnique() let r = findForInsertion(level, key, hash) @@ -159,7 +165,7 @@ extension _Node { inserter) return (true, r.leaf, r.slot) case .expansion: - let r = _Node.build( + let r = _HashNode.build( level: level, item1: inserter, hash, child2: self, self.collisionHash) @@ -177,7 +183,7 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func ensureUniqueAndInsertItem( isUnique: Bool, @@ -198,7 +204,7 @@ extension _Node { internal mutating func ensureUniqueAndInsertItem( isUnique: Bool, at bucket: _Bucket, - itemSlot slot: _Slot, + itemSlot slot: _HashSlot, _ inserter: (UnsafeMutablePointer) -> Void ) { assert(!isCollisionNode) @@ -222,9 +228,9 @@ extension _Node { @inlinable @inline(never) internal func copyNodeAndInsertItem( at bucket: _Bucket, - itemSlot slot: _Slot, + itemSlot slot: _HashSlot, _ inserter: (UnsafeMutablePointer) -> Void - ) -> _Node { + ) -> _HashNode { assert(!isCollisionNode) let c = self.count return read { src in @@ -253,7 +259,7 @@ extension _Node { @inlinable @inline(never) internal mutating func resizeNodeAndInsertItem( at bucket: _Bucket, - itemSlot slot: _Slot, + itemSlot slot: _HashSlot, _ inserter: (UnsafeMutablePointer) -> Void ) { assert(!isCollisionNode) @@ -284,12 +290,12 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func ensureUniqueAndAppendCollision( isUnique: Bool, _ item: Element - ) -> _Slot { + ) -> _HashSlot { ensureUniqueAndAppendCollision(isUnique: isUnique) { $0.initialize(to: item) } @@ -299,7 +305,7 @@ extension _Node { internal mutating func ensureUniqueAndAppendCollision( isUnique: Bool, _ inserter: (UnsafeMutablePointer) -> Void - ) -> _Slot { + ) -> _HashSlot { assert(isCollisionNode) if !isUnique { let r = copyNodeAndAppendCollision(inserter) @@ -315,13 +321,13 @@ extension _Node { inserter(p) } self.count &+= 1 - return _Slot(self.count &- 1) + return _HashSlot(self.count &- 1) } @inlinable @inline(never) internal func copyNodeAndAppendCollision( _ inserter: (UnsafeMutablePointer) -> Void - ) -> (node: _Node, slot: _Slot) { + ) -> (node: _HashNode, slot: _HashSlot) { assert(isCollisionNode) assert(self.count == read { $0.collisionCount }) let c = self.count @@ -333,13 +339,13 @@ extension _Node { inserter(dstItems.baseAddress!) }.node } - return (node, _Slot(c)) + return (node, _HashSlot(c)) } @inlinable @inline(never) internal mutating func resizeNodeAndAppendCollision( _ inserter: (UnsafeMutablePointer) -> Void - ) -> _Slot { + ) -> _HashSlot { assert(isCollisionNode) assert(self.count == read { $0.collisionCount }) let c = self.count @@ -353,18 +359,18 @@ extension _Node { src.clear() }.node } - return _Slot(c) + return _HashSlot(c) } } -extension _Node { +extension _HashNode { @inlinable internal func _copyNodeAndReplaceItemWithNewChild( - level: _Level, - _ newChild: __owned _Node, + level: _HashLevel, + _ newChild: __owned _HashNode, at bucket: _Bucket, - itemSlot: _Slot - ) -> _Node { + itemSlot: _HashSlot + ) -> _HashNode { let c = self.count return read { src in assert(!src.isCollisionNode) @@ -409,13 +415,13 @@ extension _Node { /// this function is called. @inlinable internal mutating func _resizeNodeAndReplaceItemWithNewChild( - level: _Level, - _ newChild: __owned _Node, + level: _HashLevel, + _ newChild: __owned _HashNode, at bucket: _Bucket, - itemSlot: _Slot + itemSlot: _HashSlot ) { let c = self.count - let node: _Node = update { src in + let node: _HashNode = update { src in assert(!src.isCollisionNode) assert(src.itemMap.contains(bucket)) assert(!src.childMap.contains(bucket)) @@ -453,14 +459,14 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable @inline(never) internal func copyNodeAndPushItemIntoNewChild( - level: _Level, - _ newChild: __owned _Node, + level: _HashLevel, + _ newChild: __owned _HashNode, at bucket: _Bucket, - itemSlot: _Slot - ) -> _Node { + itemSlot: _HashSlot + ) -> _HashNode { assert(!isCollisionNode) let item = read { $0[item: itemSlot] } let hash = _Hash(item.key) @@ -473,22 +479,22 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func ensureUniqueAndSpawnChild( isUnique: Bool, - level: _Level, + level: _HashLevel, replacing bucket: _Bucket, - itemSlot: _Slot, + itemSlot: _HashSlot, newHash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (leaf: _UnmanagedNode, slot: _Slot) { + ) -> (leaf: _UnmanagedHashNode, slot: _HashSlot) { let existingHash = read { _Hash($0[item: itemSlot].key) } assert(existingHash.isEqual(to: newHash, upTo: level)) if newHash == existingHash, hasSingletonItem { // Convert current node to a collision node. - self = _Node._collisionNode(newHash, read { $0[item: .zero] }, inserter) - return (unmanaged, _Slot(1)) + self = _HashNode._collisionNode(newHash, read { $0[item: .zero] }, inserter) + return (unmanaged, _HashSlot(1)) } if !isUnique { @@ -513,7 +519,7 @@ extension _Node { } let existing = removeItem(at: bucket, itemSlot) - let r = _Node.build( + let r = _HashNode.build( level: level.descend(), item1: existing, existingHash, item2: inserter, newHash) @@ -523,15 +529,15 @@ extension _Node { @inlinable @inline(never) internal func copyNodeAndSpawnChild( - level: _Level, + level: _HashLevel, replacing bucket: _Bucket, - itemSlot: _Slot, + itemSlot: _HashSlot, existingHash: _Hash, newHash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (node: _Node, leaf: _UnmanagedNode, slot: _Slot) { + ) -> (node: _HashNode, leaf: _UnmanagedHashNode, slot: _HashSlot) { let r = read { - _Node.build( + _HashNode.build( level: level.descend(), item1: $0[item: itemSlot], existingHash, item2: inserter, newHash) @@ -547,15 +553,15 @@ extension _Node { @inlinable @inline(never) internal mutating func resizeNodeAndSpawnChild( - level: _Level, + level: _HashLevel, replacing bucket: _Bucket, - itemSlot: _Slot, + itemSlot: _HashSlot, existingHash: _Hash, newHash: _Hash, _ inserter: (UnsafeMutablePointer) -> Void - ) -> (leaf: _UnmanagedNode, slot: _Slot) { + ) -> (leaf: _UnmanagedHashNode, slot: _HashSlot) { let r = update { - _Node.build( + _HashNode.build( level: level.descend(), item1: $0.itemPtr(at: itemSlot).move(), existingHash, item2: inserter, newHash) diff --git a/Sources/HashTreeCollections/Node/_Node+Subtree Modify.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Modify.swift similarity index 93% rename from Sources/HashTreeCollections/Node/_Node+Subtree Modify.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Subtree Modify.swift index fcf5bac06..f53c81b96 100644 --- a/Sources/HashTreeCollections/Node/_Node+Subtree Modify.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Modify.swift @@ -11,11 +11,11 @@ // MARK: Subtree-level in-place mutation operations -extension _Node { +extension _HashNode { @inlinable internal mutating func ensureUnique( - level: _Level, at path: _UnsafePath - ) -> (leaf: _UnmanagedNode, slot: _Slot) { + level: _HashLevel, at path: _UnsafePath + ) -> (leaf: _UnmanagedHashNode, slot: _HashSlot) { ensureUnique(isUnique: isUnique()) guard level < path.level else { return (unmanaged, path.currentItemSlot) } return update { @@ -25,7 +25,7 @@ extension _Node { } } -extension _Node { +extension _HashNode { @usableFromInline @frozen internal struct ValueUpdateState { @@ -93,7 +93,7 @@ extension _Node { state.path.selectItem(at: slot) case .appendCollision: - state.path.selectItem(at: _Slot(self.count)) + state.path.selectItem(at: _HashSlot(self.count)) case .spawnChild(_, let slot): state.path.selectItem(at: slot) @@ -140,7 +140,7 @@ extension _Node { @inlinable internal mutating func _finalizeRemoval( - _ level: _Level, _ hash: _Hash, at path: _UnsafePath + _ level: _HashLevel, _ hash: _Hash, at path: _UnsafePath ) -> Element? { assert(isUnique()) if level == path.level { @@ -157,7 +157,7 @@ extension _Node { } } -extension _Node { +extension _HashNode { @usableFromInline @frozen internal struct DefaultedValueUpdateState { @@ -165,10 +165,10 @@ extension _Node { internal var item: Element @usableFromInline - internal var node: _UnmanagedNode + internal var node: _UnmanagedHashNode @usableFromInline - internal var slot: _Slot + internal var slot: _HashSlot @usableFromInline internal var inserted: Bool @@ -176,8 +176,8 @@ extension _Node { @inlinable internal init( _ item: Element, - in node: _UnmanagedNode, - at slot: _Slot, + in node: _UnmanagedHashNode, + at slot: _HashSlot, inserted: Bool ) { self.item = item @@ -189,7 +189,7 @@ extension _Node { @inlinable internal mutating func prepareDefaultedValueUpdate( - _ level: _Level, + _ level: _HashLevel, _ key: Key, _ defaultValue: () -> Value, _ hash: _Hash @@ -237,7 +237,7 @@ extension _Node { inserted: true) case .expansion: - let r = _Node.build( + let r = _HashNode.build( level: level, item1: { _ in }, hash, child2: self, self.collisionHash diff --git a/Sources/HashTreeCollections/Node/_Node+Subtree Removals.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Removals.swift similarity index 90% rename from Sources/HashTreeCollections/Node/_Node+Subtree Removals.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+Subtree Removals.swift index 302b91c46..1df9ee610 100644 --- a/Sources/HashTreeCollections/Node/_Node+Subtree Removals.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Subtree Removals.swift @@ -11,7 +11,7 @@ // MARK: Subtree-level removal operations -extension _Node { +extension _HashNode { /// Remove the item with the specified key from this subtree and return it. /// /// This function may leave `self` containing a singleton item. @@ -19,7 +19,7 @@ extension _Node { /// by inlining the remaining item into the parent node. @inlinable internal mutating func remove( - _ level: _Level, _ key: Key, _ hash: _Hash + _ level: _HashLevel, _ key: Key, _ hash: _Hash ) -> (removed: Element, remainder: Element?)? { guard self.isUnique() else { guard let r = removing(level, key, hash) else { return nil } @@ -46,10 +46,10 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal func removing( - _ level: _Level, _ key: Key, _ hash: _Hash + _ level: _HashLevel, _ key: Key, _ hash: _Hash ) -> (removed: Element, replacement: Builder)? { guard let r = find(level, key, hash) else { return nil } let bucket = hash[level] @@ -64,10 +64,10 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func remove( - _ level: _Level, at path: _UnsafePath + _ level: _HashLevel, at path: _UnsafePath ) -> (removed: Element, remainder: Element?) { defer { _invariantCheck() } guard self.isUnique() else { @@ -94,7 +94,7 @@ extension _Node { @inlinable internal func removing( - _ level: _Level, at path: _UnsafePath + _ level: _HashLevel, at path: _UnsafePath ) -> (removed: Element, replacement: Builder) { if level == path.level { let slot = path.currentItemSlot @@ -112,12 +112,12 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func _removeItemFromUniqueLeafNode( - _ level: _Level, + _ level: _HashLevel, at bucket: _Bucket, - _ slot: _Slot, + _ slot: _HashSlot, by remover: (UnsafeMutablePointer) -> R ) -> (result: R, remainder: Element?) { assert(isUnique()) @@ -140,7 +140,7 @@ extension _Node { @inlinable internal func _removingItemFromLeaf( - _ level: _Level, at bucket: _Bucket, _ slot: _Slot + _ level: _HashLevel, at bucket: _Bucket, _ slot: _HashSlot ) -> (removed: Element, replacement: Builder) { read { if $0.isCollisionNode { @@ -148,7 +148,7 @@ extension _Node { if $0.collisionCount == 2 { // Node will evaporate - let remainder = _Slot(1 &- slot.value) + let remainder = _HashSlot(1 &- slot.value) let bucket = $0.collisionHash[level] return ( removed: $0[item: slot], @@ -178,7 +178,7 @@ extension _Node { if $0.itemMap.count == 2 && $0.childMap.isEmpty { // Evaporating node - let remainder = _Slot(1 &- slot.value) + let remainder = _HashSlot(1 &- slot.value) var map = $0.itemMap if remainder != .zero { _ = map.popFirst() } @@ -196,21 +196,21 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal func _removingChild( - _ level: _Level, at bucket: _Bucket, _ slot: _Slot + _ level: _HashLevel, at bucket: _Bucket, _ slot: _HashSlot ) -> Builder { read { assert(!$0.isCollisionNode && $0.childMap.contains(bucket)) let willAtrophy = ( $0.itemMap.isEmpty && $0.childCount == 2 - && $0[child: _Slot(1 &- slot.value)].isCollisionNode + && $0[child: _HashSlot(1 &- slot.value)].isCollisionNode ) if willAtrophy { // Compression - let child = $0[child: _Slot(1 &- slot.value)] + let child = $0[child: _HashSlot(1 &- slot.value)] return .collisionNode(level, child) } if $0.itemMap.hasExactlyOneMember && $0.childMap.hasExactlyOneMember { @@ -229,12 +229,12 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func _fixupUniqueAncestorAfterItemRemoval( - _ level: _Level, + _ level: _HashLevel, at bucket: (UnsafeHandle) -> _Bucket, - _ childSlot: _Slot, + _ childSlot: _HashSlot, remainder: Element? ) -> Element? { assert(isUnique()) @@ -261,7 +261,7 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal mutating func _convertToRegularNode() { assert(isCollisionNode && hasSingletonItem) diff --git a/Sources/HashTreeCollections/Node/_Node+UnsafeHandle.swift b/Sources/HashTreeCollections/HashNode/_HashNode+UnsafeHandle.swift similarity index 85% rename from Sources/HashTreeCollections/Node/_Node+UnsafeHandle.swift rename to Sources/HashTreeCollections/HashNode/_HashNode+UnsafeHandle.swift index 680e0517c..cd99b57b7 100644 --- a/Sources/HashTreeCollections/Node/_Node+UnsafeHandle.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+UnsafeHandle.swift @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -extension _Node { +extension _HashNode { /// An unsafe view of the data stored inside a node in the hash tree, hiding /// the mechanics of accessing storage from the code that uses it. /// @@ -27,7 +27,7 @@ extension _Node { internal typealias Element = (key: Key, value: Value) @usableFromInline - internal let _header: UnsafeMutablePointer<_StorageHeader> + internal let _header: UnsafeMutablePointer<_HashNodeHeader> @usableFromInline internal let _memory: UnsafeMutableRawPointer @@ -39,7 +39,7 @@ extension _Node { @inlinable internal init( - _ header: UnsafeMutablePointer<_StorageHeader>, + _ header: UnsafeMutablePointer<_HashNodeHeader>, _ memory: UnsafeMutableRawPointer, isMutable: Bool ) { @@ -52,7 +52,7 @@ extension _Node { } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable @inline(__always) func assertMutable() { @@ -62,10 +62,10 @@ extension _Node.UnsafeHandle { } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable @inline(__always) static func read( - _ node: _UnmanagedNode, + _ node: _UnmanagedHashNode, _ body: (Self) throws -> R ) rethrows -> R { try node.ref._withUnsafeGuaranteedRef { storage in @@ -77,7 +77,7 @@ extension _Node.UnsafeHandle { @inlinable @inline(__always) static func read( - _ storage: _RawStorage, + _ storage: _RawHashStorage, _ body: (Self) throws -> R ) rethrows -> R { try storage.withUnsafeMutablePointers { header, elements in @@ -87,7 +87,7 @@ extension _Node.UnsafeHandle { @inlinable @inline(__always) static func update( - _ node: _UnmanagedNode, + _ node: _UnmanagedHashNode, _ body: (Self) throws -> R ) rethrows -> R { try node.ref._withUnsafeGuaranteedRef { storage in @@ -99,7 +99,7 @@ extension _Node.UnsafeHandle { @inlinable @inline(__always) static func update( - _ storage: _RawStorage, + _ storage: _RawHashStorage, _ body: (Self) throws -> R ) rethrows -> R { try storage.withUnsafeMutablePointers { header, elements in @@ -108,7 +108,7 @@ extension _Node.UnsafeHandle { } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable @inline(__always) internal var itemMap: _Bitmap { get { @@ -173,8 +173,8 @@ extension _Node.UnsafeHandle { } @inlinable @inline(__always) - internal var _childrenStart: UnsafeMutablePointer<_Node> { - _memory.assumingMemoryBound(to: _Node.self) + internal var _childrenStart: UnsafeMutablePointer<_HashNode> { + _memory.assumingMemoryBound(to: _HashNode.self) } @inlinable @inline(__always) @@ -188,29 +188,29 @@ extension _Node.UnsafeHandle { } @inlinable - internal func childBucket(at slot: _Slot) -> _Bucket { + internal func childBucket(at slot: _HashSlot) -> _Bucket { guard !isCollisionNode else { return .invalid } return childMap.bucket(at: slot) } @inlinable @inline(__always) - internal var childrenEndSlot: _Slot { + internal var childrenEndSlot: _HashSlot { _header.pointee.childrenEndSlot } @inlinable - internal var children: UnsafeMutableBufferPointer<_Node> { + internal var children: UnsafeMutableBufferPointer<_HashNode> { UnsafeMutableBufferPointer(start: _childrenStart, count: childCount) } @inlinable - internal func childPtr(at slot: _Slot) -> UnsafeMutablePointer<_Node> { + internal func childPtr(at slot: _HashSlot) -> UnsafeMutablePointer<_HashNode> { assert(slot.value < childCount) return _childrenStart + slot.value } @inlinable - internal subscript(child slot: _Slot) -> _Node { + internal subscript(child slot: _HashSlot) -> _HashNode { unsafeAddress { UnsafePointer(childPtr(at: slot)) } @@ -237,13 +237,13 @@ extension _Node.UnsafeHandle { } @inlinable - internal func itemBucket(at slot: _Slot) -> _Bucket { + internal func itemBucket(at slot: _HashSlot) -> _Bucket { guard !isCollisionNode else { return .invalid } return itemMap.bucket(at: slot) } @inlinable @inline(__always) - internal var itemsEndSlot: _Slot { + internal var itemsEndSlot: _HashSlot { _header.pointee.itemsEndSlot } @@ -254,13 +254,13 @@ extension _Node.UnsafeHandle { } @inlinable - internal func itemPtr(at slot: _Slot) -> UnsafeMutablePointer { + internal func itemPtr(at slot: _HashSlot) -> UnsafeMutablePointer { assert(slot.value <= itemCount) return _itemsEnd.advanced(by: -1 &- slot.value) } @inlinable - internal subscript(item slot: _Slot) -> Element { + internal subscript(item slot: _HashSlot) -> Element { unsafeAddress { UnsafePointer(itemPtr(at: slot)) } @@ -277,7 +277,7 @@ extension _Node.UnsafeHandle { } } -extension _Node.UnsafeHandle { +extension _HashNode.UnsafeHandle { @inlinable internal var hasSingletonItem: Bool { _header.pointee.hasSingletonItem diff --git a/Sources/HashTreeCollections/Node/_Node.swift b/Sources/HashTreeCollections/HashNode/_HashNode.swift similarity index 84% rename from Sources/HashTreeCollections/Node/_Node.swift rename to Sources/HashTreeCollections/HashNode/_HashNode.swift index 9a0a3c17e..e9e35e2f8 100644 --- a/Sources/HashTreeCollections/Node/_Node.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode.swift @@ -36,31 +36,31 @@ /// items map. @usableFromInline @frozen -internal struct _Node { - // Warning: This struct must be kept layout-compatible with _RawNode. +internal struct _HashNode { + // Warning: This struct must be kept layout-compatible with _RawHashNode. // Do not add any new stored properties to this type. // // Swift guarantees that frozen structs with a single stored property will // be layout-compatible with the type they are wrapping. // - // (_RawNode is used as the Element type of the ManagedBuffer underlying - // node storage, and the memory is then rebound to `_Node` later. - // This will not work correctly unless `_Node` has the exact same alignment + // (_RawHashNode is used as the Element type of the ManagedBuffer underlying + // node storage, and the memory is then rebound to `_HashNode` later. + // This will not work correctly unless `_HashNode` has the exact same alignment // and stride as `RawNode`.) @usableFromInline internal typealias Element = (key: Key, value: Value) @usableFromInline - internal var raw: _RawNode + internal var raw: _RawHashNode @inlinable - internal init(storage: _RawStorage, count: Int) { - self.raw = _RawNode(storage: storage, count: count) + internal init(storage: _RawHashStorage, count: Int) { + self.raw = _RawHashNode(storage: storage, count: count) } } -extension _Node { +extension _HashNode { @inlinable @inline(__always) internal var count: Int { get { raw.count } @@ -68,19 +68,19 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable @inline(__always) - internal var unmanaged: _UnmanagedNode { - _UnmanagedNode(raw.storage) + internal var unmanaged: _UnmanagedHashNode { + _UnmanagedHashNode(raw.storage) } @inlinable @inline(__always) - internal func isIdentical(to other: _UnmanagedNode) -> Bool { + internal func isIdentical(to other: _UnmanagedHashNode) -> Bool { raw.isIdentical(to: other) } } -extension _Node { +extension _HashNode { @inlinable @inline(__always) internal func read( _ body: (UnsafeHandle) throws -> R @@ -98,7 +98,7 @@ extension _Node { // MARK: Shortcuts to reading header data -extension _Node { +extension _HashNode { @inlinable internal var isCollisionNode: Bool { read { $0.isCollisionNode } @@ -125,7 +125,7 @@ extension _Node { } } -extension _Node { +extension _HashNode { @inlinable internal var initialVersionNumber: UInt { // Ideally we would simply just generate a true random number, but the diff --git a/Sources/HashTreeCollections/Node/_Slot.swift b/Sources/HashTreeCollections/HashNode/_HashSlot.swift similarity index 75% rename from Sources/HashTreeCollections/Node/_Slot.swift rename to Sources/HashTreeCollections/HashNode/_HashSlot.swift index 788e43692..7f9d64dda 100644 --- a/Sources/HashTreeCollections/Node/_Slot.swift +++ b/Sources/HashTreeCollections/HashNode/_HashSlot.swift @@ -11,14 +11,14 @@ /// Identifies a position within a contiguous storage region within a hash tree /// node. Hash tree nodes have two storage regions, one for items and one for -/// children; the same `_Slot` type is used to refer to positions within both. +/// children; the same `_HashSlot` type is used to refer to positions within both. /// /// We use the term "slot" to refer to internal storage entries, to avoid /// confusion with terms that sometimes appear in public API, such as /// "index", "position" or "offset". @usableFromInline @frozen -internal struct _Slot { +internal struct _HashSlot { @usableFromInline internal var _value: UInt32 @@ -40,55 +40,55 @@ internal struct _Slot { } } -extension _Slot { +extension _HashSlot { @inlinable @inline(__always) - internal static var zero: _Slot { _Slot(0) } + internal static var zero: _HashSlot { _HashSlot(0) } } -extension _Slot { +extension _HashSlot { @inlinable @inline(__always) internal var value: Int { Int(truncatingIfNeeded: _value) } } -extension _Slot: Equatable { +extension _HashSlot: Equatable { @inlinable @inline(__always) internal static func ==(left: Self, right: Self) -> Bool { left._value == right._value } } -extension _Slot: Comparable { +extension _HashSlot: Comparable { @inlinable @inline(__always) internal static func <(left: Self, right: Self) -> Bool { left._value < right._value } } -extension _Slot: Hashable { +extension _HashSlot: Hashable { @inlinable internal func hash(into hasher: inout Hasher) { hasher.combine(_value) } } -extension _Slot: CustomStringConvertible { +extension _HashSlot: CustomStringConvertible { @usableFromInline internal var description: String { "\(_value)" } } -extension _Slot: Strideable { +extension _HashSlot: Strideable { @inlinable @inline(__always) - internal func advanced(by n: Int) -> _Slot { + internal func advanced(by n: Int) -> _HashSlot { assert(n >= 0 || value + n >= 0) - return _Slot(_value &+ UInt32(truncatingIfNeeded: n)) + return _HashSlot(_value &+ UInt32(truncatingIfNeeded: n)) } @inlinable @inline(__always) - internal func distance(to other: _Slot) -> Int { + internal func distance(to other: _HashSlot) -> Int { if self < other { return Int(truncatingIfNeeded: other._value - self._value) } @@ -96,16 +96,16 @@ extension _Slot: Strideable { } } -extension _Slot { +extension _HashSlot { @inlinable @inline(__always) - internal func next() -> _Slot { + internal func next() -> _HashSlot { assert(_value < .max) - return _Slot(_value &+ 1) + return _HashSlot(_value &+ 1) } @inlinable @inline(__always) - internal func previous() -> _Slot { + internal func previous() -> _HashSlot { assert(_value > 0) - return _Slot(_value &- 1) + return _HashSlot(_value &- 1) } } diff --git a/Sources/HashTreeCollections/Node/_Stack.swift b/Sources/HashTreeCollections/HashNode/_HashStack.swift similarity index 97% rename from Sources/HashTreeCollections/Node/_Stack.swift rename to Sources/HashTreeCollections/HashNode/_HashStack.swift index bbf9c9623..8d69b44e5 100644 --- a/Sources/HashTreeCollections/Node/_Stack.swift +++ b/Sources/HashTreeCollections/HashNode/_HashStack.swift @@ -13,7 +13,7 @@ /// `TreeDictionary`. @usableFromInline @frozen -internal struct _Stack { +internal struct _HashStack { #if arch(x86_64) || arch(arm64) @inlinable @inline(__always) @@ -45,7 +45,7 @@ internal struct _Stack { @inlinable internal init(filledWith value: Element) { - assert(Self.capacity == _Level.limit) + assert(Self.capacity == _HashLevel.limit) #if arch(x86_64) || arch(arm64) _contents = ( value, value, value, value, diff --git a/Sources/HashTreeCollections/Node/_HashTreeIterator.swift b/Sources/HashTreeCollections/HashNode/_HashTreeIterator.swift similarity index 77% rename from Sources/HashTreeCollections/Node/_HashTreeIterator.swift rename to Sources/HashTreeCollections/HashNode/_HashTreeIterator.swift index 402e4b127..29adc235f 100644 --- a/Sources/HashTreeCollections/Node/_HashTreeIterator.swift +++ b/Sources/HashTreeCollections/HashNode/_HashTreeIterator.swift @@ -14,39 +14,39 @@ internal struct _HashTreeIterator { @usableFromInline internal struct _Opaque { - internal var ancestorSlots: _AncestorSlots - internal var ancestorNodes: _Stack<_UnmanagedNode> - internal var level: _Level + internal var ancestorSlots: _AncestorHashSlots + internal var ancestorNodes: _HashStack<_UnmanagedHashNode> + internal var level: _HashLevel internal var isAtEnd: Bool @usableFromInline @_effects(releasenone) - internal init(_ root: _UnmanagedNode) { + internal init(_ root: _UnmanagedHashNode) { self.ancestorSlots = .empty - self.ancestorNodes = _Stack(filledWith: root) + self.ancestorNodes = _HashStack(filledWith: root) self.level = .top self.isAtEnd = false } } @usableFromInline - internal let root: _RawStorage + internal let root: _RawHashStorage @usableFromInline - internal var node: _UnmanagedNode + internal var node: _UnmanagedHashNode @usableFromInline - internal var slot: _Slot + internal var slot: _HashSlot @usableFromInline - internal var endSlot: _Slot + internal var endSlot: _HashSlot @usableFromInline internal var _o: _Opaque @usableFromInline @_effects(releasenone) - internal init(root: __shared _RawNode) { + internal init(root: __shared _RawHashNode) { self.root = root.storage self.node = root.unmanaged self.slot = .zero @@ -64,7 +64,8 @@ internal struct _HashTreeIterator { extension _HashTreeIterator: IteratorProtocol { @inlinable - internal mutating func next() -> (node: _UnmanagedNode, slot: _Slot)? { + internal mutating func next( + ) -> (node: _UnmanagedHashNode, slot: _HashSlot)? { guard slot < endSlot else { return _next() } @@ -74,7 +75,8 @@ extension _HashTreeIterator: IteratorProtocol { @usableFromInline @_effects(releasenone) - internal mutating func _next() -> (node: _UnmanagedNode, slot: _Slot)? { + internal mutating func _next( + ) -> (node: _UnmanagedHashNode, slot: _HashSlot)? { if _o.isAtEnd { return nil } if node.hasChildren { _descendToLeftmostItem(ofChildAtSlot: .zero) @@ -98,7 +100,7 @@ extension _HashTreeIterator: IteratorProtocol { } extension _HashTreeIterator { - internal mutating func _descend(toChildSlot childSlot: _Slot) { + internal mutating func _descend(toChildSlot childSlot: _HashSlot) { assert(childSlot < node.childrenEndSlot) _o.ancestorSlots[_o.level] = childSlot _o.ancestorNodes.push(node) @@ -108,7 +110,7 @@ extension _HashTreeIterator { endSlot = node.itemsEndSlot } - internal mutating func _ascend() -> _Slot { + internal mutating func _ascend() -> _HashSlot { assert(!_o.level.isAtRoot) node = _o.ancestorNodes.pop() _o.level = _o.level.ascend() @@ -118,7 +120,7 @@ extension _HashTreeIterator { } internal mutating func _descendToLeftmostItem( - ofChildAtSlot childSlot: _Slot + ofChildAtSlot childSlot: _HashSlot ) { _descend(toChildSlot: childSlot) while endSlot == .zero { diff --git a/Sources/HashTreeCollections/Node/_HashTreeStatistics.swift b/Sources/HashTreeCollections/HashNode/_HashTreeStatistics.swift similarity index 95% rename from Sources/HashTreeCollections/Node/_HashTreeStatistics.swift rename to Sources/HashTreeCollections/HashNode/_HashTreeStatistics.swift index 1161414db..9c5003c41 100644 --- a/Sources/HashTreeCollections/Node/_HashTreeStatistics.swift +++ b/Sources/HashTreeCollections/HashNode/_HashTreeStatistics.swift @@ -86,9 +86,9 @@ public struct _HashTreeStatistics { } -extension _Node { +extension _HashNode { internal func gatherStatistics( - _ level: _Level, _ stats: inout _HashTreeStatistics + _ level: _HashLevel, _ stats: inout _HashTreeStatistics ) { // The empty singleton does not count as a node and occupies no space. if self.raw.storage === _emptySingleton { return } @@ -111,7 +111,7 @@ extension _Node { stats.capacityBytes += $0.byteCapacity stats.freeBytes += $0.bytesFree stats.itemBytes += $0.itemCount * (keyStride + valueStride) - stats.childBytes += $0.childCount * MemoryLayout<_RawNode>.stride + stats.childBytes += $0.childCount * MemoryLayout<_RawHashNode>.stride let objectHeaderSize = 2 * MemoryLayout.stride @@ -119,7 +119,7 @@ extension _Node { // the object header and the storage header. let start = _getUnsafePointerToStoredProperties(self.raw.storage) let capacity = self.raw.storage.capacity - let end = $0._memory + capacity * MemoryLayout<_RawNode>.stride + let end = $0._memory + capacity * MemoryLayout<_RawHashNode>.stride stats.grossBytes += objectHeaderSize + (end - start) for child in $0.children { diff --git a/Sources/HashTreeCollections/Node/_RawNode+UnsafeHandle.swift b/Sources/HashTreeCollections/HashNode/_RawHashNode+UnsafeHandle.swift similarity index 79% rename from Sources/HashTreeCollections/Node/_RawNode+UnsafeHandle.swift rename to Sources/HashTreeCollections/HashNode/_RawHashNode+UnsafeHandle.swift index f7ea85577..cd92cc37e 100644 --- a/Sources/HashTreeCollections/Node/_RawNode+UnsafeHandle.swift +++ b/Sources/HashTreeCollections/HashNode/_RawHashNode+UnsafeHandle.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -extension _RawNode { +extension _RawHashNode { /// An unsafe, non-generic view of the data stored inside a node in the /// hash tree, hiding the mechanics of accessing storage from the code that /// uses it. /// - /// This is the non-generic equivalent of `_Node.UnsafeHandle`, sharing some + /// This is the non-generic equivalent of `_HashNode.UnsafeHandle`, sharing some /// of its functionality, but it only provides read-only access to the tree /// structure (incl. subtree counts) -- it doesn't provide any ways to mutate /// the underlying data or to access user payload. @@ -26,14 +26,14 @@ extension _RawNode { @frozen internal struct UnsafeHandle { @usableFromInline - internal let _header: UnsafePointer<_StorageHeader> + internal let _header: UnsafePointer<_HashNodeHeader> @usableFromInline internal let _memory: UnsafeRawPointer @inlinable internal init( - _ header: UnsafePointer<_StorageHeader>, + _ header: UnsafePointer<_HashNodeHeader>, _ memory: UnsafeRawPointer ) { self._header = header @@ -42,10 +42,10 @@ extension _RawNode { } } -extension _RawNode.UnsafeHandle { +extension _RawHashNode.UnsafeHandle { @inlinable @inline(__always) static func read( - _ node: _UnmanagedNode, + _ node: _UnmanagedHashNode, _ body: (Self) throws -> R ) rethrows -> R { try node.ref._withUnsafeGuaranteedRef { storage in @@ -56,7 +56,7 @@ extension _RawNode.UnsafeHandle { } } -extension _RawNode.UnsafeHandle { +extension _RawHashNode.UnsafeHandle { @inline(__always) internal var isCollisionNode: Bool { _header.pointee.isCollisionNode @@ -79,7 +79,7 @@ extension _RawNode.UnsafeHandle { } @inline(__always) - internal var childrenEndSlot: _Slot { + internal var childrenEndSlot: _HashSlot { _header.pointee.childrenEndSlot } @@ -94,23 +94,23 @@ extension _RawNode.UnsafeHandle { } @inline(__always) - internal var itemsEndSlot: _Slot { + internal var itemsEndSlot: _HashSlot { _header.pointee.itemsEndSlot } @inline(__always) - internal var _childrenStart: UnsafePointer<_RawNode> { - _memory.assumingMemoryBound(to: _RawNode.self) + internal var _childrenStart: UnsafePointer<_RawHashNode> { + _memory.assumingMemoryBound(to: _RawHashNode.self) } - internal subscript(child slot: _Slot) -> _RawNode { + internal subscript(child slot: _HashSlot) -> _RawHashNode { unsafeAddress { assert(slot < childrenEndSlot) return _childrenStart + slot.value } } - internal var children: UnsafeBufferPointer<_RawNode> { + internal var children: UnsafeBufferPointer<_RawHashNode> { UnsafeBufferPointer(start: _childrenStart, count: childCount) } } diff --git a/Sources/HashTreeCollections/Node/_RawNode.swift b/Sources/HashTreeCollections/HashNode/_RawHashNode.swift similarity index 82% rename from Sources/HashTreeCollections/Node/_RawNode.swift rename to Sources/HashTreeCollections/HashNode/_RawHashNode.swift index fe06e03c5..56ff0b594 100644 --- a/Sources/HashTreeCollections/Node/_RawNode.swift +++ b/Sources/HashTreeCollections/HashNode/_RawHashNode.swift @@ -17,21 +17,21 @@ /// `distance(from:to:)`, `index(_:offsetBy:)` in non-generic code. @usableFromInline @frozen -internal struct _RawNode { +internal struct _RawHashNode { @usableFromInline - internal var storage: _RawStorage + internal var storage: _RawHashStorage @usableFromInline internal var count: Int @inlinable - internal init(storage: _RawStorage, count: Int) { + internal init(storage: _RawHashStorage, count: Int) { self.storage = storage self.count = count } } -extension _RawNode { +extension _RawHashNode { @inline(__always) internal func read(_ body: (UnsafeHandle) -> R) -> R { storage.withUnsafeMutablePointers { header, elements in @@ -40,22 +40,22 @@ extension _RawNode { } } -extension _RawNode { +extension _RawHashNode { @inlinable @inline(__always) - internal var unmanaged: _UnmanagedNode { - _UnmanagedNode(storage) + internal var unmanaged: _UnmanagedHashNode { + _UnmanagedHashNode(storage) } @inlinable @inline(__always) - internal func isIdentical(to other: _UnmanagedNode) -> Bool { + internal func isIdentical(to other: _UnmanagedHashNode) -> Bool { other.ref.toOpaque() == Unmanaged.passUnretained(storage).toOpaque() } } -extension _RawNode { +extension _RawHashNode { @usableFromInline internal func validatePath(_ path: _UnsafePath) { - var l = _Level.top + var l = _HashLevel.top var n = self.unmanaged while l < path.level { let slot = path.ancestors[l] diff --git a/Sources/HashTreeCollections/Node/_StorageHeader.swift b/Sources/HashTreeCollections/HashNode/_StorageHeader.swift similarity index 92% rename from Sources/HashTreeCollections/Node/_StorageHeader.swift rename to Sources/HashTreeCollections/HashNode/_StorageHeader.swift index 3684b4a9b..4b04ed098 100644 --- a/Sources/HashTreeCollections/Node/_StorageHeader.swift +++ b/Sources/HashTreeCollections/HashNode/_StorageHeader.swift @@ -14,7 +14,7 @@ /// information about the currently occupied hash table buckets. @usableFromInline @frozen -internal struct _StorageHeader { +internal struct _HashNodeHeader { @usableFromInline internal var itemMap: _Bitmap @@ -37,7 +37,7 @@ internal struct _StorageHeader { } } -extension _StorageHeader { +extension _HashNodeHeader { @inlinable @inline(__always) internal var byteCapacity: Int { get { Int(truncatingIfNeeded: _byteCapacity) } @@ -54,7 +54,7 @@ extension _StorageHeader { } } -extension _StorageHeader { +extension _HashNodeHeader { @inlinable @inline(__always) internal var isEmpty: Bool { return itemMap.isEmpty && childMap.isEmpty @@ -101,13 +101,13 @@ extension _StorageHeader { } @inlinable @inline(__always) - internal var childrenEndSlot: _Slot { - _Slot(childCount) + internal var childrenEndSlot: _HashSlot { + _HashSlot(childCount) } @inlinable @inline(__always) - internal var itemsEndSlot: _Slot { - _Slot(itemCount) + internal var itemsEndSlot: _HashSlot { + _HashSlot(itemCount) } @inlinable @@ -125,7 +125,7 @@ extension _StorageHeader { } } -extension _StorageHeader { +extension _HashNodeHeader { @inlinable internal mutating func clear() { itemMap = .empty diff --git a/Sources/HashTreeCollections/Node/_UnmanagedNode.swift b/Sources/HashTreeCollections/HashNode/_UnmanagedHashNode.swift similarity index 69% rename from Sources/HashTreeCollections/Node/_UnmanagedNode.swift rename to Sources/HashTreeCollections/HashNode/_UnmanagedHashNode.swift index 724efca82..31d223460 100644 --- a/Sources/HashTreeCollections/Node/_UnmanagedNode.swift +++ b/Sources/HashTreeCollections/HashNode/_UnmanagedHashNode.swift @@ -9,27 +9,29 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// An unsafe, unowned, type-erased reference to a hash tree node; essentially -/// just a lightweight wrapper around `Unmanaged<_RawStorage>`. +/// just a lightweight wrapper around `Unmanaged<_RawHashStorage>`. /// /// Because such a reference may outlive the underlying object, use sites must -/// be extraordinarily careful to never dereference an invalid `_UnmanagedNode`. -/// Doing so results in undefined behavior. +/// be extraordinarily careful to never dereference an invalid +/// `_UnmanagedHashNode`. Doing so results in undefined behavior. @usableFromInline @frozen -internal struct _UnmanagedNode { +internal struct _UnmanagedHashNode { @usableFromInline - internal var ref: Unmanaged<_RawStorage> + internal var ref: Unmanaged<_RawHashStorage> @inlinable @inline(__always) - internal init(_ storage: _RawStorage) { + internal init(_ storage: _RawHashStorage) { self.ref = .passUnretained(storage) } } -extension _UnmanagedNode: Equatable { +extension _UnmanagedHashNode: Equatable { /// Indicates whether two unmanaged node references are equal. /// /// This function is safe to call even if one or both of its arguments are @@ -42,24 +44,24 @@ extension _UnmanagedNode: Equatable { } } -extension _UnmanagedNode: CustomStringConvertible { +extension _UnmanagedHashNode: CustomStringConvertible { @usableFromInline internal var description: String { _addressString(for: ref.toOpaque()) } } -extension _UnmanagedNode { +extension _UnmanagedHashNode { @inlinable @inline(__always) - internal func withRaw(_ body: (_RawStorage) -> R) -> R { + internal func withRaw(_ body: (_RawHashStorage) -> R) -> R { ref._withUnsafeGuaranteedRef(body) } @inline(__always) - internal func read(_ body: (_RawNode.UnsafeHandle) -> R) -> R { + internal func read(_ body: (_RawHashNode.UnsafeHandle) -> R) -> R { ref._withUnsafeGuaranteedRef { storage in storage.withUnsafeMutablePointers { header, elements in - body(_RawNode.UnsafeHandle(header, UnsafeRawPointer(elements))) + body(_RawHashNode.UnsafeHandle(header, UnsafeRawPointer(elements))) } } } @@ -85,17 +87,17 @@ extension _UnmanagedNode { } @inlinable - internal var itemsEndSlot: _Slot { - withRaw { _Slot($0.header.itemCount) } + internal var itemsEndSlot: _HashSlot { + withRaw { _HashSlot($0.header.itemCount) } } @inlinable - internal var childrenEndSlot: _Slot { - withRaw { _Slot($0.header.childCount) } + internal var childrenEndSlot: _HashSlot { + withRaw { _HashSlot($0.header.childCount) } } @inlinable - internal func unmanagedChild(at slot: _Slot) -> Self { + internal func unmanagedChild(at slot: _HashSlot) -> Self { withRaw { raw in assert(slot.value < raw.header.childCount) return raw.withUnsafeMutablePointerToElements { p in diff --git a/Sources/HashTreeCollections/Node/_UnsafePath.swift b/Sources/HashTreeCollections/HashNode/_UnsafePath.swift similarity index 92% rename from Sources/HashTreeCollections/Node/_UnsafePath.swift rename to Sources/HashTreeCollections/HashNode/_UnsafePath.swift index 58170d575..02cff8209 100644 --- a/Sources/HashTreeCollections/Node/_UnsafePath.swift +++ b/Sources/HashTreeCollections/HashNode/_UnsafePath.swift @@ -24,7 +24,7 @@ /// including the final node on the path. /// /// However, to speed up common operations, path values also include a single -/// `_UnmanagedNode` reference to their final node. This reference does not +/// `_UnmanagedHashNode` reference to their final node. This reference does not /// keep the targeted node alive -- it is the use site's responsibility to /// ensure that the path is still valid before calling most of its operations. /// @@ -37,22 +37,22 @@ @frozen internal struct _UnsafePath { @usableFromInline - internal var ancestors: _AncestorSlots + internal var ancestors: _AncestorHashSlots @usableFromInline - internal var node: _UnmanagedNode + internal var node: _UnmanagedHashNode @usableFromInline - internal var nodeSlot: _Slot + internal var nodeSlot: _HashSlot @usableFromInline - internal var level: _Level + internal var level: _HashLevel @usableFromInline internal var _isItem: Bool @inlinable - internal init(root: __shared _RawNode) { + internal init(root: __shared _RawHashNode) { self.level = .top self.ancestors = .empty self.node = root.unmanaged @@ -63,10 +63,10 @@ internal struct _UnsafePath { extension _UnsafePath { internal init( - _ level: _Level, - _ ancestors: _AncestorSlots, - _ node: _UnmanagedNode, - childSlot: _Slot + _ level: _HashLevel, + _ ancestors: _AncestorHashSlots, + _ node: _UnmanagedHashNode, + childSlot: _HashSlot ) { assert(childSlot < node.childrenEndSlot) self.level = level @@ -78,10 +78,10 @@ extension _UnsafePath { @inlinable internal init( - _ level: _Level, - _ ancestors: _AncestorSlots, - _ node: _UnmanagedNode, - itemSlot: _Slot + _ level: _HashLevel, + _ ancestors: _AncestorHashSlots, + _ node: _UnmanagedHashNode, + itemSlot: _HashSlot ) { assert(itemSlot < node.itemsEndSlot) self.level = level @@ -126,7 +126,7 @@ extension _UnsafePath: Comparable { // Paths addressing items within a node are ordered before paths addressing // a child node within the same node. - var level: _Level = .top + var level: _HashLevel = .top while level < left.level, level < right.level { let l = left.ancestors[level] let r = right.ancestors[level] @@ -156,7 +156,7 @@ extension _UnsafePath: CustomStringConvertible { @usableFromInline internal var description: String { var d = "@" - var l: _Level = .top + var l: _HashLevel = .top while l < self.level { d += ".\(self.ancestors[l])" l = l.descend() @@ -226,7 +226,7 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. @inlinable - internal var currentChild: _UnmanagedNode { + internal var currentChild: _UnmanagedHashNode { assert(isOnChild) return node.unmanagedChild(at: nodeSlot) } @@ -236,7 +236,7 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. @inlinable - internal func childSlot(at level: _Level) -> _Slot { + internal func childSlot(at level: _HashLevel) -> _HashSlot { assert(level < self.level) return ancestors[level] } @@ -245,7 +245,7 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. @inlinable @inline(__always) - internal var currentItemSlot: _Slot { + internal var currentItemSlot: _HashSlot { assert(isOnItem) return nodeSlot } @@ -258,7 +258,7 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. @inlinable - internal mutating func selectItem(at slot: _Slot) { + internal mutating func selectItem(at slot: _HashSlot) { // As a special exception, this allows slot to equal the item count. // This can happen for paths that address the position a new item might be // inserted later. @@ -273,7 +273,7 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. @inlinable - internal mutating func selectChild(at slot: _Slot) { + internal mutating func selectChild(at slot: _HashSlot) { // As a special exception, this allows slot to equal the child count. // This is equivalent to a call to `selectEnd()`. assert(slot <= node.childrenEndSlot) @@ -317,7 +317,7 @@ extension _UnsafePath { /// valid. @inlinable internal mutating func descendToChild( - _ child: _UnmanagedNode, at slot: _Slot + _ child: _UnmanagedHashNode, at slot: _HashSlot ) { assert(slot < node.childrenEndSlot) assert(child == node.unmanagedChild(at: slot)) @@ -328,7 +328,9 @@ extension _UnsafePath { self.level = level.descend() } - internal mutating func ascend(to ancestor: _UnmanagedNode, at level: _Level) { + internal mutating func ascend( + to ancestor: _UnmanagedHashNode, at level: _HashLevel + ) { guard level != self.level else { return } assert(level < self.level) self.level = level @@ -347,13 +349,13 @@ extension _UnsafePath { /// - Note: It is undefined behavior to call this on a path that is no longer /// valid. internal mutating func ascendToNearestAncestor( - under root: _RawNode, - where test: (_UnmanagedNode, _Slot) -> Bool + under root: _RawHashNode, + where test: (_UnmanagedHashNode, _HashSlot) -> Bool ) -> Bool { if self.level.isAtRoot { return false } var best: _UnsafePath? = nil var n = root.unmanaged - var l: _Level = .top + var l: _HashLevel = .top while l < self.level { let slot = self.ancestors[l] if test(n, slot) { @@ -442,7 +444,7 @@ extension _UnsafePath { /// path does not currently address an item. @usableFromInline @_effects(releasenone) - internal mutating func findSuccessorItem(under root: _RawNode) -> Bool { + internal mutating func findSuccessorItem(under root: _RawHashNode) -> Bool { guard isOnItem else { return false } if selectNextItem() { return true } if node.hasChildren { @@ -469,7 +471,7 @@ extension _UnsafePath { /// Return false if there is no previous item. @usableFromInline @_effects(releasenone) - internal mutating func findPredecessorItem(under root: _RawNode) -> Bool { + internal mutating func findPredecessorItem(under root: _RawHashNode) -> Bool { switch (isOnItem, nodeSlot > .zero) { case (true, true): selectItem(at: nodeSlot.previous()) @@ -505,14 +507,14 @@ extension _UnsafePath { } } -extension _RawNode { +extension _RawHashNode { /// Return the integer position of the item addressed by the given path /// within a preorder walk of the tree. If the path addresses the end /// position, then return the number of items in the tree. /// /// This method must only be called on the root node. internal func preorderPosition( - _ level: _Level, of path: _UnsafePath + _ level: _HashLevel, of path: _UnsafePath ) -> Int { if path.isOnNodeEnd { return count } assert(path.isOnItem) @@ -551,7 +553,7 @@ extension _UnsafePath { while !stop { let itemCount = node.itemCount if remaining < itemCount { - selectItem(at: _Slot(remaining)) + selectItem(at: _HashSlot(remaining)) return (true, 0) } remaining -= itemCount @@ -560,7 +562,7 @@ extension _UnsafePath { for i in children.indices { let c = children[i].count if remaining < c { - descendToChild(children[i].unmanaged, at: _Slot(i)) + descendToChild(children[i].unmanaged, at: _HashSlot(i)) return } remaining &-= c @@ -574,7 +576,7 @@ extension _UnsafePath { } } -extension _RawNode { +extension _RawHashNode { /// Return the number of steps between two paths within a preorder walk of the /// tree. The two paths must not address a child node. /// @@ -582,7 +584,7 @@ extension _RawNode { @usableFromInline @_effects(releasenone) internal func distance( - _ level: _Level, from start: _UnsafePath, to end: _UnsafePath + _ level: _HashLevel, from start: _UnsafePath, to end: _UnsafePath ) -> Int { assert(level.isAtRoot) if start.isOnNodeEnd { @@ -623,7 +625,7 @@ extension _RawNode { } internal func _distance( - _ level: _Level, from start: _UnsafePath, to end: _UnsafePath + _ level: _HashLevel, from start: _UnsafePath, to end: _UnsafePath ) -> Int { assert(start < end) assert(level < start.level) @@ -648,9 +650,9 @@ extension _RawNode { } } -extension _UnmanagedNode { +extension _UnmanagedHashNode { internal func _distance( - _ level: _Level, fromItemAt start: _Slot, to end: _UnsafePath + _ level: _HashLevel, fromItemAt start: _HashSlot, to end: _UnsafePath ) -> Int { read { assert(start < $0.itemsEndSlot) @@ -666,7 +668,7 @@ extension _UnmanagedNode { } } -extension _Node { +extension _HashNode { /// Return the path to the given key in this tree if it exists; otherwise /// return nil. @inlinable @@ -674,8 +676,8 @@ extension _Node { to key: Key, _ hash: _Hash ) -> _UnsafePath? { var node = unmanaged - var level: _Level = .top - var ancestors: _AncestorSlots = .empty + var level: _HashLevel = .top + var ancestors: _AncestorHashSlots = .empty while true { let r = UnsafeHandle.read(node) { $0.find(level, key, hash) } guard let r = r else { break } @@ -690,11 +692,11 @@ extension _Node { } } -extension _RawNode { +extension _RawHashNode { @usableFromInline @_effects(releasenone) internal func seek( - _ level: _Level, + _ level: _HashLevel, _ path: inout _UnsafePath, offsetBy distance: Int, limitedBy limit: _UnsafePath @@ -718,7 +720,7 @@ extension _RawNode { @usableFromInline @_effects(releasenone) internal func seek( - _ level: _Level, + _ level: _HashLevel, _ path: inout _UnsafePath, offsetBy distance: Int ) -> Bool { @@ -733,7 +735,7 @@ extension _RawNode { } internal func _seek( - _ level: _Level, + _ level: _HashLevel, _ path: inout _UnsafePath, offsetBy distance: inout Int ) -> Bool { @@ -768,7 +770,7 @@ extension _RawNode { let slot = path.nodeSlot distance &+= slot.value if distance >= 0 { - path.selectItem(at: _Slot(distance)) + path.selectItem(at: _HashSlot(distance)) distance = 0 return true } @@ -782,7 +784,7 @@ extension _RawNode { /// Find the item at the given positive distance from the last item within the /// subtree rooted at the current node in `path`. internal func _seekForward( - _ level: _Level, + _ level: _HashLevel, by distance: inout Int, fromSubtree path: inout _UnsafePath ) -> Bool { @@ -806,7 +808,7 @@ extension _RawNode { while i < children.endIndex { let c = children[i].count if distance < c { - path.descendToChild(children[i].unmanaged, at: _Slot(i)) + path.descendToChild(children[i].unmanaged, at: _HashSlot(i)) let r = path.findItemAtPreorderPosition(distance) precondition(r.found, "Internal inconsistency: invalid node counts") assert(r.remaining == 0) @@ -824,7 +826,7 @@ extension _RawNode { /// Find the item at the given negative distance from the first item within the /// subtree rooted at the current node in `path`. internal func _seekBackward( - _ level: _Level, + _ level: _HashLevel, by distance: inout Int, fromSubtree path: inout _UnsafePath ) -> Bool { @@ -833,7 +835,7 @@ extension _RawNode { return read { let children = $0.children - var slot: _Slot + var slot: _HashSlot if level < path.level { // We need to descend to the end of the path before we can start the // search for real. @@ -875,7 +877,7 @@ extension _RawNode { // See if the target is hiding somwhere in our immediate items. distance &+= $0.itemCount if distance >= 0 { - path.selectItem(at: _Slot(distance)) + path.selectItem(at: _HashSlot(distance)) distance = 0 return true } diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Codable.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Codable.swift index 7a9d42464..c929e01a7 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Codable.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Codable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 @@ -60,7 +60,6 @@ where Key: Encodable, Value: Encodable } return } -#if swift(>=5.6) if #available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *), Key.self is CodingKeyRepresentable.Type { // Since the keys are CodingKeyRepresentable, we can use the `codingKey` @@ -73,7 +72,6 @@ where Key: Encodable, Value: Encodable } return } -#endif // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily // convert to keys. We can encode as an array of alternating key-value // pairs, though. @@ -133,7 +131,6 @@ where Key: Decodable, Value: Decodable return } -#if swift(>=5.6) if #available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *), let keyType = Key.self as? CodingKeyRepresentable.Type { // The keys are CodingKeyRepresentable, so we should be able to expect @@ -152,7 +149,6 @@ where Key: Decodable, Value: Decodable } return } -#endif // We should have encoded as an array of alternating key-value pairs. var container = try decoder.unkeyedContainer() diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Collection.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Collection.swift index e05279f2e..a1df0ed00 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Collection.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Collection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 @@ -19,7 +19,7 @@ extension TreeDictionary { @frozen public struct Index { @usableFromInline - internal let _root: _UnmanagedNode + internal let _root: _UnmanagedHashNode @usableFromInline internal var _version: UInt @@ -29,7 +29,7 @@ extension TreeDictionary { @inlinable @inline(__always) internal init( - _root: _UnmanagedNode, version: UInt, path: _UnsafePath + _root: _UnmanagedHashNode, version: UInt, path: _UnsafePath ) { self._root = _root self._version = version @@ -38,10 +38,8 @@ extension TreeDictionary { } } -#if swift(>=5.5) extension TreeDictionary.Index: @unchecked Sendable where Key: Sendable, Value: Sendable {} -#endif extension TreeDictionary.Index: Equatable { /// Returns a Boolean value indicating whether two index values are equal. diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Debugging.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Debugging.swift index 17a6c52a5..eeb26b006 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Debugging.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Debugging.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeDictionary { /// True if consistency checking is enabled in the implementation of this @@ -32,7 +34,7 @@ extension TreeDictionary { } public static var _maxDepth: Int { - _Level.limit + _HashLevel.limit } public var _statistics: _HashTreeStatistics { diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Descriptions.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Descriptions.swift index e0c1eca62..d0e8f6aa3 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Descriptions.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Descriptions.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeDictionary: CustomStringConvertible { // A textual representation of this instance. diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Keys.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Keys.swift index 8098cbf2a..da2d3a541 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Keys.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Keys.swift @@ -2,21 +2,23 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeDictionary { /// A view of a persistent dictionary’s keys, as a standalone collection. @frozen public struct Keys { @usableFromInline - internal typealias _Node = HashTreeCollections._Node + internal typealias _Node = _HashNode @usableFromInline internal var _base: TreeDictionary @@ -36,10 +38,8 @@ extension TreeDictionary { } } -#if swift(>=5.5) extension TreeDictionary.Keys: Sendable where Key: Sendable, Value: Sendable {} -#endif extension TreeDictionary.Keys: _UniqueCollection {} @@ -112,10 +112,8 @@ extension TreeDictionary.Keys: Sequence { } } -#if swift(>=5.5) extension TreeDictionary.Keys.Iterator: Sendable where Key: Sendable, Value: Sendable {} -#endif extension TreeDictionary.Keys: Collection { public typealias Index = TreeDictionary.Index diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sendable.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sendable.swift index 01bdf1159..f347d3c63 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sendable.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sendable.swift @@ -2,14 +2,12 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 swift(>=5.5) extension TreeDictionary: @unchecked Sendable where Key: Sendable, Value: Sendable {} -#endif diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sequence.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sequence.swift index b3ef68b1d..3c86dd01d 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sequence.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Sequence.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2019 - 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2019 - 2023 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 @@ -28,7 +28,7 @@ extension TreeDictionary: Sequence { internal var _it: _HashTreeIterator @inlinable - internal init(_root: _RawNode) { + internal init(_root: _RawHashNode) { self._it = _HashTreeIterator(root: _root) } } @@ -51,10 +51,8 @@ extension TreeDictionary: Sequence { } } -#if swift(>=5.5) extension TreeDictionary.Iterator: @unchecked Sendable where Key: Sendable, Value: Sendable {} -#endif extension TreeDictionary.Iterator: IteratorProtocol { /// The element type of a dictionary: a tuple containing an individual diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Values.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Values.swift index cb0c6a3bf..40d4d4a15 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Values.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Values.swift @@ -2,21 +2,23 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeDictionary { /// A view of a dictionary’s values. @frozen public struct Values { @usableFromInline - internal typealias _Node = HashTreeCollections._Node + internal typealias _Node = TreeDictionary._Node @usableFromInline internal typealias _UnsafeHandle = _Node.UnsafeHandle @@ -41,10 +43,8 @@ extension TreeDictionary { } } -#if swift(>=5.5) extension TreeDictionary.Values: Sendable where Key: Sendable, Value: Sendable {} -#endif extension TreeDictionary.Values: CustomStringConvertible { // A textual representation of this instance. @@ -90,10 +90,8 @@ extension TreeDictionary.Values: Sequence { } } -#if swift(>=5.5) extension TreeDictionary.Values.Iterator: Sendable where Key: Sendable, Value: Sendable {} -#endif // Note: This cannot be a MutableCollection because its subscript setter // needs to invalidate indices. diff --git a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary.swift b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary.swift index 7b4c5611d..fba483b34 100644 --- a/Sources/HashTreeCollections/TreeDictionary/TreeDictionary.swift +++ b/Sources/HashTreeCollections/TreeDictionary/TreeDictionary.swift @@ -65,7 +65,7 @@ @frozen // Not really -- this package is not at all ABI stable public struct TreeDictionary { @usableFromInline - internal typealias _Node = HashTreeCollections._Node + internal typealias _Node = _HashNode @usableFromInline internal typealias _UnsafeHandle = _Node.UnsafeHandle @@ -345,6 +345,7 @@ extension TreeDictionary { public mutating func updateValue( _ value: __owned Value, forKey key: Key ) -> Value? { + defer { _fixLifetime(self) } let hash = _Hash(key) let r = _root.updateValue(.top, forKey: key, hash) { $0.initialize(to: (key, value)) @@ -364,6 +365,7 @@ extension TreeDictionary { internal mutating func _updateValue( _ value: __owned Value, forKey key: Key ) -> Bool { + defer { _fixLifetime(self) } let hash = _Hash(key) let r = _root.updateValue(.top, forKey: key, hash) { $0.initialize(to: (key, value)) @@ -470,6 +472,7 @@ extension TreeDictionary { default defaultValue: @autoclosure () -> Value, with body: (inout Value) throws -> R ) rethrows -> R { + defer { _fixLifetime(self) } let hash = _Hash(key) let r = _root.updateValue(.top, forKey: key, hash) { $0.initialize(to: (key, defaultValue())) diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Collection.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Collection.swift index 7817af252..dde99c0ce 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Collection.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Collection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 @@ -19,7 +19,7 @@ extension TreeSet { @frozen public struct Index { @usableFromInline - internal let _root: _UnmanagedNode + internal let _root: _UnmanagedHashNode @usableFromInline internal var _version: UInt @@ -29,7 +29,7 @@ extension TreeSet { @inlinable @inline(__always) internal init( - _root: _UnmanagedNode, version: UInt, path: _UnsafePath + _root: _UnmanagedHashNode, version: UInt, path: _UnsafePath ) { self._root = _root self._version = version @@ -38,10 +38,8 @@ extension TreeSet { } } -#if swift(>=5.5) extension TreeSet.Index: @unchecked Sendable where Element: Sendable {} -#endif extension TreeSet.Index: Equatable { /// Returns a Boolean value indicating whether two index values are equal. diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Debugging.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Debugging.swift index 14d69fa0a..22567864a 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Debugging.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Debugging.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet { /// True if consistency checking is enabled in the implementation of this @@ -32,7 +34,7 @@ extension TreeSet { } public static var _maxDepth: Int { - _Level.limit + _HashLevel.limit } public var _statistics: _HashTreeStatistics { diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Descriptions.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Descriptions.swift index f32127f16..13d7b4193 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Descriptions.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Descriptions.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet: CustomStringConvertible { /// A textual representation of this instance. diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Equatable.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Equatable.swift index 9e2382998..70130a20b 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Equatable.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Equatable.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet: Equatable { /// Returns a Boolean value indicating whether two values are equal. diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Extras.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Extras.swift index 788187ef2..2450a22ad 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Extras.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Extras.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet: _UniqueCollection {} @@ -52,6 +54,7 @@ extension TreeSet { /// - Complexity: O(log(`count`)) if set storage might be shared; O(1) /// otherwise. public mutating func update(_ member: Element, at index: Index) -> Element { + defer { _fixLifetime(self) } precondition(_isValid(index), "Invalid index") precondition(index._path.isOnItem, "Can't get element at endIndex") _invalidateIndices() diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Sendable.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Sendable.swift index b5b758e2c..e19e0a1b6 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Sendable.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Sendable.swift @@ -2,13 +2,11 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 swift(>=5.5) extension TreeSet: @unchecked Sendable where Element: Sendable {} -#endif diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+Sequence.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+Sequence.swift index cd0e3fdf2..43d3ac4af 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+Sequence.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+Sequence.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 @@ -20,7 +20,7 @@ extension TreeSet: Sequence { internal var _it: _HashTreeIterator @inlinable - internal init(_root: _RawNode) { + internal init(_root: _RawHashNode) { _it = _HashTreeIterator(root: _root) } @@ -49,7 +49,5 @@ extension TreeSet: Sequence { } } -#if swift(>=5.5) extension TreeSet.Iterator: @unchecked Sendable where Element: Sendable {} -#endif diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra basics.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra basics.swift index da97d142b..78b9a4bc1 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra basics.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra basics.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet: SetAlgebra { /// Returns a Boolean value that indicates whether the given element exists @@ -49,6 +51,7 @@ extension TreeSet: SetAlgebra { public mutating func insert( _ newMember: __owned Element ) -> (inserted: Bool, memberAfterInsert: Element) { + defer { _fixLifetime(self) } let hash = _Hash(newMember) let r = _root.insert(.top, (newMember, ()), hash) if r.inserted { @@ -115,6 +118,7 @@ extension TreeSet: SetAlgebra { @discardableResult @inlinable public mutating func update(with newMember: __owned Element) -> Element? { + defer { _fixLifetime(self) } let hash = _Hash(newMember) let r = _root.updateValue(.top, forKey: newMember, hash) { $0.initialize(to: (newMember, ())) diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra intersection.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra intersection.swift index f90000f93..c76205aa8 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra intersection.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra intersection.swift @@ -61,9 +61,7 @@ extension TreeSet { } @inlinable - internal func _intersection( - _ other: HashTreeCollections._Node - ) -> Self { + internal func _intersection(_ other: _HashNode) -> Self { guard let r = _root.intersection(.top, other) else { return self } return Self(_new: r) } diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isEqualSet.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isEqualSet.swift index 508762520..ba1e906c1 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isEqualSet.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isEqualSet.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // FIXME: These are non-standard extensions generalizing ==. extension TreeSet { diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSubset.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSubset.swift index be71ea6bb..b75ea452b 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSubset.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSubset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet { /// Returns a Boolean value that indicates whether the set is a strict subset diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSuperset.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSuperset.swift index 8a1633ca3..ba7eb0a06 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSuperset.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isStrictSuperset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet { /// Returns a Boolean value that indicates whether the set is a strict diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isSubset.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isSubset.swift index fb0d216b1..29bf895a4 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isSubset.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra isSubset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet { /// Returns a Boolean value that indicates whether this set is a subset of diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra subtracting.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra subtracting.swift index 78fba0d08..3bcb7b7df 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra subtracting.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra subtracting.swift @@ -54,7 +54,7 @@ extension TreeSet { @inlinable internal __consuming func _subtracting( - _ other: HashTreeCollections._Node + _ other: _HashNode ) -> Self { guard let r = _root.subtracting(.top, other) else { return self } return Self(_new: r) diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra symmetricDifference.swift b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra symmetricDifference.swift index 9d68d781a..e00bbe242 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra symmetricDifference.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet+SetAlgebra symmetricDifference.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension TreeSet { /// Returns a new set with the elements that are either in this set or in diff --git a/Sources/HashTreeCollections/TreeSet/TreeSet.swift b/Sources/HashTreeCollections/TreeSet/TreeSet.swift index 8ca4c6e43..096a38468 100644 --- a/Sources/HashTreeCollections/TreeSet/TreeSet.swift +++ b/Sources/HashTreeCollections/TreeSet/TreeSet.swift @@ -60,7 +60,7 @@ @frozen // Not really -- this package is not at all ABI stable public struct TreeSet { @usableFromInline - internal typealias _Node = HashTreeCollections._Node + internal typealias _Node = _HashNode @usableFromInline internal typealias _UnsafeHandle = _Node.UnsafeHandle diff --git a/Sources/HeapModule/CMakeLists.txt b/Sources/HeapModule/CMakeLists.txt index 9e0bb6daf..2249aa472 100644 --- a/Sources/HeapModule/CMakeLists.txt +++ b/Sources/HeapModule/CMakeLists.txt @@ -8,7 +8,7 @@ See https://swift.org/LICENSE.txt for license information #]] add_library(HeapModule - "_Node.swift" + "_HeapNode.swift" "Heap.swift" "Heap+Descriptions.swift" "Heap+ExpressibleByArrayLiteral.swift" diff --git a/Sources/HeapModule/Heap+Invariants.swift b/Sources/HeapModule/Heap+Invariants.swift index 12e9192a5..50f015835 100644 --- a/Sources/HeapModule/Heap+Invariants.swift +++ b/Sources/HeapModule/Heap+Invariants.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension Heap { /// True if consistency checking is enabled in the implementation of this @@ -33,7 +35,7 @@ extension Heap { } @inlinable - internal func _checkInvariants(node: _Node, min: Element?, max: Element?) { + internal func _checkInvariants(node: _HeapNode, min: Element?, max: Element?) { let value = _storage[node.offset] if let min = min { precondition(value >= min, diff --git a/Sources/HeapModule/Heap+UnsafeHandle.swift b/Sources/HeapModule/Heap+UnsafeHandle.swift index 601a5ec64..709d7dc9b 100644 --- a/Sources/HeapModule/Heap+UnsafeHandle.swift +++ b/Sources/HeapModule/Heap+UnsafeHandle.swift @@ -36,7 +36,7 @@ extension Heap._UnsafeHandle { } @inlinable - subscript(node: _Node) -> Element { + subscript(node: _HeapNode) -> Element { @inline(__always) get { buffer[node.offset] @@ -48,7 +48,7 @@ extension Heap._UnsafeHandle { } @inlinable @inline(__always) - internal func ptr(to node: _Node) -> UnsafeMutablePointer { + internal func ptr(to node: _HeapNode) -> UnsafeMutablePointer { assert(node.offset < count) return buffer.baseAddress! + node.offset } @@ -56,43 +56,43 @@ extension Heap._UnsafeHandle { /// Move the value at the specified node out of the buffer, leaving it /// uninitialized. @inlinable @inline(__always) - internal func extract(_ node: _Node) -> Element { + internal func extract(_ node: _HeapNode) -> Element { ptr(to: node).move() } @inlinable @inline(__always) - internal func initialize(_ node: _Node, to value: __owned Element) { + internal func initialize(_ node: _HeapNode, to value: __owned Element) { ptr(to: node).initialize(to: value) } /// Swaps the elements in the heap at the given indices. @inlinable @inline(__always) - internal func swapAt(_ i: _Node, _ j: _Node) { + internal func swapAt(_ i: _HeapNode, _ j: _HeapNode) { buffer.swapAt(i.offset, j.offset) } /// Swaps the element at the given node with the supplied value. @inlinable @inline(__always) - internal func swapAt(_ i: _Node, with value: inout Element) { + internal func swapAt(_ i: _HeapNode, with value: inout Element) { let p = buffer.baseAddress.unsafelyUnwrapped + i.offset swap(&p.pointee, &value) } @inlinable @inline(__always) - internal func minValue(_ a: _Node, _ b: _Node) -> _Node { + internal func minValue(_ a: _HeapNode, _ b: _HeapNode) -> _HeapNode { self[a] < self[b] ? a : b } @inlinable @inline(__always) - internal func maxValue(_ a: _Node, _ b: _Node) -> _Node { + internal func maxValue(_ a: _HeapNode, _ b: _HeapNode) -> _HeapNode { self[a] < self[b] ? b : a } } extension Heap._UnsafeHandle { @inlinable - internal func bubbleUp(_ node: _Node) { + internal func bubbleUp(_ node: _HeapNode) { guard !node.isRoot else { return } let parent = node.parent() @@ -124,7 +124,7 @@ extension Heap._UnsafeHandle { /// Sink the item at `node` to its correct position in the heap. /// The given node must be minimum-ordered. @inlinable - internal func trickleDownMin(_ node: _Node) { + internal func trickleDownMin(_ node: _HeapNode) { assert(node.isMinLevel) var node = node var value = extract(node) @@ -133,11 +133,11 @@ extension Heap._UnsafeHandle { } @inlinable @inline(__always) - internal func _trickleDownMin(node: inout _Node, value: inout Element) { - // Note: `_Node` is quite the useless abstraction here, as we don't need + internal func _trickleDownMin(node: inout _HeapNode, value: inout Element) { + // Note: `_HeapNode` is quite the useless abstraction here, as we don't need // to look at its `level` property, and we need to move sideways amongst // siblings/cousins in the tree, for which we don't have direct operations. - // Luckily, all the `_Node` business gets optimized away, so this only + // Luckily, all the `_HeapNode` business gets optimized away, so this only // affects the readability of the code, not its performance. // The alternative would be to reintroduce offset-based parent/child // navigation methods, which seems less palatable. @@ -147,11 +147,11 @@ extension Heap._UnsafeHandle { // Invariant: buffer slot at `node` is uninitialized // We have four grandchildren, so we don't need to compare children. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) let minA = minValue(gc0, gc1) - let gc2 = _Node(offset: gc0.offset &+ 2, level: gc0.level) - let gc3 = _Node(offset: gc0.offset &+ 3, level: gc0.level) + let gc2 = _HeapNode(offset: gc0.offset &+ 2, level: gc0.level) + let gc3 = _HeapNode(offset: gc0.offset &+ 3, level: gc0.level) let minB = minValue(gc2, gc3) let min = minValue(minA, minB) @@ -205,23 +205,23 @@ extension Heap._UnsafeHandle { /// This method is an implementation detail of `trickleDownMin`. Do not call /// it directly. @inlinable - internal func _minDescendant(c0: _Node, gc0: _Node) -> _Node { + internal func _minDescendant(c0: _HeapNode, gc0: _HeapNode) -> _HeapNode { assert(c0.offset < count) assert(gc0.offset + 3 >= count) if gc0.offset < count { if gc0.offset &+ 2 < count { // We have three grandchildren. We don't need to compare direct children. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) - let gc2 = _Node(offset: gc0.offset &+ 2, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) + let gc2 = _HeapNode(offset: gc0.offset &+ 2, level: gc0.level) return minValue(minValue(gc0, gc1), gc2) } - let c1 = _Node(offset: c0.offset &+ 1, level: c0.level) + let c1 = _HeapNode(offset: c0.offset &+ 1, level: c0.level) let m = minValue(c1, gc0) if gc0.offset &+ 1 < count { // Two grandchildren. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) return minValue(m, gc1) } @@ -229,7 +229,7 @@ extension Heap._UnsafeHandle { return m } - let c1 = _Node(offset: c0.offset &+ 1, level: c0.level) + let c1 = _HeapNode(offset: c0.offset &+ 1, level: c0.level) if c1.offset < count { return minValue(c0, c1) } @@ -240,7 +240,7 @@ extension Heap._UnsafeHandle { /// Sink the item at `node` to its correct position in the heap. /// The given node must be maximum-ordered. @inlinable - internal func trickleDownMax(_ node: _Node) { + internal func trickleDownMax(_ node: _HeapNode) { assert(!node.isMinLevel) var node = node var value = extract(node) @@ -250,19 +250,19 @@ extension Heap._UnsafeHandle { } @inlinable @inline(__always) - internal func _trickleDownMax(node: inout _Node, value: inout Element) { - // See note on `_Node` in `_trickleDownMin` above. + internal func _trickleDownMax(node: inout _HeapNode, value: inout Element) { + // See note on `_HeapNode` in `_trickleDownMin` above. var gc0 = node.firstGrandchild() while gc0.offset &+ 3 < count { // Invariant: buffer slot at `node` is uninitialized // We have four grandchildren, so we don't need to compare children. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) let maxA = maxValue(gc0, gc1) - let gc2 = _Node(offset: gc0.offset &+ 2, level: gc0.level) - let gc3 = _Node(offset: gc0.offset &+ 3, level: gc0.level) + let gc2 = _HeapNode(offset: gc0.offset &+ 2, level: gc0.level) + let gc3 = _HeapNode(offset: gc0.offset &+ 3, level: gc0.level) let maxB = maxValue(gc2, gc3) let max = maxValue(maxA, maxB) @@ -316,23 +316,23 @@ extension Heap._UnsafeHandle { /// This method is an implementation detail of `trickleDownMax`. Do not call /// it directly. @inlinable - internal func _maxDescendant(c0: _Node, gc0: _Node) -> _Node { + internal func _maxDescendant(c0: _HeapNode, gc0: _HeapNode) -> _HeapNode { assert(c0.offset < count) assert(gc0.offset + 3 >= count) if gc0.offset < count { if gc0.offset &+ 2 < count { // We have three grandchildren. We don't need to compare direct children. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) - let gc2 = _Node(offset: gc0.offset &+ 2, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) + let gc2 = _HeapNode(offset: gc0.offset &+ 2, level: gc0.level) return maxValue(maxValue(gc0, gc1), gc2) } - let c1 = _Node(offset: c0.offset &+ 1, level: c0.level) + let c1 = _HeapNode(offset: c0.offset &+ 1, level: c0.level) let m = maxValue(c1, gc0) if gc0.offset &+ 1 < count { // Two grandchildren. - let gc1 = _Node(offset: gc0.offset &+ 1, level: gc0.level) + let gc1 = _HeapNode(offset: gc0.offset &+ 1, level: gc0.level) return maxValue(m, gc1) } @@ -340,7 +340,7 @@ extension Heap._UnsafeHandle { return m } - let c1 = _Node(offset: c0.offset &+ 1, level: c0.level) + let c1 = _HeapNode(offset: c0.offset &+ 1, level: c0.level) if c1.offset < count { return maxValue(c0, c1) } @@ -358,18 +358,18 @@ extension Heap._UnsafeHandle { // FIXME: See if a more cache friendly algorithm would be faster. let limit = count / 2 // The first offset without a left child - var level = _Node.level(forOffset: limit &- 1) + var level = _HeapNode.level(forOffset: limit &- 1) while level >= 0 { - let nodes = _Node.allNodes(onLevel: level, limit: limit) + let nodes = _HeapNode.allNodes(onLevel: level, limit: limit) _heapify(level, nodes) level &-= 1 } } @inlinable - internal func _heapify(_ level: Int, _ nodes: ClosedRange<_Node>?) { + internal func _heapify(_ level: Int, _ nodes: ClosedRange<_HeapNode>?) { guard let nodes = nodes else { return } - if _Node.isMinLevel(level) { + if _HeapNode.isMinLevel(level) { nodes._forEach { node in trickleDownMin(node) } diff --git a/Sources/HeapModule/Heap.swift b/Sources/HeapModule/Heap.swift index 995c32084..4e7e60e9a 100644 --- a/Sources/HeapModule/Heap.swift +++ b/Sources/HeapModule/Heap.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 - 2023 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 @@ -35,9 +35,7 @@ public struct Heap { } } -#if swift(>=5.5) extension Heap: Sendable where Element: Sendable {} -#endif extension Heap { /// A Boolean value indicating whether or not the heap is empty. @@ -76,7 +74,7 @@ extension Heap { _storage.append(element) _update { handle in - handle.bubbleUp(_Node(offset: handle.count - 1)) + handle.bubbleUp(_HeapNode(offset: handle.count - 1)) } _checkInvariants() } @@ -119,7 +117,7 @@ extension Heap { if _storage.count > 0 { _update { handle in - let minNode = _Node.root + let minNode = _HeapNode.root handle.swapAt(minNode, with: &removed) handle.trickleDownMin(minNode) } @@ -191,7 +189,7 @@ extension Heap { var removed = replacement _update { handle in - let minNode = _Node.root + let minNode = _HeapNode.root handle.swapAt(minNode, with: &removed) handle.trickleDownMin(minNode) } diff --git a/Sources/HeapModule/_Node.swift b/Sources/HeapModule/_HeapNode.swift similarity index 86% rename from Sources/HeapModule/_Node.swift rename to Sources/HeapModule/_HeapNode.swift index a67639606..e3044c675 100644 --- a/Sources/HeapModule/_Node.swift +++ b/Sources/HeapModule/_HeapNode.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// @usableFromInline @frozen -internal struct _Node { +internal struct _HeapNode { @usableFromInline internal var offset: Int @@ -33,7 +33,7 @@ internal struct _Node { } } -extension _Node: Comparable { +extension _HeapNode: Comparable { @inlinable @inline(__always) internal static func ==(left: Self, right: Self) -> Bool { left.offset == right.offset @@ -45,29 +45,29 @@ extension _Node: Comparable { } } -extension _Node: CustomStringConvertible { +extension _HeapNode: CustomStringConvertible { @usableFromInline internal var description: String { "(offset: \(offset), level: \(level))" } } -extension _Node { +extension _HeapNode { @inlinable @inline(__always) internal static func level(forOffset offset: Int) -> Int { (offset &+ 1)._binaryLogarithm() } @inlinable @inline(__always) - internal static func firstNode(onLevel level: Int) -> _Node { + internal static func firstNode(onLevel level: Int) -> _HeapNode { assert(level >= 0) - return _Node(offset: (1 &<< level) &- 1, level: level) + return _HeapNode(offset: (1 &<< level) &- 1, level: level) } @inlinable @inline(__always) - internal static func lastNode(onLevel level: Int) -> _Node { + internal static func lastNode(onLevel level: Int) -> _HeapNode { assert(level >= 0) - return _Node(offset: (1 &<< (level &+ 1)) &- 2, level: level) + return _HeapNode(offset: (1 &<< (level &+ 1)) &- 2, level: level) } @inlinable @inline(__always) @@ -76,7 +76,7 @@ extension _Node { } } -extension _Node { +extension _HeapNode { /// The root node in the heap. @inlinable @inline(__always) internal static var root: Self { @@ -106,7 +106,7 @@ extension _Node { } } -extension _Node { +extension _HeapNode { /// Returns the parent of this index, or `nil` if the index has no parent /// (i.e. when this is the root index). @inlinable @inline(__always) @@ -160,11 +160,11 @@ extension _Node { } } -extension ClosedRange where Bound == _Node { +extension ClosedRange where Bound == _HeapNode { @inlinable @inline(__always) - internal func _forEach(_ body: (_Node) -> Void) { + internal func _forEach(_ body: (_HeapNode) -> Void) { assert( - isEmpty || _Node.level(forOffset: upperBound.offset) == lowerBound.level) + isEmpty || _HeapNode.level(forOffset: upperBound.offset) == lowerBound.level) var node = self.lowerBound while node.offset <= self.upperBound.offset { body(node) diff --git a/Sources/OrderedCollections/HashTable/_HashTable+UnsafeHandle.swift b/Sources/OrderedCollections/HashTable/_HashTable+UnsafeHandle.swift index 699a5ec84..205a2af5a 100644 --- a/Sources/OrderedCollections/HashTable/_HashTable+UnsafeHandle.swift +++ b/Sources/OrderedCollections/HashTable/_HashTable+UnsafeHandle.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif @usableFromInline internal typealias _UnsafeHashTable = _HashTable.UnsafeHandle diff --git a/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Elements.md b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Elements.md new file mode 100644 index 000000000..752f3f5c0 --- /dev/null +++ b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Elements.md @@ -0,0 +1,37 @@ +# ``OrderedCollections/OrderedDictionary/Elements-swift.struct`` + +## Topics + +### Inspecting an Elements View + +- ``isEmpty`` +- ``count`` + +### Accessing Elements + +- ``subscript(_:)-4xwc2`` +- ``keys`` +- ``values`` +- ``index(forKey:)`` + +### Removing Elements + +- ``remove(at:)`` +- ``removeSubrange(_:)-5x7oo`` +- ``removeSubrange(_:)-7wdak`` +- ``removeAll(keepingCapacity:)`` +- ``removeAll(where:)`` +- ``removeFirst()`` +- ``removeFirst(_:)`` +- ``removeLast()`` +- ``removeLast(_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)`` +- ``reverse()`` +- ``sort()`` +- ``sort(by:)`` +- ``partition(by:)`` +- ``shuffle()`` +- ``shuffle(using:)`` diff --git a/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Values.md b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Values.md new file mode 100644 index 000000000..6e50c1f4f --- /dev/null +++ b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.Values.md @@ -0,0 +1,24 @@ +# ``OrderedCollections/OrderedDictionary/Values-swift.struct`` + +## Topics + +### Inspecting a Values Collection + +- ``isEmpty`` +- ``count`` + +### Accessing Elements + +- ``subscript(_:)-25vfz`` +- ``elements`` +- ``withUnsafeBufferPointer(_:)`` +- ``withUnsafeMutableBufferPointer(_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)-77eiy`` +- ``partition(by:)-9x0i5`` +- ``sort()`` +- ``sort(by:)`` +- ``shuffle()`` +- ``shuffle(using:)`` diff --git a/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.md b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.md new file mode 100644 index 000000000..7e0845ee6 --- /dev/null +++ b/Sources/OrderedCollections/OrderedCollections.docc/Extensions/OrderedDictionary.md @@ -0,0 +1,94 @@ +# ``OrderedCollections/OrderedDictionary`` + +## Topics + +### Creating a Dictionary + +- ``init()`` +- ``init(minimumCapacity:persistent:)`` +- ``init(uniqueKeysWithValues:)-5ux9r`` +- ``init(uniqueKeysWithValues:)-88mzi`` +- ``init(uncheckedUniqueKeysWithValues:)-6gxhj`` +- ``init(uncheckedUniqueKeysWithValues:)-2j0dw`` +- ``init(uncheckedUniqueKeysWithValues:)-6gxhj`` +- ``init(uniqueKeys:values:)`` +- ``init(uncheckedUniqueKeys:values:)`` +- ``init(_:uniquingKeysWith:)-2y39b`` +- ``init(_:uniquingKeysWith:)-zhfp`` +- ``init(grouping:by:)-6mahw`` +- ``init(grouping:by:)-6m2zw`` + +### Inspecting a Dictionary + +- ``isEmpty`` +- ``count`` + +### Accessing Keys and Values + +- ``subscript(_:)`` +- ``subscript(_:default:)`` +- ``index(forKey:)`` + +### Collection Views + +- ``keys`` +- ``values-swift.property`` +- ``elements-swift.property`` + +### Updating Values + +- ``updateValue(_:forKey:)`` +- ``updateValue(_:forKey:insertingAt:)`` +- ``updateValue(forKey:default:with:)`` +- ``updateValue(forKey:insertingDefault:at:with:)`` + +### Removing Keys and Values + +- ``removeValue(forKey:)`` +- ``remove(at:)`` +- ``filter(_:)`` +- ``removeAll(where:)`` +- ``removeAll(keepingCapacity:)`` +- ``removeFirst()`` +- ``removeLast()`` +- ``removeFirst(_:)`` +- ``removeLast(_:)`` +- ``removeSubrange(_:)-512n3`` +- ``removeSubrange(_:)-8rmzx`` + +### Combining Dictionaries + +- ``merge(_:uniquingKeysWith:)-6ka2i`` +- ``merge(_:uniquingKeysWith:)-9wkad`` +- ``merging(_:uniquingKeysWith:)-4z49c`` +- ``merging(_:uniquingKeysWith:)-2e0xa`` + +### Comparing Dictionaries + +- ``==(_:_:)`` + +### Reordering Elements + +- ``swapAt(_:_:)`` +- ``reverse()`` +- ``sort()`` +- ``sort(by:)`` +- ``reverse()`` +- ``shuffle()`` +- ``shuffle(using:)`` +- ``partition(by:)`` + +### Transforming a Dictionary + +- ``mapValues(_:)`` +- ``compactMapValues(_:)`` + +### Memory Management + +- ``reserveCapacity(_:)`` + +### Supporting Types + +- ``Index`` +- ``Values-swift.struct`` +- ``Elements-swift.struct`` diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Descriptions.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Descriptions.swift index fede81ec4..9188f615b 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Descriptions.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Descriptions.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedDictionary: CustomStringConvertible { /// A textual representation of this instance. diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.SubSequence.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.SubSequence.swift index 64313feaf..b6c9f16ff 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.SubSequence.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.SubSequence.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedDictionary.Elements { /// A collection that represents a contiguous slice of an ordered dictionary. @@ -32,10 +34,8 @@ extension OrderedDictionary.Elements { } } -#if swift(>=5.5) extension OrderedDictionary.Elements.SubSequence: Sendable where Key: Sendable, Value: Sendable {} -#endif extension OrderedDictionary.Elements.SubSequence: CustomStringConvertible { // A textual representation of this instance. @@ -147,10 +147,8 @@ extension OrderedDictionary.Elements.SubSequence: Sequence { } } -#if swift(>=5.5) extension OrderedDictionary.Elements.SubSequence.Iterator: Sendable where Key: Sendable, Value: Sendable {} -#endif extension OrderedDictionary.Elements.SubSequence: RandomAccessCollection { /// The index type for an ordered dictionary: `Int`. diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.swift index 39c4fb1d2..66cd9926a 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Elements.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedDictionary { /// A view of the contents of an ordered dictionary as a random-access @@ -27,10 +29,8 @@ extension OrderedDictionary { } } -#if swift(>=5.5) extension OrderedDictionary.Elements: Sendable where Key: Sendable, Value: Sendable {} -#endif extension OrderedDictionary { /// A view of the contents of this dictionary as a random-access collection. diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Invariants.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Invariants.swift index 58964d224..ae3830386 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Invariants.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Invariants.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedDictionary { /// True if consistency checking is enabled in the implementation of this diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Partial MutableCollection.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Partial MutableCollection.swift index f43d69935..35970a7f3 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Partial MutableCollection.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Partial MutableCollection.swift @@ -132,7 +132,7 @@ extension OrderedDictionary { /// Use the `shuffle()` method to randomly reorder the elements of an ordered /// dictionary. /// - /// This method is equivalent to calling `shuffle(using:)`, passing in the + /// This method is equivalent to calling ``shuffle(using:)``, passing in the /// system's default random generator. /// /// - Complexity: O(*n*), where *n* is the length of the collection. diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sendable.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sendable.swift index 539a4fe19..b8ce46703 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sendable.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sendable.swift @@ -2,14 +2,12 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 swift(>=5.5) extension OrderedDictionary: @unchecked Sendable where Key: Sendable, Value: Sendable {} -#endif diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sequence.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sequence.swift index 21967d9ed..4d516ed5c 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sequence.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Sequence.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 - 2023 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 @@ -62,7 +62,5 @@ extension OrderedDictionary: Sequence { } } -#if swift(>=5.5) extension OrderedDictionary.Iterator: Sendable where Key: Sendable, Value: Sendable {} -#endif diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Values.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Values.swift index 013badd90..6796917f4 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Values.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Values.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedDictionary { /// A view of an ordered dictionary's values as a standalone collection. @@ -26,10 +28,8 @@ extension OrderedDictionary { } } -#if swift(>=5.5) extension OrderedDictionary.Values: Sendable where Key: Sendable, Value: Sendable {} -#endif extension OrderedDictionary.Values: CustomStringConvertible { // A textual representation of this instance. diff --git a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary.swift b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary.swift index c3dc81f86..14e37fb93 100644 --- a/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary.swift +++ b/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary.swift @@ -108,7 +108,8 @@ /// beginning of the collection. However, to avoid ambiguity between key-based /// and indexing subscripts, `OrderedDictionary` doesn't directly conform to /// `Collection`. Instead, it only conforms to `Sequence`, and provides a -/// random-access collection view over its key-value pairs: +/// random-access collection view over its key-value pairs, called +/// ``elements-swift.property``: /// /// responses[0] // `nil` (key-based subscript) /// responses.elements[0] // `(200, "OK")` (index-based subscript) @@ -116,7 +117,6 @@ /// Because ordered dictionaries need to maintain unique keys, neither /// `OrderedDictionary` nor its `elements` view can conform to the full /// `MutableCollection` or `RangeReplaceableCollection` protocols. -/// /// However, `OrderedDictioanr` is still able to implement some of the /// requirements of these protocols. In particular, it supports permutation /// operations from `MutableCollection`: diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Descriptions.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Descriptions.swift index 3e4056680..fa023ffc1 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Descriptions.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Descriptions.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet: CustomStringConvertible { /// A textual representation of this instance. diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Initializers.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Initializers.swift index ab24de09d..12d6fc479 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Initializers.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Initializers.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// Creates a set with the contents of the given sequence, which diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Invariants.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Invariants.swift index 53de455ea..467a43777 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Invariants.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Invariants.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// True if consistency checking is enabled in the implementation of this diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial MutableCollection.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial MutableCollection.swift index bbc139bb7..b917e04c1 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial MutableCollection.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial MutableCollection.swift @@ -11,7 +11,9 @@ // The parts of MutableCollection that OrderedSet is able to implement. +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// Exchanges the values at the specified indices of the set. diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra intersection.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra intersection.swift index 7535c14b8..c71061a10 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra intersection.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra intersection.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isDisjoint.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isDisjoint.swift index 8c2f1340c..60ed2b96f 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isDisjoint.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isDisjoint.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isEqualSet.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isEqualSet.swift index bb77e59d8..2f69d6585 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isEqualSet.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isEqualSet.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// Returns a Boolean value indicating whether two set values contain the diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSubset.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSubset.swift index 827b07f93..fcdbb9518 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSubset.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSubset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSuperset.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSuperset.swift index 02fbb67d0..61b708508 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSuperset.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isStrictSuperset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isSubset.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isSubset.swift index 9d35c0fc9..0b1c64f81 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isSubset.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra isSubset.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra subtracting.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra subtracting.swift index ce4daf882..f5e54d52d 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra subtracting.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra subtracting.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra symmetricDifference.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra symmetricDifference.swift index d693c5a03..327d80969 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra symmetricDifference.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Partial SetAlgebra symmetricDifference.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif // `OrderedSet` does not directly conform to `SetAlgebra` because its definition // of equality conflicts with `SetAlgebra` requirements. However, it still diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift index c8b5dc8b0..d8d194443 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet: Sequence { /// The type that allows iteration over an ordered set's elements. diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+SubSequence.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+SubSequence.swift index b21262d24..017a0d222 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+SubSequence.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+SubSequence.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// A collection that represents a contiguous slice of an ordered set. @@ -35,9 +37,7 @@ extension OrderedSet { } } -#if swift(>=5.5) extension OrderedSet.SubSequence: Sendable where Element: Sendable {} -#endif extension OrderedSet.SubSequence { @inlinable diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Testing.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Testing.swift index 84443339f..96f812717 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Testing.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+Testing.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet._UnstableInternals { @_spi(Testing) public var capacity: Int { base._capacity } diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+UnorderedView.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+UnorderedView.swift index 0611718d3..8efa76d03 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+UnorderedView.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+UnorderedView.swift @@ -2,14 +2,16 @@ // // 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 - 2023 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 !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif extension OrderedSet { /// An unordered view into an ordered set, providing `SetAlgebra` @@ -64,9 +66,7 @@ extension OrderedSet { } } -#if swift(>=5.5) extension OrderedSet.UnorderedView: Sendable where Element: Sendable {} -#endif extension OrderedSet.UnorderedView: CustomStringConvertible { /// A textual representation of this instance. diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+UnstableInternals.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+UnstableInternals.swift index 3c94ed43f..effc6ec0c 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+UnstableInternals.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+UnstableInternals.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 - 2023 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 @@ -47,7 +47,5 @@ extension OrderedSet { } } -#if swift(>=5.5) extension OrderedSet._UnstableInternals: Sendable where Element: Sendable {} -#endif diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet.swift index 761b7d116..0934047b3 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet.swift @@ -9,7 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities +#endif /// An ordered collection of unique elements. /// @@ -46,9 +48,9 @@ import _CollectionsUtilities /// # Set Operations /// /// `OrderedSet` implements most, but not all, `SetAlgebra` requirements. In -/// particular, it supports the membership test `contains(_:)` as well as all -/// high-level set operations such as `union(_:)`, `intersection(_:)` or -/// `isSubset(of:)`. +/// particular, it supports the membership test ``contains(_:)`` as well as all +/// high-level set operations such as ``union(_:)-67y2h``, +/// ``intersection(_:)-4o09a`` or ``isSubset(of:)-ptij``. /// /// buildingMaterials.contains("glass") // false /// buildingMaterials.intersection(["brick", "straw"]) // ["straw", "brick"] @@ -58,20 +60,21 @@ import _CollectionsUtilities /// above, the ordering of elements in the result is guaranteed to match their /// order in the first input set, `buildingMaterials`. /// -/// On the other hand, predicates such as `isSubset(of:)` tend to ignore element -/// ordering: +/// On the other hand, predicates such as ``isSubset(of:)-ptij`` tend to ignore +/// element ordering: /// /// let moreMaterials: OrderedSet = ["bricks", "glass", "sticks", "straw"] /// buildingMaterials.isSubset(of: moreMaterials) // true /// -/// However, `OrderedSet` does not implement `insert(_:)` nor `update(with:)` -- -/// it provides its own variants for insertion that are more explicit about -/// where in the collection new elements gets inserted: +/// `OrderedSet` does not implement `insert(_:)` nor `update(with:)` from +/// `SetAlgebra` -- it provides its own variants for insertion that are more +/// explicit about where in the collection new elements gets inserted: /// -/// func insert(_ item: Element, at index: Int) -> (inserted: Bool, index: Int) /// func append(_ item: Element) -> (inserted: Bool, index: Int) -/// func update(at index: Int, with item: Element) -> Element +/// func insert(_ item: Element, at index: Int) -> (inserted: Bool, index: Int) /// func updateOrAppend(_ item: Element) -> Element? +/// func updateOrInsert(_ item: Element, at index: Int) -> (originalMember: Element?, index: Int) +/// func update(_ item: Element, at index: Int) -> Element /// /// Additionally,`OrderedSet` has an order-sensitive definition of equality (see /// above) that is incompatible with `SetAlgebra`'s documented semantic @@ -83,8 +86,9 @@ import _CollectionsUtilities /// For cases where `SetAlgebra` conformance is desired (such as when passing an /// ordered set to a function that is generic over that protocol), `OrderedSet` /// provides an efficient *unordered view* of its elements that conforms to -/// `SetAlgebra`. The unordered view implements the same concept of equality as -/// the standard `Set`, ignoring element ordering. +/// `SetAlgebra`. This view is accessed through the ``unordered`` property, and +/// it implements the same concept of equality as the standard `Set`, ignoring +/// element ordering. /// /// var a: OrderedSet = [0, 1, 2, 3] /// let b: OrderedSet = [3, 2, 1, 0] @@ -128,13 +132,15 @@ import _CollectionsUtilities /// Because `OrderedSet` needs to keep its members unique, it cannot conform to /// the full `MutableCollection` or `RangeReplaceableCollection` protocols. /// Operations such as `MutableCollection`'s subscript setter or -/// `RangeReplaceableCollection`'s `replaceSubrange` assume the ability to -/// insert/replace arbitrary elements in the collection, but allowing that could -/// lead to duplicate values. +/// `RangeReplaceableCollection`'s `replaceSubrange` method assume the ability +/// to insert/replace arbitrary elements in the collection, but allowing that +/// could lead to duplicate values. /// /// However, `OrderedSet` is able to partially implement these two protocols; -/// namely, there is no issue with mutation operations that merely change the -/// order of elements, or just remove some subset of existing members. +/// namely, it supports mutation operations that merely change the +/// order of elements (such as ``sort()`` or ``swapAt(_:_:)``, or just remove +/// some subset of existing members (such as ``remove(at:)`` or +/// ``removeAll(where:)``). /// /// Accordingly, `OrderedSet` provides permutation operations from `MutableCollection`: /// - ``swapAt(_:_:)`` @@ -162,7 +168,7 @@ import _CollectionsUtilities /// that only takes an array value or (or something that's generic over /// `RangeReplaceableCollection` or `MutableCollection`), then the best option /// is usually to directly extract the members of the `OrderedSet` as an `Array` -/// value using its `elements` property. `OrderedSet` uses a standard array +/// value using its ``elements`` property. `OrderedSet` uses a standard array /// value for element storage, so extracting the array value has minimal /// overhead. /// diff --git a/Sources/OrderedCollections/Utilities/_UnsafeBitset.swift b/Sources/OrderedCollections/Utilities/_UnsafeBitset.swift index 5fe05fcdb..10a8aee55 100644 --- a/Sources/OrderedCollections/Utilities/_UnsafeBitset.swift +++ b/Sources/OrderedCollections/Utilities/_UnsafeBitset.swift @@ -9,8 +9,9 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsUtilities @usableFromInline internal typealias _UnsafeBitSet = _CollectionsUtilities._UnsafeBitSet - +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Builder.swift b/Sources/RopeModule/BigString/Basics/BigString+Builder.swift new file mode 100644 index 000000000..2f595fdd1 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Builder.swift @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + struct Builder { + typealias _Chunk = BigString._Chunk + typealias _Ingester = BigString._Ingester + typealias _Rope = BigString._Rope + + var base: _Rope.Builder + var suffixStartState: _CharacterRecognizer + var prefixEndState: _CharacterRecognizer + + init( + base: _Rope.Builder, + prefixEndState: _CharacterRecognizer, + suffixStartState: _CharacterRecognizer + ) { + self.base = base + self.suffixStartState = suffixStartState + self.prefixEndState = prefixEndState + } + + init() { + self.base = _Rope.Builder() + self.suffixStartState = _CharacterRecognizer() + self.prefixEndState = _CharacterRecognizer() + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension Rope.Builder { + internal func _breakState() -> _CharacterRecognizer { + let chars = self.prefixSummary.characters + assert(self.isPrefixEmpty || chars > 0) + let metric = BigString._CharacterMetric() + var state = _CharacterRecognizer() + _ = self.forEachElementInPrefix(from: chars - 1, in: metric) { chunk, i in + if let i { + state = .init(partialCharacter: chunk.string[i...]) + } else { + state.consumePartialCharacter(chunk.string[...]) + } + return true + } + return state + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Builder { + mutating func append(_ str: __owned some StringProtocol) { + append(Substring(str)) + } + + mutating func append(_ str: __owned String) { + append(str[...]) + } + + mutating func append(_ str: __owned Substring) { + guard !str.isEmpty else { return } + var ingester = _Ingester(str, startState: self.prefixEndState) + if var prefix = base._prefix._take() { + if let slice = ingester.nextSlice(maxUTF8Count: prefix.value.availableSpace) { + prefix.value._append(slice) + } + self.base._prefix = prefix + } + while let next = ingester.nextChunk() { + base.insertBeforeTip(next) + } + self.prefixEndState = ingester.state + } + + mutating func append(_ newChunk: __owned _Chunk) { + var state = _CharacterRecognizer() + append(newChunk, state: &state) + } + + mutating func append(_ newChunk: __owned _Chunk, state: inout _CharacterRecognizer) { + var newChunk = newChunk + newChunk.resyncBreaksFromStartToEnd(old: &state, new: &self.prefixEndState) + self.base.insertBeforeTip(newChunk) + } + + mutating func append(_ other: __owned BigString) { + var state = _CharacterRecognizer() + append(other._rope, state: &state) + } + + mutating func append(_ other: __owned BigString, in range: Range) { + let extract = BigString(other, in: range, state: &self.prefixEndState) + self.base.insertBeforeTip(extract._rope) + } + + mutating func append(_ other: __owned _Rope, state: inout _CharacterRecognizer) { + guard !other.isEmpty else { return } + var other = BigString(_rope: other) + other._rope.resyncBreaksToEnd(old: &state, new: &self.prefixEndState) + self.base.insertBeforeTip(other._rope) + } + + mutating func append(from ingester: inout _Ingester) { + //assert(ingester.state._isKnownEqual(to: self.prefixEndState)) + if var prefix = base._prefix._take() { + if let first = ingester.nextSlice(maxUTF8Count: prefix.value.availableSpace) { + prefix.value._append(first) + } + base._prefix = prefix + } + + let suffixCount = base._suffix?.value.utf8Count ?? 0 + + while let chunk = ingester.nextWellSizedChunk(suffix: suffixCount) { + base.insertBeforeTip(chunk) + } + precondition(ingester.isAtEnd) + self.prefixEndState = ingester.state + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Builder { + mutating func finalize() -> BigString { + // Resync breaks in suffix. + _ = base.mutatingForEachSuffix { chunk in + chunk.resyncBreaksFromStart(old: &suffixStartState, new: &prefixEndState) + } + // Roll it all up. + let rope = self.base.finalize() + let string = BigString(_rope: rope) + string._invariantCheck() + return string + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Contents.swift b/Sources/RopeModule/BigString/Basics/BigString+Contents.swift new file mode 100644 index 000000000..1786ce852 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Contents.swift @@ -0,0 +1,534 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + /// The estimated maximum number of UTF-8 code units that `BigString` is guaranteed to be able + /// to hold without encountering an overflow in its operations. This corresponds to the capacity + /// of the deepest tree where every node is the minimum possible size. + public static var _minimumCapacity: Int { + let c = _Rope._minimumCapacity + let (r, overflow) = _Chunk.minUTF8Count.multipliedReportingOverflow(by: c) + guard !overflow else { return Int.max } + return r + } + + /// The maximum number of UTF-8 code units that `BigString` may be able to store in the best + /// possible case, when every node in the underlying tree is fully filled with data. + public static var _maximumCapacity: Int { + let c = _Rope._maximumCapacity + let (r, overflow) = _Chunk.maxUTF8Count.multipliedReportingOverflow(by: c) + guard !overflow else { return Int.max } + return r + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + var _characterCount: Int { _rope.summary.characters } + var _unicodeScalarCount: Int { _rope.summary.unicodeScalars } + var _utf16Count: Int { _rope.summary.utf16 } + var _utf8Count: Int { _rope.summary.utf8 } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _distance( + from start: Index, + to end: Index, + in metric: some _StringMetric + ) -> Int { + precondition(start <= endIndex && end <= endIndex, "Invalid index") + guard start != end else { return 0 } + assert(!isEmpty) + let (lesser, greater) = (start <= end ? (start, end) : (end, start)) + let a = resolve(lesser, preferEnd: false) + let b = resolve(greater, preferEnd: true) + var d = 0 + + let ropeIndexA = a._rope! + let ropeIndexB = b._rope! + let chunkIndexA = a._chunkIndex + let chunkIndexB = b._chunkIndex + + if ropeIndexA == ropeIndexB { + d = metric.distance(from: chunkIndexA, to: chunkIndexB, in: _rope[ropeIndexA]) + } else { + let chunkA = _rope[ropeIndexA] + let chunkB = _rope[ropeIndexB] + d += _rope.distance(from: ropeIndexA, to: ropeIndexB, in: metric) + d -= metric.distance(from: chunkA.string.startIndex, to: chunkIndexA, in: chunkA) + d += metric.distance(from: chunkB.string.startIndex, to: chunkIndexB, in: chunkB) + } + return start <= end ? d : -d + } + + func _characterDistance(from start: Index, to end: Index) -> Int { + _distance(from: start, to: end, in: _CharacterMetric()) + } + + func _unicodeScalarDistance(from start: Index, to end: Index) -> Int { + _distance(from: start, to: end, in: _UnicodeScalarMetric()) + } + + func _utf16Distance(from start: Index, to end: Index) -> Int { + _distance(from: start, to: end, in: _UTF16Metric()) + } + + func _utf8Distance(from start: Index, to end: Index) -> Int { + end.utf8Offset - start.utf8Offset + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + // FIXME: See if we need direct implementations for these. + + func _characterOffset(of index: Index) -> Int { + _characterDistance(from: startIndex, to: index) + } + + func _unicodeScalarOffset(of index: Index) -> Int { + _unicodeScalarDistance(from: startIndex, to: index) + } + + func _utf16Offset(of index: Index) -> Int { + _utf16Distance(from: startIndex, to: index) + } + + func _utf8Offset(of index: Index) -> Int { + _utf8Distance(from: startIndex, to: index) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + // FIXME: See if we need direct implementations for these. + + func _characterIndex(at offset: Int) -> Index { + _characterIndex(startIndex, offsetBy: offset) + } + + func _unicodeScalarIndex(at offset: Int) -> Index { + _unicodeScalarIndex(startIndex, offsetBy: offset) + } + + func _utf16Index(at offset: Int) -> Index { + _utf16Index(startIndex, offsetBy: offset) + } + + func _utf8Index(at offset: Int) -> Index { + _utf8Index(startIndex, offsetBy: offset) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _index( + _ i: Index, + offsetBy distance: Int, + in metric: some _StringMetric + ) -> Index { + precondition(i <= endIndex, "Index out of bounds") + if isEmpty { + precondition(distance == 0, "Index out of bounds") + return startIndex + } + if i == endIndex, distance == 0 { return i } + let i = resolve(i, preferEnd: i == endIndex || distance < 0) + var ri = i._rope! + var ci = i._chunkIndex + var d = distance + var chunk = _rope[ri] + let r = metric.formIndex(&ci, offsetBy: &d, in: chunk) + if r.found { + return Index(baseUTF8Offset: i._utf8BaseOffset, _rope: ri, chunk: ci) + } + + if r.forward { + assert(distance >= 0) + assert(ci == chunk.string.endIndex) + d += metric._nonnegativeSize(of: chunk.summary) + let start = ri + _rope.formIndex(&ri, offsetBy: &d, in: metric, preferEnd: false) + if ri == _rope.endIndex { + return endIndex + } + chunk = _rope[ri] + ci = metric.index(at: d, in: chunk) + let base = i._utf8BaseOffset + _rope.distance(from: start, to: ri, in: _UTF8Metric()) + return Index(baseUTF8Offset: base, _rope: ri, chunk: ci) + } + + assert(distance <= 0) + assert(ci == chunk.string.startIndex) + let start = ri + _rope.formIndex(&ri, offsetBy: &d, in: metric, preferEnd: false) + chunk = _rope[ri] + ci = metric.index(at: d, in: chunk) + let base = i._utf8BaseOffset + _rope.distance(from: start, to: ri, in: _UTF8Metric()) + return Index(baseUTF8Offset: base, _rope: ri, chunk: ci) + } + + func _characterIndex(_ i: Index, offsetBy distance: Int) -> Index { + _index(i, offsetBy: distance, in: _CharacterMetric())._knownCharacterAligned() + } + + func _unicodeScalarIndex(_ i: Index, offsetBy distance: Int) -> Index { + _index(i, offsetBy: distance, in: _UnicodeScalarMetric())._knownScalarAligned() + } + + func _utf16Index(_ i: Index, offsetBy distance: Int) -> Index { + _index(i, offsetBy: distance, in: _UTF16Metric()) + } + + func _utf8Index(_ i: Index, offsetBy distance: Int) -> Index { + _index(i, offsetBy: distance, in: _UTF8Metric()) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _index( + _ i: Index, + offsetBy distance: Int, + limitedBy limit: Index, + in metric: some _StringMetric + ) -> Index? { + // FIXME: Do we need a direct implementation? + if distance >= 0 { + if limit >= i { + let d = self._distance(from: i, to: limit, in: metric) + if d < distance { return nil } + } + } else { + if limit <= i { + let d = self._distance(from: i, to: limit, in: metric) + if d > distance { return nil } + } + } + return self._index(i, offsetBy: distance, in: metric) + } + + func _characterIndex(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + guard let j = _index(i, offsetBy: distance, limitedBy: limit, in: _CharacterMetric()) else { + return nil + } + return j._knownCharacterAligned() + } + + func _unicodeScalarIndex(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + guard let j = _index(i, offsetBy: distance, limitedBy: limit, in: _UnicodeScalarMetric()) else { + return nil + } + return j._knownScalarAligned() + } + + func _utf16Index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _index(i, offsetBy: distance, limitedBy: limit, in: _UTF16Metric()) + } + + func _utf8Index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _index(i, offsetBy: distance, limitedBy: limit, in: _UTF8Metric()) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _characterIndex(after i: Index) -> Index { + _index(i, offsetBy: 1, in: _CharacterMetric())._knownCharacterAligned() + } + + func _unicodeScalarIndex(after i: Index) -> Index { + _index(i, offsetBy: 1, in: _UnicodeScalarMetric())._knownScalarAligned() + } + + func _utf16Index(after i: Index) -> Index { + _index(i, offsetBy: 1, in: _UTF16Metric()) + } + + func _utf8Index(after i: Index) -> Index { + precondition(i < endIndex, "Can't advance above end index") + let i = resolve(i, preferEnd: false) + let ri = i._rope! + var ci = i._chunkIndex + let chunk = _rope[ri] + chunk.string.utf8.formIndex(after: &ci) + if ci == chunk.string.endIndex { + return Index( + baseUTF8Offset: i._utf8BaseOffset + chunk.utf8Count, + _rope: _rope.index(after: ri), + chunk: String.Index(_utf8Offset: 0)) + } + return Index(_utf8Offset: i.utf8Offset + 1, _rope: ri, chunkOffset: ci._utf8Offset) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _characterIndex(before i: Index) -> Index { + _index(i, offsetBy: -1, in: _CharacterMetric())._knownCharacterAligned() + } + + func _unicodeScalarIndex(before i: Index) -> Index { + _index(i, offsetBy: -1, in: _UnicodeScalarMetric())._knownScalarAligned() + } + + func _utf16Index(before i: Index) -> Index { + _index(i, offsetBy: -1, in: _UTF16Metric()) + } + + func _utf8Index(before i: Index) -> Index { + precondition(i > startIndex, "Can't advance below start index") + let i = resolve(i, preferEnd: true) + var ri = i._rope! + let ci = i._chunkIndex + if ci._utf8Offset > 0 { + return Index( + _utf8Offset: i.utf8Offset &- 1, + _rope: ri, + chunkOffset: ci._utf8Offset &- 1) + } + _rope.formIndex(before: &ri) + let chunk = _rope[ri] + return Index( + baseUTF8Offset: i._utf8BaseOffset - chunk.utf8Count, + _rope: ri, + chunk: String.Index(_utf8Offset: chunk.utf8Count - 1)) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _characterIndex(roundingDown i: Index) -> Index { + let offset = i.utf8Offset + precondition(offset >= 0 && offset <= _utf8Count, "Index out of bounds") + guard offset > 0 else { return resolve(i, preferEnd: false)._knownCharacterAligned() } + guard offset < _utf8Count else { return resolve(i, preferEnd: true)._knownCharacterAligned() } + + let i = resolve(i, preferEnd: false) + guard !i._isKnownCharacterAligned else { return resolve(i, preferEnd: false) } + + var ri = i._rope! + let ci = i._chunkIndex + var chunk = _rope[ri] + if chunk.hasBreaks { + let first = chunk.firstBreak + let last = chunk.lastBreak + if ci == first || ci == last { return i } + if ci > last { + return Index( + baseUTF8Offset: i._utf8BaseOffset, _rope: ri, chunk: last + )._knownCharacterAligned() + } + if ci > first { + let j = chunk.wholeCharacters._index(roundingDown: ci) + return Index(baseUTF8Offset: i._utf8BaseOffset, _rope: ri, chunk: j)._knownCharacterAligned() + } + } + + var baseOffset = i._utf8BaseOffset + while ri > self._rope.startIndex { + self._rope.formIndex(before: &ri) + chunk = self._rope[ri] + baseOffset -= chunk.utf8Count + if chunk.hasBreaks { break } + } + return Index( + baseUTF8Offset: baseOffset, _rope: ri, chunk: chunk.lastBreak + )._knownCharacterAligned() + } + + func _unicodeScalarIndex(roundingDown i: Index) -> Index { + precondition(i <= endIndex, "Index out of bounds") + guard i > startIndex else { return resolve(i, preferEnd: false)._knownCharacterAligned() } + guard i < endIndex else { return resolve(i, preferEnd: true)._knownCharacterAligned() } + + let start = self.resolve(i, preferEnd: false) + guard !i._isKnownScalarAligned else { return resolve(i, preferEnd: false) } + let ri = start._rope! + let chunk = self._rope[ri] + let ci = chunk.string.unicodeScalars._index(roundingDown: start._chunkIndex) + return Index(baseUTF8Offset: start._utf8BaseOffset, _rope: ri, chunk: ci)._knownScalarAligned() + } + + func _utf8Index(roundingDown i: Index) -> Index { + precondition(i <= endIndex, "Index out of bounds") + guard i < endIndex else { return endIndex } + var r = i + if i._isUTF16TrailingSurrogate { + r._clearUTF16TrailingSurrogate() + } + return resolve(r, preferEnd: false) + } + + func _utf16Index(roundingDown i: Index) -> Index { + if i._isUTF16TrailingSurrogate { + precondition(i < endIndex, "Index out of bounds") + // (We know i can't be the endIndex -- it addresses a trailing surrogate.) + return self.resolve(i, preferEnd: false) + } + return _unicodeScalarIndex(roundingDown: i) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _characterIndex(roundingUp i: Index) -> Index { + let j = _characterIndex(roundingDown: i) + if i == j { return j } + return _characterIndex(after: j) + } + + func _unicodeScalarIndex(roundingUp i: Index) -> Index { + let j = _unicodeScalarIndex(roundingDown: i) + if i == j { return j } + return _unicodeScalarIndex(after: j) + } + + func _utf8Index(roundingUp i: Index) -> Index { + // Note: this orders UTF-16 trailing surrogate indices in between the first and second byte + // of the UTF-8 encoding. + let j = _utf8Index(roundingDown: i) + if i == j { return j } + return _utf8Index(after: j) + } + + func _utf16Index(roundingUp i: Index) -> Index { + // Note: if `i` addresses some byte in the middle of a non-BMP scalar then the result will + // point to the trailing surrogate. + let j = _utf16Index(roundingDown: i) + if i == j { return j } + return _utf16Index(after: j) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _character(at start: Index) -> (character: Character, end: Index) { + let start = _characterIndex(roundingDown: start) + precondition(start.utf8Offset < _utf8Count, "Index out of bounds") + + var ri = start._rope! + var ci = start._chunkIndex + var chunk = _rope[ri] + let char = chunk.wholeCharacters[ci] + let endOffset = start._utf8ChunkOffset + char.utf8.count + if endOffset < chunk.utf8Count { + let endStringIndex = chunk.string._utf8Index(at: endOffset) + let endIndex = Index( + baseUTF8Offset: start._utf8BaseOffset, _rope: ri, chunk: endStringIndex + )._knownCharacterAligned() + return (char, endIndex) + } + var s = String(char) + var base = start._utf8BaseOffset + chunk.utf8Count + while true { + _rope.formIndex(after: &ri) + guard ri < _rope.endIndex else { + ci = "".endIndex + break + } + chunk = _rope[ri] + s.append(contentsOf: chunk.prefix) + if chunk.hasBreaks { + ci = chunk.firstBreak + break + } + base += chunk.utf8Count + } + return (Character(s), Index(baseUTF8Offset: base, _rope: ri, chunk: ci)._knownCharacterAligned()) + } + + subscript(_utf8 index: Index) -> UInt8 { + precondition(index < endIndex, "Index out of bounds") + let index = resolve(index, preferEnd: false) + return _rope[index._rope!].string.utf8[index._chunkIndex] + } + + subscript(_utf8 offset: Int) -> UInt8 { + precondition(offset >= 0 && offset < _utf8Count, "Offset out of bounds") + let index = _utf8Index(at: offset) + return self[_utf8: index] + } + + subscript(_utf16 index: Index) -> UInt16 { + precondition(index < endIndex, "Index out of bounds") + let index = resolve(index, preferEnd: false) + return _rope[index._rope!].string.utf16[index._chunkIndex] + } + + subscript(_utf16 offset: Int) -> UInt16 { + precondition(offset >= 0 && offset < _utf16Count, "Offset out of bounds") + let index = _utf16Index(at: offset) + return self[_utf16: index] + } + + subscript(_character index: Index) -> Character { + _character(at: index).character + } + + subscript(_character offset: Int) -> Character { + precondition(offset >= 0 && offset < _utf8Count, "Offset out of bounds") + return _character(at: Index(_utf8Offset: offset)).character + } + + subscript(_unicodeScalar index: Index) -> Unicode.Scalar { + precondition(index < endIndex, "Index out of bounds") + let index = resolve(index, preferEnd: false) + return _rope[index._rope!].string.unicodeScalars[index._chunkIndex] + } + + subscript(_unicodeScalar offset: Int) -> Unicode.Scalar { + precondition(offset >= 0 && offset < _unicodeScalarCount, "Offset out of bounds") + let index = _unicodeScalarIndex(at: offset) + return self[_unicodeScalar: index] + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _foreachChunk( + from start: Index, + to end: Index, + _ body: (Substring) -> Void + ) { + precondition(start <= end) + guard start < end else { return } + let start = resolve(start, preferEnd: false) + let end = resolve(end, preferEnd: true) + + var ri = start._rope! + let endRopeIndex = end._rope! + + if ri == endRopeIndex { + let str = self._rope[ri].string + body(str[start._chunkIndex ..< end._chunkIndex]) + return + } + + let firstChunk = self._rope[ri].string + body(firstChunk[start._chunkIndex...]) + + _rope.formIndex(after: &ri) + while ri < endRopeIndex { + let string = _rope[ri].string + body(string[...]) + } + + let lastChunk = self._rope[ri].string + body(lastChunk[..=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public func _dump(heightLimit: Int = .max) { + _rope._dump(heightLimit: heightLimit) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Index.swift b/Sources/RopeModule/BigString/Basics/BigString+Index.swift new file mode 100644 index 000000000..b1602bef3 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Index.swift @@ -0,0 +1,250 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public struct Index: Sendable { + typealias _Rope = BigString._Rope + + // ┌───────────────────────────────┬───┬───────────┬────────────────────┐ + // │ b63:b11 │b10│ b9:b8 │ b7:b0 │ + // ├───────────────────────────────┼───┼───────────┼────────────────────┤ + // │ UTF-8 global offset │ T │ alignment │ UTF-8 chunk offset │ + // └───────────────────────────────┴───┴───────────┴────────────────────┘ + // b10 (T): UTF-16 trailing surrogate indicator + // b9: isCharacterAligned + // b8: isScalarAligned + // + // 100: UTF-16 trailing surrogate + // 001: Index known to be scalar aligned + // 011: Index known to be Character aligned + var _rawBits: UInt64 + + /// A (possibly invalid) rope index. + var _rope: _Rope.Index? + + internal init(_raw: UInt64, _rope: _Rope.Index?) { + self._rawBits = _raw + self._rope = _rope + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index { + @inline(__always) + internal static func _bitsForUTF8Offset(_ utf8Offset: Int) -> UInt64 { + let v = UInt64(truncatingIfNeeded: UInt(bitPattern: utf8Offset)) + assert(v &>> 53 == 0) + return v &<< 11 + } + + @inline(__always) + internal static var _flagsMask: UInt64 { 0x700 } + + @inline(__always) + internal static var _utf16TrailingSurrogateBits: UInt64 { 0x400 } + + @inline(__always) + internal static var _characterAlignmentBit: UInt64 { 0x200 } + + @inline(__always) + internal static var _scalarAlignmentBit: UInt64 { 0x100 } + + public var utf8Offset: Int { + Int(truncatingIfNeeded: _rawBits &>> 11) + } + + @inline(__always) + internal var _orderingValue: UInt64 { + _rawBits &>> 10 + } + + /// The offset within the addressed chunk. Only valid if `_rope` is not nil. + internal var _utf8ChunkOffset: Int { + assert(_rope != nil) + return Int(truncatingIfNeeded: _rawBits & 0xFF) + } + + /// The base offset of the addressed chunk. Only valid if `_rope` is not nil. + internal var _utf8BaseOffset: Int { + utf8Offset - _utf8ChunkOffset + } + + @inline(__always) + internal var _flags: UInt64 { + get { + _rawBits & Self._flagsMask + } + set { + assert(newValue & ~Self._flagsMask == 0) + _rawBits &= ~Self._flagsMask + _rawBits |= newValue + } + } +} + +extension String.Index { + @available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) + func _copyingAlignmentBits(from i: BigString.Index) -> String.Index { + var bits = _abi_rawBits & ~3 + bits |= (i._flags &>> 8) & 3 + return String.Index(_rawBits: bits) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index { + internal var _chunkIndex: String.Index { + assert(_rope != nil) + return String.Index( + _utf8Offset: _utf8ChunkOffset, utf16TrailingSurrogate: _isUTF16TrailingSurrogate + )._copyingAlignmentBits(from: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index { + internal mutating func _clearUTF16TrailingSurrogate() { + _flags = 0 + } + + public var _isUTF16TrailingSurrogate: Bool { + _orderingValue & 1 != 0 + } + + internal func _knownScalarAligned() -> Self { + var copy = self + copy._flags = Self._scalarAlignmentBit + return copy + } + + internal func _knownCharacterAligned() -> Self { + var copy = self + copy._flags = Self._characterAlignmentBit | Self._scalarAlignmentBit + return copy + } + + public var _isKnownScalarAligned: Bool { + _rawBits & Self._scalarAlignmentBit != 0 + } + + public var _isKnownCharacterAligned: Bool { + _rawBits & Self._characterAlignmentBit != 0 + } + + public init(_utf8Offset: Int) { + _rawBits = Self._bitsForUTF8Offset(_utf8Offset) + _rope = nil + } + + public init(_utf8Offset: Int, utf16TrailingSurrogate: Bool) { + _rawBits = Self._bitsForUTF8Offset(_utf8Offset) + if utf16TrailingSurrogate { + _rawBits |= Self._utf16TrailingSurrogateBits + } + _rope = nil + } + + internal init( + _utf8Offset: Int, utf16TrailingSurrogate: Bool = false, _rope: _Rope.Index, chunkOffset: Int + ) { + _rawBits = Self._bitsForUTF8Offset(_utf8Offset) + if utf16TrailingSurrogate { + _rawBits |= Self._utf16TrailingSurrogateBits + } + assert(chunkOffset >= 0 && chunkOffset <= 0xFF) + _rawBits |= UInt64(truncatingIfNeeded: chunkOffset) & 0xFF + self._rope = _rope + } + + internal init(baseUTF8Offset: Int, _rope: _Rope.Index, chunk: String.Index) { + let chunkUTF8Offset = chunk._utf8Offset + self.init( + _utf8Offset: baseUTF8Offset + chunkUTF8Offset, + utf16TrailingSurrogate: chunk._isUTF16TrailingSurrogate, + _rope: _rope, + chunkOffset: chunkUTF8Offset) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + left._orderingValue == right._orderingValue + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index: Comparable { + public static func <(left: Self, right: Self) -> Bool { + left._orderingValue < right._orderingValue + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(_orderingValue) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Index: CustomStringConvertible { + public var description: String { + let utf16Offset = _isUTF16TrailingSurrogate ? "+1" : "" + return "\(utf8Offset)[utf8]\(utf16Offset)" + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func resolve(_ i: Index, preferEnd: Bool) -> Index { + if var ri = i._rope, _rope.isValid(ri) { + if preferEnd { + guard i.utf8Offset > 0, i._utf8ChunkOffset == 0, !i._isUTF16TrailingSurrogate else { + return i + } + _rope.formIndex(before: &ri) + let length = _rope[ri].utf8Count + let ci = String.Index(_utf8Offset: length) + var j = Index(baseUTF8Offset: i.utf8Offset - length, _rope: ri, chunk: ci) + j._flags = i._flags + return j + } + guard i.utf8Offset < _utf8Count, i._utf8ChunkOffset == _rope[ri].utf8Count else { + return i + } + _rope.formIndex(after: &ri) + let ci = String.Index(_utf8Offset: 0) + var j = Index(baseUTF8Offset: i.utf8Offset, _rope: ri, chunk: ci) + j._flags = i._flags + return j + } + + // Indices addressing trailing surrogates must never be resolved at the end of chunk, + // because the +1 doesn't make sense on any endIndex. + let trailingSurrogate = i._isUTF16TrailingSurrogate + + let (ri, chunkOffset) = _rope.find( + at: i.utf8Offset, + in: _UTF8Metric(), + preferEnd: preferEnd && !trailingSurrogate) + + let ci = String.Index( + _utf8Offset: chunkOffset, + utf16TrailingSurrogate: trailingSurrogate) + return Index(baseUTF8Offset: i.utf8Offset - ci._utf8Offset, _rope: ri, chunk: ci) + } +} +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Ingester.swift b/Sources/RopeModule/BigString/Basics/BigString+Ingester.swift new file mode 100644 index 000000000..ecb505439 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Ingester.swift @@ -0,0 +1,167 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _ingester( + forInserting input: __owned Substring, + at index: Index, + allowForwardPeek: Bool + ) -> _Ingester { + let hint = allowForwardPeek ? input.unicodeScalars.first : nil + let state = self._breakState(upTo: index, nextScalarHint: hint) + return _Ingester(input, startState: state) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + internal struct _Ingester { + typealias _Chunk = BigString._Chunk + typealias Counts = BigString._Chunk.Counts + + var input: Substring + + /// The index of the beginning of the next chunk. + var start: String.Index + + /// Grapheme breaking state at the start of the next chunk. + var state: _CharacterRecognizer + + init(_ input: Substring) { + self.input = input + self.start = input.startIndex + self.state = _CharacterRecognizer() + } + + init(_ input: Substring, startState: __owned _CharacterRecognizer) { + self.input = input + self.start = input.startIndex + self.state = startState + } + + init(_ input: String) { + self.init(input[...]) + } + + init(_ input: S) { + self.init(Substring(input)) + } + + var isAtEnd: Bool { + start == input.endIndex + } + + var remainingUTF8: Int { + input.utf8.distance(from: start, to: input.endIndex) + } + + mutating func nextSlice( + maxUTF8Count: Int = _Chunk.maxUTF8Count + ) -> _Chunk.Slice? { + guard let range = input.base._nextSlice( + after: start, limit: input.endIndex, maxUTF8Count: maxUTF8Count) + else { + assert(start == input.endIndex) + return nil + } + if range.isEmpty { + return nil // Not enough room. + } + assert(range.lowerBound == start && range.upperBound <= input.endIndex) + start = range.upperBound + + var s = input[range] + let c8 = s.utf8.count + guard let r = state.firstBreak(in: s) else { + // Anomalous case -- chunk is entirely a continuation of a single character. + return ( + string: s, + characters: 0, + prefix: c8, + suffix: c8) + } + let first = r.lowerBound + s = s.suffix(from: r.upperBound) + + var characterCount = 1 + var last = first + while let r = state.firstBreak(in: s) { + last = r.lowerBound + s = s.suffix(from: r.upperBound) + characterCount += 1 + } + let prefixCount = input.utf8.distance(from: range.lowerBound, to: first) + let suffixCount = input.utf8.distance(from: last, to: range.upperBound) + return ( + string: input[range], + characters: characterCount, + prefix: prefixCount, + suffix: suffixCount) + } + + mutating func nextChunk(maxUTF8Count: Int = _Chunk.maxUTF8Count) -> _Chunk? { + guard let slice = nextSlice(maxUTF8Count: maxUTF8Count) else { return nil } + return _Chunk(slice) + } + + static func desiredNextChunkSize(remaining: Int) -> Int { + if remaining <= _Chunk.maxUTF8Count { + return remaining + } + if remaining >= _Chunk.maxUTF8Count + _Chunk.minUTF8Count { + return _Chunk.maxUTF8Count + } + return remaining - _Chunk.minUTF8Count + } + + mutating func nextWellSizedSlice(suffix: Int = 0) -> _Chunk.Slice? { + let desired = Self.desiredNextChunkSize(remaining: remainingUTF8 + suffix) + return nextSlice(maxUTF8Count: desired) + } + + mutating func nextWellSizedChunk(suffix: Int = 0) -> _Chunk? { + guard let slice = nextWellSizedSlice(suffix: suffix) else { return nil } + return _Chunk(slice) + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension String { + func _nextSlice( + after i: Index, + limit: Index, + maxUTF8Count: Int + ) -> Range? { + assert(maxUTF8Count >= 0) + assert(i._isKnownScalarAligned) + guard i < limit else { return nil } + let end = self.utf8.index(i, offsetBy: maxUTF8Count, limitedBy: limit) ?? limit + let j = self.unicodeScalars._index(roundingDown: end) + return Range(uncheckedBounds: (i, j)) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + init(_ string: String) { + guard !string.isEmpty else { self.init(); return } + assert(string.utf8.count <= Self.maxUTF8Count) + var ingester = BigString._Ingester(string) + self = ingester.nextChunk()! + assert(ingester.isAtEnd) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Invariants.swift b/Sources/RopeModule/BigString/Basics/BigString+Invariants.swift new file mode 100644 index 000000000..989d9a734 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Invariants.swift @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public func _invariantCheck() { +#if COLLECTIONS_INTERNAL_CHECKS + _rope._invariantCheck() + let allowUndersize = _rope.isSingleton + + var state = _CharacterRecognizer() + for chunk in _rope { + precondition(allowUndersize || !chunk.isUndersized, "Undersized chunk") + let (characters, prefix, suffix) = state.edgeCounts(consuming: chunk.string) + precondition( + chunk.prefixCount == prefix, + "Inconsistent position of first grapheme break in chunk") + precondition( + chunk.suffixCount == suffix, + "Inconsistent position of last grapheme break in chunk") + precondition( + chunk.characterCount == characters, + "Inconsistent character count in chunk") + } +#endif + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Iterators.swift b/Sources/RopeModule/BigString/Basics/BigString+Iterators.swift new file mode 100644 index 000000000..bfe8ddb96 --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Iterators.swift @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + struct ChunkIterator { + var base: _Rope.Iterator + + init(base: _Rope.Iterator) { + self.base = base + } + } + + func makeChunkIterator() -> ChunkIterator { + ChunkIterator(base: _rope.makeIterator()) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.ChunkIterator: IteratorProtocol { + typealias Element = String + + mutating func next() -> String? { + base.next()?.string + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Metrics.swift b/Sources/RopeModule/BigString/Basics/BigString+Metrics.swift new file mode 100644 index 000000000..3b2c7f2cc --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Metrics.swift @@ -0,0 +1,209 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +internal protocol _StringMetric: RopeMetric where Element == BigString._Chunk { + func distance( + from start: String.Index, + to end: String.Index, + in chunk: BigString._Chunk + ) -> Int + + func formIndex( + _ i: inout String.Index, + offsetBy distance: inout Int, + in chunk: BigString._Chunk + ) -> (found: Bool, forward: Bool) +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + internal struct _CharacterMetric: _StringMetric { + typealias Element = BigString._Chunk + typealias Summary = BigString.Summary + + @inline(__always) + func size(of summary: Summary) -> Int { + summary.characters + } + + func distance( + from start: String.Index, + to end: String.Index, + in chunk: BigString._Chunk + ) -> Int { + chunk.characterDistance(from: start, to: end) + } + + func formIndex( + _ i: inout String.Index, + offsetBy distance: inout Int, + in chunk: BigString._Chunk + ) -> (found: Bool, forward: Bool) { + chunk.formCharacterIndex(&i, offsetBy: &distance) + } + + func index(at offset: Int, in chunk: BigString._Chunk) -> String.Index { + precondition(offset < chunk.characterCount) + return chunk.wholeCharacters._index(at: offset) + } + } + + internal struct _UnicodeScalarMetric: _StringMetric { + @inline(__always) + func size(of summary: Summary) -> Int { + summary.unicodeScalars + } + + func distance( + from start: String.Index, + to end: String.Index, + in chunk: BigString._Chunk + ) -> Int { + chunk.string.unicodeScalars.distance(from: start, to: end) + } + + func formIndex( + _ i: inout String.Index, + offsetBy distance: inout Int, + in chunk: BigString._Chunk + ) -> (found: Bool, forward: Bool) { + guard distance != 0 else { + i = chunk.string.unicodeScalars._index(roundingDown: i) + return (true, false) + } + if distance > 0 { + let end = chunk.string.endIndex + while distance > 0, i < end { + chunk.string.unicodeScalars.formIndex(after: &i) + distance &-= 1 + } + return (distance == 0, true) + } + let start = chunk.string.startIndex + while distance < 0, i > start { + chunk.string.unicodeScalars.formIndex(before: &i) + distance &+= 1 + } + return (distance == 0, false) + } + + func index(at offset: Int, in chunk: BigString._Chunk) -> String.Index { + chunk.string.unicodeScalars.index(chunk.string.startIndex, offsetBy: offset) + } + } + + internal struct _UTF8Metric: _StringMetric { + @inline(__always) + func size(of summary: Summary) -> Int { + summary.utf8 + } + + func distance( + from start: String.Index, + to end: String.Index, + in chunk: BigString._Chunk + ) -> Int { + chunk.string.utf8.distance(from: start, to: end) + } + + func formIndex( + _ i: inout String.Index, + offsetBy distance: inout Int, + in chunk: BigString._Chunk + ) -> (found: Bool, forward: Bool) { + // Here we make use of the fact that the UTF-8 view of native Swift strings + // have O(1) index distance & offset calculations. + let offset = chunk.string.utf8.distance(from: chunk.string.startIndex, to: i) + if distance >= 0 { + let rest = chunk.utf8Count - offset + if distance > rest { + i = chunk.string.endIndex + distance -= rest + return (false, true) + } + i = chunk.string.utf8.index(i, offsetBy: distance) + distance = 0 + return (true, true) + } + + if offset + distance < 0 { + i = chunk.string.startIndex + distance += offset + return (false, false) + } + i = chunk.string.utf8.index(i, offsetBy: distance) + distance = 0 + return (true, false) + } + + func index(at offset: Int, in chunk: BigString._Chunk) -> String.Index { + chunk.string.utf8.index(chunk.string.startIndex, offsetBy: offset) + } + } + + internal struct _UTF16Metric: _StringMetric { + @inline(__always) + func size(of summary: Summary) -> Int { + summary.utf16 + } + + func distance( + from start: String.Index, + to end: String.Index, + in chunk: BigString._Chunk + ) -> Int { + chunk.string.utf16.distance(from: start, to: end) + } + + func formIndex( + _ i: inout String.Index, + offsetBy distance: inout Int, + in chunk: BigString._Chunk + ) -> (found: Bool, forward: Bool) { + if distance >= 0 { + if + distance <= chunk.utf16Count, + let r = chunk.string.utf16.index( + i, offsetBy: distance, limitedBy: chunk.string.endIndex + ) { + i = r + distance = 0 + return (true, true) + } + distance -= chunk.string.utf16.distance(from: i, to: chunk.string.endIndex) + i = chunk.string.endIndex + return (false, true) + } + + if + distance.magnitude <= chunk.utf16Count, + let r = chunk.string.utf16.index( + i, offsetBy: distance, limitedBy: chunk.string.endIndex + ) { + i = r + distance = 0 + return (true, true) + } + distance += chunk.string.utf16.distance(from: chunk.string.startIndex, to: i) + i = chunk.string.startIndex + return (false, false) + } + + func index(at offset: Int, in chunk: BigString._Chunk) -> String.Index { + chunk.string.utf16.index(chunk.string.startIndex, offsetBy: offset) + } + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString+Summary.swift b/Sources/RopeModule/BigString/Basics/BigString+Summary.swift new file mode 100644 index 000000000..eed08698c --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString+Summary.swift @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + struct Summary { + // FIXME: We only need 48 * 3 = 192 bits to represent a nonnegative value; pack these better + // (Unfortunately we also need to represent negative values right now.) + private(set) var characters: Int + private(set) var unicodeScalars: Int + private(set) var utf16: Int + private(set) var utf8: Int + + init() { + characters = 0 + unicodeScalars = 0 + utf16 = 0 + utf8 = 0 + } + + init(_ chunk: BigString._Chunk) { + self.utf8 = chunk.utf8Count + self.utf16 = Int(chunk.counts.utf16) + self.unicodeScalars = Int(chunk.counts.unicodeScalars) + self.characters = Int(chunk.counts.characters) + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Summary: CustomStringConvertible { + var description: String { + "❨\(utf8)⋅\(utf16)⋅\(unicodeScalars)⋅\(characters)❩" + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Summary: RopeSummary { + @inline(__always) + static var maxNodeSize: Int { + #if DEBUG + return 10 + #else + return 15 + #endif + } + + @inline(__always) + static var nodeSizeBitWidth: Int { 4 } + + @inline(__always) + static var zero: Self { Self() } + + @inline(__always) + var isZero: Bool { utf8 == 0 } + + mutating func add(_ other: BigString.Summary) { + characters += other.characters + unicodeScalars += other.unicodeScalars + utf16 += other.utf16 + utf8 += other.utf8 + } + + mutating func subtract(_ other: BigString.Summary) { + characters -= other.characters + unicodeScalars -= other.unicodeScalars + utf16 -= other.utf16 + utf8 -= other.utf8 + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Basics/BigString.swift b/Sources/RopeModule/BigString/Basics/BigString.swift new file mode 100644 index 000000000..c98f1b8ca --- /dev/null +++ b/Sources/RopeModule/BigString/Basics/BigString.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +/// The core of a B-tree based String implementation. +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +public struct BigString: Sendable { + typealias _Rope = Rope<_Chunk> + + var _rope: _Rope + + internal init(_rope: _Rope) { + self._rope = _rope + } +} + +#else + +// `BigString` depends on fixes and newly exposed functionality that landed in +// version 5.8 of the Swift Standard Library. +@available(*, unavailable, message: "BigString depends on version 5.8 of the Swift Standard Library") +public struct BigString: Sendable {} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Append and Insert.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Append and Insert.swift new file mode 100644 index 000000000..a4d4b8e0d --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Append and Insert.swift @@ -0,0 +1,174 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + mutating func append(_ other: __owned Self) { + self._append(other.string[...], other.counts) + } + + mutating func append(from ingester: inout BigString._Ingester) -> Self? { + let desired = BigString._Ingester.desiredNextChunkSize( + remaining: self.utf8Count + ingester.remainingUTF8) + if desired == self.utf8Count { + return nil + } + if desired > self.utf8Count { + if let slice = ingester.nextSlice(maxUTF8Count: desired - self.utf8Count) { + self._append(slice) + } + return nil + } + + // Split current chunk. + let cut = string.unicodeScalars._index(roundingDown: string._utf8Index(at: desired)) + var new = self.split(at: cut) + precondition(!self.isUndersized) + let slice = ingester.nextSlice()! + new._append(slice) + precondition(ingester.isAtEnd) + precondition(!new.isUndersized) + return new + } + + mutating func _append(_ other: Slice) { + let c = Counts(other) + _append(other.string, c) + } + + mutating func _append(_ str: __owned Substring, _ other: Counts) { + self.counts.append(other) + self.string += str + invariantCheck() + } + + mutating func _prepend(_ other: Slice) { + let c = Counts(other) + _prepend(other.string, c) + } + + mutating func _prepend(_ str: __owned Substring, _ other: Counts) { + let c = self.counts + self.counts = other + self.counts.append(c) + self.string = str + self.string + invariantCheck() + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + mutating func _insert( + _ slice: Slice, + at index: String.Index, + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) -> String.Index? { + let offset = string._utf8Offset(of: index) + let count = slice.string.utf8.count + precondition(utf8Count + count <= Self.maxUTF8Count) + + let parts = self.splitCounts(at: index) + self.counts = parts.left + self.counts.append(Counts(slice)) + self.counts.append(parts.right) + + string.insert(contentsOf: slice.string, at: index) + + let end = string._utf8Index(at: offset + count) + return resyncBreaks(startingAt: end, old: &old, new: &new) + } + + typealias States = (increment: Int, old: _CharacterRecognizer, new: _CharacterRecognizer) + + mutating func insertAll( + from ingester: inout BigString._Ingester, + at index: String.Index + ) -> States? { + let remaining = ingester.remainingUTF8 + precondition(self.utf8Count + remaining <= Self.maxUTF8Count) + var startState = ingester.state + guard let slice = ingester.nextSlice(maxUTF8Count: remaining) else { return nil } + var endState = ingester.state + assert(ingester.isAtEnd) + let offset = string._utf8Offset(of: index) + if let _ = self._insert(slice, at: index, old: &startState, new: &endState) { + return nil + } + return (self.utf8Count - offset, startState, endState) + } + + enum InsertResult { + case inline(States?) + case split(spawn: BigString._Chunk, endStates: States?) + case large + } + + mutating func insert( + from ingester: inout BigString._Ingester, + at index: String.Index + ) -> InsertResult { + let origCount = self.utf8Count + let rem = ingester.remainingUTF8 + guard rem > 0 else { return .inline(nil) } + let sum = origCount + rem + + let offset = string._utf8Offset(of: index) + if sum <= Self.maxUTF8Count { + let r = insertAll(from: &ingester, at: index) + return .inline(r) + } + + let desired = BigString._Ingester.desiredNextChunkSize(remaining: sum) + guard sum - desired + Self.maxSlicingError <= Self.maxUTF8Count else { return .large } + + if desired <= offset { + // Inserted text lies entirely within `spawn`. + let cut = string.unicodeScalars._index(roundingDown: string._utf8Index(at: desired)) + var spawn = split(at: cut) + let i = spawn.string._utf8Index(at: offset - self.utf8Count) + let r = spawn.insertAll(from: &ingester, at: i) + assert(r == nil || r?.increment == sum - offset) + return .split(spawn: spawn, endStates: r) + } + if desired >= offset + rem { + // Inserted text lies entirely within `self`. + let cut = string.unicodeScalars._index(roundingDown: string._utf8Index(at: desired - rem)) + assert(cut >= index) + var spawn = split(at: cut) + guard + var r = self.insertAll(from: &ingester, at: string._utf8Index(at: offset)), + nil == spawn.resyncBreaks(startingAt: spawn.string.startIndex, old: &r.old, new: &r.new) + else { + return .split(spawn: spawn, endStates: nil) + } + return .split(spawn: spawn, endStates: (sum - offset, r.old, r.new)) + } + // Inserted text is split across `self` and `spawn`. + var spawn = split(at: index) + var old = ingester.state + if let slice = ingester.nextSlice(maxUTF8Count: desired - offset) { + self._append(slice) + } + let slice = ingester.nextSlice()! + assert(ingester.isAtEnd) + var new = ingester.state + let stop = spawn._insert(slice, at: spawn.string.startIndex, old: &old, new: &new) + if stop != nil { + return .split(spawn: spawn, endStates: nil) + } + return .split(spawn: spawn, endStates: (sum - offset, old, new)) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Breaks.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Breaks.swift new file mode 100644 index 000000000..f49b3fede --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Breaks.swift @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + @inline(__always) + var hasBreaks: Bool { counts.hasBreaks } + + var firstBreak: String.Index { + get { + string._utf8Index(at: prefixCount) + } + set { + counts.prefix = string._utf8Offset(of: newValue) + } + } + + var lastBreak: String.Index { + get { + string._utf8Index(at: utf8Count - suffixCount) + } + set { + counts.suffix = utf8Count - string._utf8Offset(of: newValue) + } + } + + var prefix: Substring { string[.. String.Index? { + let index = string.unicodeScalars._index(roundingDown: index) + let first = firstBreak + guard index > first else { return nil } + let last = lastBreak + guard index <= last else { return last } + let w = string[first...] + let rounded = w._index(roundingDown: index) + guard rounded == index else { return rounded } + return w.index(before: rounded) + } + + func immediateBreakState( + upTo index: String.Index + ) -> (prevBreak: String.Index, state: _CharacterRecognizer)? { + guard let prev = nearestBreak(before: index) else { return nil } + let state = _CharacterRecognizer(partialCharacter: string[prev..=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + struct Counts: Equatable { + /// The number of UTF-8 code units within this chunk. + var utf8: UInt8 + /// The number of UTF-16 code units within this chunk. + var utf16: UInt8 + /// The number of Unicode scalars within this chunk. + var unicodeScalars: UInt8 + /// The number of Unicode scalars within this chunk that start a Character. + var _characters: UInt8 + /// The number of UTF-8 code units at the start of this chunk that continue a Character + /// whose start scalar is in a previous chunk. + var _prefix: UInt8 + /// The number of UTF-8 code units at the end of this chunk that form the start a Character + /// whose end scalar is in a subsequent chunk. + var _suffix: UInt8 + + init() { + self.utf8 = 0 + self.utf16 = 0 + self.unicodeScalars = 0 + self._characters = 0 + self._prefix = 0 + self._suffix = 0 + } + + init( + utf8: UInt8, + utf16: UInt8, + unicodeScalars: UInt8, + characters: UInt8, + prefix: UInt8, + suffix: UInt8 + ) { + assert(characters >= 0 && characters <= unicodeScalars && unicodeScalars <= utf16) + self.utf8 = utf8 + self.utf16 = utf16 + self.unicodeScalars = unicodeScalars + self._characters = characters + self._prefix = prefix + self._suffix = suffix + } + + init( + utf8: Int, + utf16: Int, + unicodeScalars: Int, + characters: Int, + prefix: Int, + suffix: Int + ) { + assert(characters >= 0 && characters <= unicodeScalars && unicodeScalars <= utf16) + self.utf8 = UInt8(utf8) + self.utf16 = UInt8(utf16) + self.unicodeScalars = UInt8(unicodeScalars) + self._characters = UInt8(characters) + self._prefix = UInt8(prefix) + self._suffix = UInt8(suffix) + } + + init( + anomalousUTF8 utf8: Int, + utf16: Int, + unicodeScalars: Int + ) { + self.utf8 = UInt8(utf8) + self.utf16 = UInt8(utf16) + self.unicodeScalars = UInt8(unicodeScalars) + self._characters = 0 + self._prefix = self.utf8 + self._suffix = self.utf8 + } + + init(_ slice: Slice) { + let c = slice.string.utf8.count + precondition(c <= BigString._Chunk.maxUTF8Count) + self.init( + utf8: slice.string.utf8.count, + utf16: slice.string.utf16.count, + unicodeScalars: slice.string.unicodeScalars.count, + characters: slice.characters, + prefix: slice.prefix, + suffix: slice.suffix) + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk.Counts { + var characters: Int { + get { Int(_characters) } + set { _characters = UInt8(newValue) } + } + + var prefix: Int { + get { Int(_prefix) } + set { _prefix = UInt8(newValue) } + } + + var suffix: Int { + get { Int(_suffix) } + set { _suffix = UInt8(newValue) } + } + + var hasBreaks: Bool { + _prefix < utf8 + } + + func hasSpaceToMerge(_ other: Self) -> Bool { + Int(utf8) + Int(other.utf8) <= BigString._Chunk.maxUTF8Count + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk.Counts { + mutating func append(_ other: Self) { + assert(hasSpaceToMerge(other)) + + switch (self.hasBreaks, other.hasBreaks) { + case (true, true): + self._suffix = other._suffix + case (true, false): + self._suffix += other._suffix + case (false, true): + self._prefix += other._prefix + self._suffix = other._suffix + case (false, false): + self._prefix += other._prefix + self._suffix += other._suffix + } + self.utf8 += other.utf8 + self.utf16 += other.utf16 + self.unicodeScalars += other.unicodeScalars + self._characters += other._characters + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Description.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Description.swift new file mode 100644 index 000000000..8b7964c1d --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Description.swift @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk: CustomStringConvertible { + var description: String { + let counts = """ + ❨\(utf8Count)⋅\(utf16Count)⋅\(unicodeScalarCount)⋅\(characterCount)❩ + """._rpad(to: 17) + let d = _succinctContents(maxLength: 10) + return "Chunk(\(_identity)\(counts) \(d))" + } + + var _identity: String { +#if arch(arm64) || arch(x86_64) + // Let's use the second word of the string representation as the identity; it contains + // the String's storage reference (if any). + let b = unsafeBitCast(self.string, to: (UInt64, UInt64).self) + return "@" + String(b.1, radix: 16)._rpad(to: 17) +#else + return "" +#endif + } + + func _succinctContents(maxLength c: Int) -> String { + /// 4+"short"-1 + /// 0+"longer...tring"-1 + let pc = String(prefixCount)._lpad(to: 3) + let sc = String(suffixCount) + + let s = String(wholeCharacters) + if s.isEmpty { + return "\(pc)+...-\(sc)" + } + var result = "\(pc)+\"" + var state = _CharacterRecognizer(consuming: result) + + let i = result._appendQuotedProtectingLeft(s, with: &state, maxLength: c) + let j = s.index(s.endIndex, offsetBy: -c, limitedBy: string.startIndex) ?? string.startIndex + + if i < j { + result._appendProtectingRight("...", with: &state) + result._appendQuotedProtectingLeft(String(s[j...]), with: &state) + } else if i < s.endIndex { + let k = s._index(roundingDown: i) + if i == k { + result._appendQuotedProtectingLeft(String(s[i...]), with: &state) + } else if s.index(after: k) < s.endIndex { + result._appendProtectingRight("...", with: &state) + result._appendQuotedProtectingLeft(String(s[s.index(after: k)...]), with: &state) + } else { + let suffix = String(s[i...].unicodeScalars.suffix(3)) + result._appendProtectingRight("...", with: &state) + result._appendQuotedProtectingLeft(suffix, with: &state) + } + } + result._appendProtectingRight("\"-\(sc)", with: &state) + return result + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by Characters.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by Characters.swift new file mode 100644 index 000000000..199195a03 --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by Characters.swift @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension UInt8 { + /// Returns true if this is a leading code unit in the UTF-8 encoding of a Unicode scalar that + /// is outside the BMP. + var _isUTF8NonBMPLeadingCodeUnit: Bool { self >= 0b11110000 } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + func characterDistance(from start: String.Index, to end: String.Index) -> Int { + let firstBreak = self.firstBreak + let (start, a) = start < firstBreak ? (firstBreak, 1) : (start, 0) + let (end, b) = end < firstBreak ? (firstBreak, 1) : (end, 0) + let d = wholeCharacters.distance(from: start, to: end) + return d + a - b + } + + /// If this returns false, the next position is on the first grapheme break following this + /// chunk. + func formCharacterIndex(after i: inout String.Index) -> Bool { + if i >= lastBreak { + i = string.endIndex + return false + } + let first = firstBreak + if i < first { + i = first + return true + } + wholeCharacters.formIndex(after: &i) + return true + } + + /// If this returns false, the right position is `distance` steps from the first grapheme break + /// following this chunk if `distance` was originally positive. Otherwise the right position is + /// `-distance` steps from the first grapheme break preceding this chunk. + func formCharacterIndex( + _ i: inout String.Index, offsetBy distance: inout Int + ) -> (found: Bool, forward: Bool) { + if distance == 0 { + if i < firstBreak { + i = string.startIndex + return (false, false) + } + if i >= lastBreak { + i = lastBreak + return (true, false) + } + i = wholeCharacters._index(roundingDown: i) + return (true, false) + } + if distance > 0 { + if i >= lastBreak { + i = string.endIndex + distance -= 1 + return (false, true) + } + if i < firstBreak { + i = firstBreak + distance -= 1 + if distance == 0 { return (true, true) } + } + if + distance <= characterCount, + let r = wholeCharacters.index(i, offsetBy: distance, limitedBy: string.endIndex) + { + i = r + distance = 0 + return (i < string.endIndex, true) + } + distance -= wholeCharacters.distance(from: i, to: lastBreak) + 1 + i = string.endIndex + return (false, true) + } + if i <= firstBreak { + i = string.startIndex + if i == firstBreak { distance += 1 } + return (false, false) + } + if i > lastBreak { + i = lastBreak + distance += 1 + if distance == 0 { return (true, false) } + } + if + distance.magnitude <= characterCount, + let r = self.wholeCharacters.index(i, offsetBy: distance, limitedBy: firstBreak) + { + i = r + distance = 0 + return (true, false) + } + distance += self.wholeCharacters.distance(from: firstBreak, to: i) + i = string.startIndex + return (false, false) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by UTF16.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by UTF16.swift new file mode 100644 index 000000000..e10d8f01b --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+Indexing by UTF16.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + /// UTF-16 index lookup. + func index(at utf8Offset: Int, utf16TrailingSurrogate: Bool) -> String.Index { + var index = string._utf8Index(at: utf8Offset) + if + utf16TrailingSurrogate, + index < string.endIndex, + string.utf8[index]._isUTF8NonBMPLeadingCodeUnit + { + index = string.utf16.index(after: index) + } + return index + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk+RopeElement.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+RopeElement.swift new file mode 100644 index 000000000..6f26a8871 --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk+RopeElement.swift @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk: RopeElement { + typealias Summary = BigString.Summary + typealias Index = String.Index + + var summary: BigString.Summary { + Summary(self) + } + + var isEmpty: Bool { string.isEmpty } + + var isUndersized: Bool { utf8Count < Self.minUTF8Count } + + func invariantCheck() { +#if COLLECTIONS_INTERNAL_CHECKS + precondition(string.endIndex._canBeUTF8) + let c = utf8Count + if c == 0 { + precondition(counts == Counts(), "Non-empty counts") + return + } + precondition(c <= Self.maxUTF8Count, "Oversized chunk") + //precondition(utf8Count >= Self.minUTF8Count, "Undersized chunk") + + precondition(counts.utf8 == string.utf8.count, "UTF-8 count mismatch") + precondition(counts.utf16 == string.utf16.count, "UTF-16 count mismatch") + precondition(counts.unicodeScalars == string.unicodeScalars.count, "Scalar count mismatch") + + precondition(counts.prefix <= c, "Invalid prefix count") + precondition(counts.suffix <= c && counts.suffix > 0, "Invalid suffix count") + if Int(counts.prefix) + Int(counts.suffix) <= c { + let i = firstBreak + let j = lastBreak + precondition(i <= j, "Overlapping prefix and suffix") + let s = string[i...] + precondition(counts.characters == s.count, "Inconsistent character count") + precondition(j == s.index(before: s.endIndex), "Inconsistent suffix count") + } else { + // Anomalous case + precondition(counts.prefix == c, "Inconsistent prefix count (continuation)") + precondition(counts.suffix == c, "Inconsistent suffix count (continuation)") + precondition(counts.characters == 0, "Inconsistent character count (continuation)") + } +#endif + } + + mutating func rebalance(nextNeighbor right: inout Self) -> Bool { + if self.isEmpty { + swap(&self, &right) + return true + } + guard !right.isEmpty else { return true } + guard self.isUndersized || right.isUndersized else { return false } + let sum = self.utf8Count + right.utf8Count + let desired = BigString._Ingester.desiredNextChunkSize(remaining: sum) + + precondition(desired != self.utf8Count) + if desired < self.utf8Count { + let i = self.string._utf8Index(at: desired) + let j = self.string.unicodeScalars._index(roundingDown: i) + Self._redistributeData(&self, &right, splittingLeftAt: j) + } else { + let i = right.string._utf8Index(at: desired - self.utf8Count) + let j = right.string.unicodeScalars._index(roundingDown: i) + Self._redistributeData(&self, &right, splittingRightAt: j) + } + assert(right.isEmpty || (!self.isUndersized && !right.isUndersized)) + return right.isEmpty + } + + mutating func rebalance(prevNeighbor left: inout Self) -> Bool { + if self.isEmpty { + swap(&self, &left) + return true + } + guard !left.isEmpty else { return true } + guard left.isUndersized || self.isUndersized else { return false } + let sum = left.utf8Count + self.utf8Count + let desired = BigString._Ingester.desiredNextChunkSize(remaining: sum) + + precondition(desired != self.utf8Count) + if desired < self.utf8Count { + let i = self.string._utf8Index(at: self.utf8Count - desired) + let j = self.string.unicodeScalars._index(roundingDown: i) + let k = (i == j ? i : self.string.unicodeScalars.index(after: j)) + Self._redistributeData(&left, &self, splittingRightAt: k) + } else { + let i = left.string._utf8Index(at: left.utf8Count + self.utf8Count - desired) + let j = left.string.unicodeScalars._index(roundingDown: i) + let k = (i == j ? i : left.string.unicodeScalars.index(after: j)) + Self._redistributeData(&left, &self, splittingLeftAt: k) + } + assert(left.isEmpty || (!left.isUndersized && !self.isUndersized)) + return left.isEmpty + } + + mutating func split(at i: String.Index) -> Self { + assert(i == string.unicodeScalars._index(roundingDown: i)) + let c = splitCounts(at: i) + let new = Self(string[i...], c.right) + self = Self(string[.. right.string.startIndex) + guard i < right.string.endIndex else { + left.append(right) + right.clear() + return + } + let counts = right.splitCounts(at: i) + left._append(right.string[.. left.string.startIndex else { + left.append(right) + right.clear() + swap(&left, &right) + return + } + let counts = left.splitCounts(at: i) + right._prepend(left.string[i...], counts.right) + left = Self(left.string[..=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + func splitCounts(at i: String.Index) -> (left: Counts, right: Counts) { + precondition(i <= string.endIndex) + guard i < string.endIndex else { + return (self.counts, Counts()) + } + guard i > string.startIndex else { + return (Counts(), self.counts) + } + let i = string.unicodeScalars._index(roundingDown: i) + + let leftUTF16: Int + let leftScalars: Int + let rightUTF16: Int + let rightScalars: Int + if string._utf8Offset(of: i) <= utf8Count / 2 { + leftUTF16 = string.utf16.distance(from: string.startIndex, to: i) + rightUTF16 = self.utf16Count - leftUTF16 + leftScalars = string.unicodeScalars.distance(from: string.startIndex, to: i) + rightScalars = self.unicodeScalarCount - leftScalars + } else { + rightUTF16 = string.utf16.distance(from: i, to: string.endIndex) + leftUTF16 = self.utf16Count - rightUTF16 + rightScalars = string.unicodeScalars.distance(from: i, to: string.endIndex) + leftScalars = self.unicodeScalarCount - rightScalars + } + + let left = _counts(upTo: i, utf16: leftUTF16, scalars: leftScalars) + let right = _counts(from: i, utf16: rightUTF16, scalars: rightScalars) + + assert(left.utf8 + right.utf8 == self.counts.utf8) + assert(left.utf16 + right.utf16 == self.counts.utf16) + assert(left.unicodeScalars + right.unicodeScalars == self.counts.unicodeScalars) + assert(left.characters + right.characters == self.counts.characters) + return (left, right) + } + + func counts(upTo i: String.Index) -> Counts { + precondition(i <= string.endIndex) + let i = string.unicodeScalars._index(roundingDown: i) + guard i > string.startIndex else { return Counts() } + guard i < string.endIndex else { return self.counts } + + let utf16 = string.utf16.distance(from: string.startIndex, to: i) + let scalars = string.unicodeScalars.distance(from: string.startIndex, to: i) + return _counts(from: i, utf16: utf16, scalars: scalars) + } + + func _counts(upTo i: String.Index, utf16: Int, scalars: Int) -> Counts { + assert(i > string.startIndex && i < string.endIndex) + let s = string[.. lastBreak { + result._characters = self.counts._characters + result._prefix = self.counts._prefix + result._suffix = self.counts._suffix - (self.counts.utf8 - result.utf8) + } else { // i > firstBreak, i <= lastBreak + result._prefix = self.counts._prefix + let wholeChars = string[firstBreak ..< i] + assert(!wholeChars.isEmpty) + var state = _CharacterRecognizer() + let (characters, _, last) = state.consume(wholeChars)! + result.characters = characters + result.suffix = string.utf8.distance(from: last, to: i) + } + return result + } + + func counts(from i: String.Index) -> Counts { + precondition(i <= string.endIndex) + let i = string.unicodeScalars._index(roundingDown: i) + guard i > string.startIndex else { return self.counts } + guard i < string.endIndex else { return Counts() } + + let utf16 = string.utf16.distance(from: i, to: string.endIndex) + let scalars = string.unicodeScalars.distance(from: i, to: string.endIndex) + return _counts(from: i, utf16: utf16, scalars: scalars) + } + + func _counts(from i: String.Index, utf16: Int, scalars: Int) -> Counts { + assert(i > string.startIndex && i < string.endIndex) + let s = string[i...] + + var result = Counts( + utf8: s.utf8.count, + utf16: utf16, + unicodeScalars: scalars, + characters: 0, + prefix: 0, + suffix: 0) + + let firstBreak = self.firstBreak + let lastBreak = self.lastBreak + + if i > lastBreak { + result._characters = 0 + result._prefix = result.utf8 + result._suffix = result.utf8 + } else if i <= firstBreak { + result._characters = self.counts._characters + result.prefix = self.counts.prefix - self.string._utf8Offset(of: i) + result._suffix = self.counts._suffix + } else { // i > firstBreak, i <= lastBreak + result._suffix = self.counts._suffix + let prevBreak = string[firstBreak..= i) + result.characters = characters + 1 + result.prefix = string.utf8.distance(from: i, to: first) + } else { + result.characters = 1 + result.prefix = string.utf8.distance(from: i, to: lastBreak) + } + } + return result + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Chunk/BigString+Chunk.swift b/Sources/RopeModule/BigString/Chunk/BigString+Chunk.swift new file mode 100644 index 000000000..530966e19 --- /dev/null +++ b/Sources/RopeModule/BigString/Chunk/BigString+Chunk.swift @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + internal struct _Chunk { + typealias Slice = (string: Substring, characters: Int, prefix: Int, suffix: Int) + + var string: String + var counts: Counts + + init() { + self.string = "" + self.counts = Counts() + } + + init(_ string: String, _ counts: Counts) { + self.string = string + self.string.makeContiguousUTF8() + self.counts = counts + invariantCheck() + } + + init(_ string: Substring, _ counts: Counts) { + self.string = String(string) + self.counts = counts + invariantCheck() + } + + init(_ slice: Slice) { + self.string = String(slice.string) + self.string.makeContiguousUTF8() + self.counts = Counts((self.string[...], slice.characters, slice.prefix, slice.suffix)) + invariantCheck() + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + @inline(__always) + static var maxUTF8Count: Int { 255 } + + @inline(__always) + static var minUTF8Count: Int { maxUTF8Count / 2 - maxSlicingError } + + @inline(__always) + static var maxSlicingError: Int { 3 } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + @inline(__always) + mutating func take() -> Self { + let r = self + self = Self() + return r + } + + @inline(__always) + mutating func modify( + _ body: (inout Self) -> R + ) -> R { + body(&self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + @inline(__always) + var characterCount: Int { counts.characters } + + @inline(__always) + var unicodeScalarCount: Int { Int(counts.unicodeScalars) } + + @inline(__always) + var utf16Count: Int { Int(counts.utf16) } + + @inline(__always) + var utf8Count: Int { Int(counts.utf8) } + + @inline(__always) + var prefixCount: Int { counts.prefix } + + @inline(__always) + var suffixCount: Int { counts.suffix } + + var firstScalar: UnicodeScalar { string.unicodeScalars.first! } + var lastScalar: UnicodeScalar { string.unicodeScalars.last! } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + var availableSpace: Int { Swift.max(0, Self.maxUTF8Count - utf8Count) } + + mutating func clear() { + string = "" + counts = Counts() + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + func hasSpaceToMerge(_ other: some StringProtocol) -> Bool { + utf8Count + other.utf8.count <= Self.maxUTF8Count + } + + func hasSpaceToMerge(_ other: Self) -> Bool { + utf8Count + other.utf8Count <= Self.maxUTF8Count + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+BidirectionalCollection.swift b/Sources/RopeModule/BigString/Conformances/BigString+BidirectionalCollection.swift new file mode 100644 index 000000000..af7d64a40 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+BidirectionalCollection.swift @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: BidirectionalCollection { + public typealias SubSequence = BigSubstring + + public var isEmpty: Bool { + _rope.summary.isZero + } + + public var startIndex: Index { + Index(_utf8Offset: 0)._knownCharacterAligned() + } + + public var endIndex: Index { + Index(_utf8Offset: _utf8Count)._knownCharacterAligned() + } + + public var count: Int { _characterCount } + + @inline(__always) + public func index(after i: Index) -> Index { + _characterIndex(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + _characterIndex(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + _characterIndex(i, offsetBy: distance) + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _characterIndex(i, offsetBy: distance, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + _characterDistance(from: start, to: end) + } + + public subscript(position: Index) -> Character { + self[_character: position] + } + + public subscript(bounds: Range) -> BigSubstring { + BigSubstring(self, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public func index(roundingDown i: Index) -> Index { + _characterIndex(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + _characterIndex(roundingUp: i) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+Comparable.swift b/Sources/RopeModule/BigString/Conformances/BigString+Comparable.swift new file mode 100644 index 000000000..69a349448 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+Comparable.swift @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: Comparable { + public static func < (left: Self, right: Self) -> Bool { + // FIXME: Implement properly normalized comparisons & hashing. + // This is somewhat tricky as we shouldn't just normalize individual pieces of the string + // split up on random Character boundaries -- Unicode does not promise that + // norm(a + c) == norm(a) + norm(b) in this case. + // To do this properly, we'll probably need to expose new stdlib entry points. :-/ + if left.isIdentical(to: right) { return false } + // FIXME: Even if we keep doing characterwise comparisons, we should skip over shared subtrees. + var it1 = left.makeIterator() + var it2 = right.makeIterator() + while true { + switch (it1.next(), it2.next()) { + case (nil, nil): return false + case (nil, .some): return true + case (.some, nil): return false + case let (a?, b?): + if a == b { continue } + return a < b + } + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + /// Lexicographically compare the UTF-8 representations of `left` to `right`, returning a Boolean + /// value indicating whether `left` is ordered before `right`. + internal func utf8IsLess(than other: Self) -> Bool { + if self.isIdentical(to: other) { return false } + + // FIXME: Implement a structural comparison that ignores shared subtrees when possible. + // This is somewhat tricky as this is only possible to do when the shared subtrees start + // at the same logical position in both trees. Additionally, the two input trees may not + // have the same height, even if they are equal. + + var it1 = self.makeChunkIterator() + var it2 = other.makeChunkIterator() + var s1: Substring = "" + var s2: Substring = "" + while true { + if s1.isEmpty { + s1 = it1.next()?[...] ?? "" + } + if s2.isEmpty { + s2 = it2.next()?[...] ?? "" + } + if s1.isEmpty { + return !s2.isEmpty + } + if s2.isEmpty { + return false + } + let c = Swift.min(s1.utf8.count, s2.utf8.count) + assert(c > 0) + let r: Bool? = s1.withUTF8 { b1 in + s2.withUTF8 { b2 in + for i in 0 ..< c { + let u1 = b1[i] + let u2 = b2[i] + if u1 < u2 { return true } + if u1 > u2 { return false } + } + return nil + } + } + if let r = r { return r } + s1 = s1.suffix(from: s1._utf8Index(at: c)) + s2 = s2.suffix(from: s2._utf8Index(at: c)) + } + } +} + +#endif diff --git a/Sources/BitCollections/BitArray/BitArray+CustomDebugStringConvertible.swift b/Sources/RopeModule/BigString/Conformances/BigString+CustomDebugStringConvertible.swift similarity index 64% rename from Sources/BitCollections/BitArray/BitArray+CustomDebugStringConvertible.swift rename to Sources/RopeModule/BigString/Conformances/BigString+CustomDebugStringConvertible.swift index f8502d2e7..f5baca1f7 100644 --- a/Sources/BitCollections/BitArray/BitArray+CustomDebugStringConvertible.swift +++ b/Sources/RopeModule/BigString/Conformances/BigString+CustomDebugStringConvertible.swift @@ -2,16 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2023 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 // //===----------------------------------------------------------------------===// -extension BitArray: CustomDebugStringConvertible { - /// A textual representation of this instance, suitable for debugging. +#if swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: CustomDebugStringConvertible { public var debugDescription: String { - "BitArray(\(_bitString))" + description.debugDescription } } + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+CustomStringConvertible.swift b/Sources/RopeModule/BigString/Conformances/BigString+CustomStringConvertible.swift new file mode 100644 index 000000000..6bec87926 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+CustomStringConvertible.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: CustomStringConvertible { + public var description: String { + String(self) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+Equatable.swift b/Sources/RopeModule/BigString/Conformances/BigString+Equatable.swift new file mode 100644 index 000000000..87f8cc70b --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+Equatable.swift @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public func isIdentical(to other: Self) -> Bool { + self._rope.isIdentical(to: other._rope) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + // FIXME: Implement properly normalized comparisons & hashing. + // This is somewhat tricky as we shouldn't just normalize individual pieces of the string + // split up on random Character boundaries -- Unicode does not promise that + // norm(a + c) == norm(a) + norm(b) in this case. + // To do this properly, we'll probably need to expose new stdlib entry points. :-/ + if left.isIdentical(to: right) { return true } + guard left._characterCount == right._characterCount else { return false } + // FIXME: Even if we keep doing characterwise comparisons, we should skip over shared subtrees. + var it1 = left.makeIterator() + var it2 = right.makeIterator() + var a: Character? = nil + var b: Character? = nil + repeat { + a = it1.next() + b = it2.next() + guard a == b else { return false } + } while a != nil + return true + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + /// Lexicographically compare the UTF-8 representations of `left` to `right`, returning a Boolean + /// value indicating whether `left` is equal to `right`. + internal static func utf8IsEqual(_ left: Self, to right: Self) -> Bool { + if left.isIdentical(to: right) { return true } + guard left._rope.summary == right._rope.summary else { return false } + + // FIXME: Implement a structural comparison that ignores shared subtrees when possible. + // This is somewhat tricky as this is only possible to do when the shared subtrees start + // at the same logical position in both trees. Additionally, the two input trees may not + // have the same height, even if they are equal. + + var it1 = left.utf8.makeIterator() + var it2 = right.utf8.makeIterator() + var remaining = left._utf8Count + + while remaining > 0 { + let consumed = it1.next(maximumCount: remaining) { b1 in + let consumed = it2.next(maximumCount: b1.count) { b2 in + guard b2.elementsEqual(b1.prefix(b2.count)) else { return (0, 0) } + return (b2.count, b2.count) + } + return (consumed, consumed) + } + guard consumed > 0 else { return false } + remaining -= consumed + } + return true + } + + internal static func utf8IsEqual( + _ left: Self, + in leftRange: Range, + to right: Self, + in rightRange: Range + ) -> Bool { + let leftUTF8Count = left._utf8Distance(from: leftRange.lowerBound, to: leftRange.upperBound) + let rightUTF8Count = right._utf8Distance(from: rightRange.lowerBound, to: rightRange.upperBound) + guard leftUTF8Count == rightUTF8Count else { return false } + + var remaining = leftUTF8Count + var it1 = BigString.UTF8View.Iterator(_base: left, from: leftRange.lowerBound) + var it2 = BigString.UTF8View.Iterator(_base: right, from: rightRange.lowerBound) + + while remaining > 0 { + let consumed = it1.next(maximumCount: remaining) { b1 in + let consumed = it2.next(maximumCount: b1.count) { b2 in + guard b2.elementsEqual(b1.prefix(b2.count)) else { return (0, 0) } + return (b2.count, b2.count) + } + return (consumed, consumed) + } + guard consumed > 0 else { return false } + remaining -= consumed + } + return true + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+ExpressibleByStringLiteral.swift b/Sources/RopeModule/BigString/Conformances/BigString+ExpressibleByStringLiteral.swift new file mode 100644 index 000000000..c804216ef --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+ExpressibleByStringLiteral.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+Hashing.swift b/Sources/RopeModule/BigString/Conformances/BigString+Hashing.swift new file mode 100644 index 000000000..958084f26 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+Hashing.swift @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: Hashable { + public func hash(into hasher: inout Hasher) { + hashCharacters(into: &hasher) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + internal func hashCharacters(into hasher: inout Hasher) { + // FIXME: Implement properly normalized comparisons & hashing. + // This is somewhat tricky as we shouldn't just normalize individual pieces of the string + // split up on random Character boundaries -- Unicode does not promise that + // norm(a + c) == norm(a) + norm(b) in this case. + // To do this properly, we'll probably need to expose new stdlib entry points. :-/ + var it = self.makeIterator() + while let character = it.next() { + let s = String(character) + s._withNFCCodeUnits { hasher.combine($0) } + } + hasher.combine(0xFF as UInt8) + } + + /// Feed the UTF-8 encoding of `self` into hasher, with a terminating byte. + internal func hashUTF8(into hasher: inout Hasher) { + for chunk in self._rope { + var string = chunk.string + string.withUTF8 { + hasher.combine(bytes: .init($0)) + } + } + hasher.combine(0xFF as UInt8) + } + + /// Feed the UTF-8 encoding of `self[start..=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: LosslessStringConvertible { + // init?(_: String) is implemented by RangeReplaceableCollection.init(_:) +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+RangeReplaceableCollection.swift b/Sources/RopeModule/BigString/Conformances/BigString+RangeReplaceableCollection.swift new file mode 100644 index 000000000..8a63558f1 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+RangeReplaceableCollection.swift @@ -0,0 +1,227 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: RangeReplaceableCollection { + public init() { + self.init(_rope: _Rope()) + } + + public mutating func reserveCapacity(_ n: Int) { + // Do nothing. + } + + public mutating func replaceSubrange>( // Note: Sequence, not Collection + _ subrange: Range, with newElements: __owned S + ) { + if S.self == String.self { + let newElements = _identityCast(newElements, to: String.self) + self._replaceSubrange(subrange, with: newElements) + } else if S.self == Substring.self { + let newElements = _identityCast(newElements, to: Substring.self) + self._replaceSubrange(subrange, with: newElements) + } else if S.self == BigString.self { + let newElements = _identityCast(newElements, to: BigString.self) + self._replaceSubrange(subrange, with: newElements) + } else if S.self == BigSubstring.self { + let newElements = _identityCast(newElements, to: BigSubstring.self) + self._replaceSubrange(subrange, with: newElements) + } else { + self._replaceSubrange(subrange, with: BigString(newElements)) + } + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned String + ) { + _replaceSubrange(subrange, with: newElements) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned Substring + ) { + _replaceSubrange(subrange, with: newElements) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned BigString + ) { + _replaceSubrange(subrange, with: newElements) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned BigSubstring + ) { + _replaceSubrange(subrange, with: newElements) + } + + public init>(_ elements: S) { + if S.self == String.self { + let elements = _identityCast(elements, to: String.self) + self.init(_from: elements) + } else if S.self == Substring.self { + let elements = _identityCast(elements, to: Substring.self) + self.init(_from: elements) + } else if S.self == BigString.self { + self = _identityCast(elements, to: BigString.self) + } else if S.self == BigSubstring.self { + let elements = _identityCast(elements, to: BigSubstring.self) + self.init(_from: elements) + } else { + self.init(_from: elements) + } + } + + public init(_ elements: String) { + self.init(_from: elements) + } + + public init(_ elements: Substring) { + self.init(_from: elements) + } + + public init(_ elements: BigString) { + self = elements + } + + public init(_ elements: BigSubstring) { + self.init(_from: elements) + } + + public init(repeating repeatedValue: Character, count: Int) { + self.init(repeating: BigString(String(repeatedValue)), count: count) + } + + public init(repeating repeatedValue: some StringProtocol, count: Int) { + self.init(repeating: BigString(repeatedValue), count: count) + } + + public init(repeating value: Self, count: Int) { + precondition(count >= 0, "Negative count") + guard count > 0 else { + self.init() + return + } + self.init() + var c = 0 + + var piece = value + var current = 1 + + while c < count { + if count & current != 0 { + self.append(contentsOf: piece) + c |= current + } + piece.append(contentsOf: piece) + current *= 2 + } + } + + public init(repeating value: BigSubstring, count: Int) { + self.init(repeating: BigString(value), count: count) + } + + public mutating func append(_ newElement: __owned Character) { + append(contentsOf: String(newElement)) + } + + public mutating func append>(contentsOf newElements: __owned S) { + if S.self == String.self { + let newElements = _identityCast(newElements, to: String.self) + append(contentsOf: newElements) + } else if S.self == Substring.self { + let newElements = _identityCast(newElements, to: Substring.self) + append(contentsOf: newElements) + } else if S.self == BigString.self { + let newElements = _identityCast(newElements, to: BigString.self) + append(contentsOf: newElements) + } else if S.self == BigSubstring.self { + let newElements = _identityCast(newElements, to: BigSubstring.self) + append(contentsOf: newElements) + } else { + append(contentsOf: BigString(newElements)) + } + } + + public mutating func append(contentsOf newElements: __owned String) { + _append(contentsOf: newElements[...]) + } + + public mutating func append(contentsOf newElements: __owned Substring) { + _append(contentsOf: newElements) + } + + public mutating func append(contentsOf newElements: __owned BigString) { + _append(contentsOf: newElements) + } + + public mutating func append(contentsOf newElements: __owned BigSubstring) { + _append(contentsOf: newElements._base, in: newElements._bounds) + } + + public mutating func insert(_ newElement: Character, at i: Index) { + insert(contentsOf: String(newElement), at: i) + } + + public mutating func insert>( // Note: Sequence, not Collection + contentsOf newElements: __owned S, at i: Index + ) { + if S.self == String.self { + let newElements = _identityCast(newElements, to: String.self) + insert(contentsOf: newElements, at: i) + } else if S.self == Substring.self { + let newElements = _identityCast(newElements, to: Substring.self) + insert(contentsOf: newElements, at: i) + } else if S.self == BigString.self { + let newElements = _identityCast(newElements, to: BigString.self) + insert(contentsOf: newElements, at: i) + } else if S.self == BigSubstring.self { + let newElements = _identityCast(newElements, to: BigSubstring.self) + insert(contentsOf: newElements, at: i) + } else { + insert(contentsOf: BigString(newElements), at: i) + } + } + + public mutating func insert(contentsOf newElements: __owned String, at i: Index) { + _insert(contentsOf: newElements[...], at: i) + } + + public mutating func insert(contentsOf newElements: __owned Substring, at i: Index) { + _insert(contentsOf: newElements, at: i) + } + + public mutating func insert(contentsOf newElements: __owned BigString, at i: Index) { + _insert(contentsOf: newElements, at: i) + } + + public mutating func insert(contentsOf newElements: __owned BigSubstring, at i: Index) { + _insert(contentsOf: newElements._base, in: newElements._bounds, at: i) + } + + @discardableResult + public mutating func remove(at i: Index) -> Character { + removeCharacter(at: i) + } + + public mutating func removeSubrange(_ bounds: Range) { + _removeSubrange(bounds) + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { + self = BigString() + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+Sequence.swift b/Sources/RopeModule/BigString/Conformances/BigString+Sequence.swift new file mode 100644 index 000000000..4987e5021 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+Sequence.swift @@ -0,0 +1,229 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: Sequence { + public typealias Element = Character + + public func makeIterator() -> Iterator { + Iterator(self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public struct Iterator { + internal let _base: BigString + internal var _utf8BaseOffset: Int + internal var _ropeIndex: _Rope.Index + internal var _chunkIndex: String.Index + internal var _next: String.Index + + internal init(_ string: BigString) { + self._base = string + self._ropeIndex = string._rope.startIndex + string._rope.grease(&_ropeIndex) + + self._utf8BaseOffset = 0 + guard _ropeIndex < string._rope.endIndex else { + _chunkIndex = "".startIndex + _next = "".endIndex + return + } + let chunk = _base._rope[_ropeIndex] + assert(chunk.firstBreak == chunk.string.startIndex) + self._chunkIndex = chunk.firstBreak + self._next = chunk.string[_chunkIndex...].index(after: _chunkIndex) + } + + internal init( + _ string: BigString, + from start: Index + ) { + self._base = string + self._utf8BaseOffset = start.utf8Offset + + if start == string.endIndex { + self._ropeIndex = string._rope.endIndex + self._chunkIndex = "".startIndex + self._next = "".endIndex + return + } + let i = string.resolve(start, preferEnd: false) + self._ropeIndex = i._rope! + self._utf8BaseOffset = i._utf8BaseOffset + self._chunkIndex = i._chunkIndex + self._next = _base._rope[_ropeIndex].wholeCharacters.index(after: _chunkIndex) + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Iterator: IteratorProtocol { + public typealias Element = Character + + internal var isAtEnd: Bool { + _chunkIndex == _next + } + + internal var isAtStart: Bool { + _ropeIndex == _base._rope.startIndex && _chunkIndex._utf8Offset == 0 + } + + internal var current: Element { + assert(!isAtEnd) + let chunk = _base._rope[_ropeIndex] + var str = String(chunk.string[_chunkIndex ..< _next]) + if _next < chunk.string.endIndex { return Character(str) } + + var i = _base._rope.index(after: _ropeIndex) + while i < _base._rope.endIndex { + let chunk = _base._rope[i] + let b = chunk.firstBreak + str += chunk.string[.. Bool { + guard !isAtEnd else { return false } + let chunk = _base._rope[_ropeIndex] + if _next < chunk.string.endIndex { + _chunkIndex = _next + _next = chunk.wholeCharacters.index(after: _next) + return true + } + var baseOffset = _utf8BaseOffset + chunk.utf8Count + var i = _base._rope.index(after: _ropeIndex) + while i < _base._rope.endIndex { + let chunk = _base._rope[i] + let b = chunk.firstBreak + if b < chunk.string.endIndex { + _ropeIndex = i + _utf8BaseOffset = baseOffset + _chunkIndex = b + _next = chunk.string[b...].index(after: b) + return true + } + baseOffset += chunk.utf8Count + _base._rope.formIndex(after: &i) + } + return false + } + + mutating func stepBackward() -> Bool { + if !isAtEnd { + let chunk = _base._rope[_ropeIndex] + let i = chunk.firstBreak + if _chunkIndex > i { + _next = _chunkIndex + _chunkIndex = chunk.string[i...].index(before: _chunkIndex) + return true + } + } + var i = _ropeIndex + var baseOffset = _utf8BaseOffset + while i > _base._rope.startIndex { + _base._rope.formIndex(before: &i) + let chunk = _base._rope[i] + baseOffset -= chunk.utf8Count + if chunk.hasBreaks { + _ropeIndex = i + _utf8BaseOffset = baseOffset + _next = chunk.string.endIndex + _chunkIndex = chunk.lastBreak + return true + } + } + return false + } + + public mutating func next() -> Character? { + guard !isAtEnd else { return nil } + let item = self.current + if !stepForward() { + _chunkIndex = _next + } + return item + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.Iterator { + // The UTF-8 offset of the current position, from the start of the string. + var utf8Offset: Int { + _utf8BaseOffset + _chunkIndex._utf8Offset + } + + var index: BigString.Index { + BigString.Index(baseUTF8Offset: _utf8BaseOffset, _rope: _ropeIndex, chunk: _chunkIndex) + } + + internal var nextIndex: BigString.Index { + assert(!isAtEnd) + let chunk = _base._rope[_ropeIndex] + if _next < chunk.string.endIndex { + return BigString.Index(baseUTF8Offset: _utf8BaseOffset, _rope: _ropeIndex, chunk: _next) + } + var i = _base._rope.index(after: _ropeIndex) + var base = _utf8BaseOffset + while i < _base._rope.endIndex { + let chunk = _base._rope[i] + let b = chunk.firstBreak + if b < chunk.string.endIndex { + return BigString.Index(baseUTF8Offset: base, _rope: i, chunk: b) + } + base += chunk.utf8Count + _base._rope.formIndex(after: &i) + } + return _base.endIndex + } + + /// The UTF-8 offset range of the current character, measured from the start of the string. + var utf8Range: Range { + assert(!isAtEnd) + let start = utf8Offset + var end = _utf8BaseOffset + var chunk = _base._rope[_ropeIndex] + if _next < chunk.string.endIndex { + end += _next._utf8Offset + return Range(uncheckedBounds: (start, end)) + } + end += chunk.utf8Count + var i = _base._rope.index(after: _ropeIndex) + while i < _base._rope.endIndex { + chunk = _base._rope[i] + let b = chunk.firstBreak + if b < chunk.string.endIndex { + end += b._utf8Offset + break + } + end += chunk.utf8Count + _base._rope.formIndex(after: &i) + } + return Range(uncheckedBounds: (start, end)) + } + + func isAbove(_ index: BigString.Index) -> Bool { + self.utf8Offset > index.utf8Offset + } + + func isBelow(_ index: BigString.Index) -> Bool { + self.utf8Offset < index.utf8Offset + } +} + + +#endif diff --git a/Sources/RopeModule/BigString/Conformances/BigString+TextOutputStream.swift b/Sources/RopeModule/BigString/Conformances/BigString+TextOutputStream.swift new file mode 100644 index 000000000..3fb8007a4 --- /dev/null +++ b/Sources/RopeModule/BigString/Conformances/BigString+TextOutputStream.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: TextOutputStream { + public mutating func write(_ string: String) { + append(contentsOf: string) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString: TextOutputStreamable { + public func write(to target: inout some TextOutputStream) { + for chunk in _rope { + chunk.string.write(to: &target) + } + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+Append.swift b/Sources/RopeModule/BigString/Operations/BigString+Append.swift new file mode 100644 index 000000000..8c0bb36d3 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+Append.swift @@ -0,0 +1,198 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _append(contentsOf other: __owned Substring) { + if other.isEmpty { return } + if isEmpty { + self = Self(other) + return + } + var ingester = _ingester(forInserting: other, at: endIndex, allowForwardPeek: true) + + let last = _rope.index(before: _rope.endIndex) + if let final = _rope[last].append(from: &ingester) { + precondition(!final.isUndersized) + _rope.append(final) + return + } + + // Make a temp rope out of the rest of the chunks and then join the two trees together. + if !ingester.isAtEnd { + var builder = _Rope.Builder() + while let chunk = ingester.nextWellSizedChunk() { + precondition(!chunk.isUndersized) + builder.insertBeforeTip(chunk) + } + precondition(ingester.isAtEnd) + _rope = _Rope.join(_rope, builder.finalize()) + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + var _firstUnicodeScalar: Unicode.Scalar { + assert(!isEmpty) + return _rope.root.firstItem.value.string.unicodeScalars.first! + } + + mutating func _append(contentsOf other: __owned BigString) { + guard !other.isEmpty else { return } + guard !self.isEmpty else { + self = other + return + } + + let hint = other._firstUnicodeScalar + var other = other._rope + var old = _CharacterRecognizer() + var new = self._breakState(upTo: endIndex, nextScalarHint: hint) + _ = other.resyncBreaks(old: &old, new: &new) + _append(other) + } + + mutating func _append(contentsOf other: __owned BigString, in range: Range) { + guard !range._isEmptyUTF8 else { return } + guard !self.isEmpty else { + self = Self(_from: other, in: range) + return + } + + var other = BigString(_from: other, in: range) + let hint = other._firstUnicodeScalar + var old = _CharacterRecognizer() + var new = self._breakState(upTo: endIndex, nextScalarHint: hint) + _ = other._rope.resyncBreaks(old: &old, new: &new) + _append(other._rope) + } + + mutating func prepend(contentsOf other: __owned BigString) { + guard !other.isEmpty else { return } + guard !self.isEmpty else { + self = other + return + } + let hint = self._firstUnicodeScalar + var old = _CharacterRecognizer() + var new = other._breakState(upTo: other.endIndex, nextScalarHint: hint) + _ = self._rope.resyncBreaks(old: &old, new: &new) + _prepend(other._rope) + } + + mutating func prepend(contentsOf other: __owned BigString, in range: Range) { + guard !range._isEmptyUTF8 else { return } + let extract = Self(_from: other, in: range) + guard !self.isEmpty else { + self = extract + return + } + let hint = self._firstUnicodeScalar + var old = _CharacterRecognizer() + var new = extract._breakState(upTo: extract.endIndex, nextScalarHint: hint) + _ = self._rope.resyncBreaks(old: &old, new: &new) + _prepend(extract._rope) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + var isUndersized: Bool { + _utf8Count < _Chunk.minUTF8Count + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + /// Note: This assumes `other` already has the correct break positions. + mutating func _append(_ other: __owned _Chunk) { + assert(!other.isEmpty) + guard !self.isEmpty else { + self._rope.append(other) + return + } + guard self.isUndersized || other.isUndersized else { + self._rope.append(other) + return + } + var other = other + let last = self._rope.index(before: self._rope.endIndex) + if !self._rope[last].rebalance(nextNeighbor: &other) { + assert(!other.isUndersized) + self._rope.append(other) + } + } + + /// Note: This assumes `self` and `other` already have the correct break positions. + mutating func _prepend(_ other: __owned _Chunk) { + assert(!other.isEmpty) + guard !self.isEmpty else { + self._rope.prepend(other) + return + } + guard self.isUndersized || other.isUndersized else { + self._rope.prepend(other) + return + } + var other = other + let first = self._rope.startIndex + if !self._rope[first].rebalance(prevNeighbor: &other) { + self._rope.prepend(other) + } + } + + /// Note: This assumes `other` already has the correct break positions. + mutating func _append(_ other: __owned _Rope) { + guard !other.isEmpty else { return } + guard !self._rope.isEmpty else { + self._rope = other + return + } + if other.isSingleton { + self._append(other.first!) + return + } + if self.isUndersized { + assert(self._rope.isSingleton) + let chunk = self._rope.first! + self._rope = other + self._prepend(chunk) + return + } + self._rope = _Rope.join(self._rope, other) + } + + /// Note: This assumes `self` and `other` already have the correct break positions. + mutating func _prepend(_ other: __owned _Rope) { + guard !other.isEmpty else { return } + guard !self.isEmpty else { + self._rope = other + return + } + if other.isSingleton { + self._prepend(other.first!) + return + } + if self.isUndersized { + assert(self._rope.isSingleton) + let chunk = self._rope.first! + self._rope = other + self._append(chunk) + return + } + self._rope = _Rope.join(other, self._rope) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+Initializers.swift b/Sources/RopeModule/BigString/Operations/BigString+Initializers.swift new file mode 100644 index 000000000..37b045e41 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+Initializers.swift @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + internal init(_from input: some StringProtocol) { + var builder = _Rope.Builder() + var ingester = _Ingester(input) + while let chunk = ingester.nextWellSizedChunk() { + builder.insertBeforeTip(chunk) + } + self._rope = builder.finalize() + } + + internal init(_from input: some Sequence) { + var builder = Builder() + var piece = "" + for character in input { + piece.append(character) + if piece.utf8.count >= _Chunk.minUTF8Count { + builder.append(piece) + piece = "" + } + } + builder.append(piece) + self = builder.finalize() + } + + internal init(_from input: some Sequence) { + var builder = Builder() + var piece = "" + for scalar in input { + piece.unicodeScalars.append(scalar) + if piece.utf8.count >= _Chunk.minUTF8Count { + builder.append(piece) + piece = "" + } + } + builder.append(piece) + self = builder.finalize() + } + + internal init(_from other: BigSubstring) { + self.init(_from: other._base, in: other._bounds) + } + + internal init(_from other: BigSubstring.UnicodeScalarView) { + self.init(_from: other._base, in: other._bounds) + } + + internal init( + _from other: Self, + in range: Range + ) { + self._rope = other._rope.extract( + from: range.lowerBound.utf8Offset, + to: range.upperBound.utf8Offset, + in: _UTF8Metric()) + var old = other._breakState(upTo: range.lowerBound) + var new = _CharacterRecognizer() + _ = self._rope.resyncBreaks(old: &old, new: &new) + } + + internal init( + _ other: Self, + in range: Range, + state: inout _CharacterRecognizer + ) { + self._rope = other._rope.extract( + from: range.lowerBound.utf8Offset, + to: range.upperBound.utf8Offset, + in: _UTF8Metric()) + var old = other._breakState(upTo: range.lowerBound) + var new = state + self._rope.resyncBreaksToEnd(old: &old, new: &new) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension String { + public init(_ big: BigString) { + guard !big.isEmpty else { + self.init() + return + } + if big._rope.isSingleton { + self = big._rope[big._rope.startIndex].string + return + } + self.init() + self.reserveCapacity(big._utf8Count) + for chunk in big._rope { + self.append(chunk.string) + } + } + + internal init(_from big: BigString, in range: Range) { + self.init() + + var start = big._unicodeScalarIndex(roundingDown: range.lowerBound) + start = big.resolve(start, preferEnd: false) + + var end = big._unicodeScalarIndex(roundingDown: range.upperBound) + end = big.resolve(end, preferEnd: true) + + let utf8Capacity = end.utf8Offset - start.utf8Offset + guard utf8Capacity > 0 else { return } + + self.reserveCapacity(utf8Capacity) + + let startRopeIndex = start._rope! + let endRopeIndex = end._rope! + if startRopeIndex == endRopeIndex { + self += big._rope[startRopeIndex].string[start._chunkIndex ..< end._chunkIndex] + return + } + + self += big._rope[startRopeIndex].string[start._chunkIndex...] + var i = big._rope.index(after: startRopeIndex) + while i < endRopeIndex { + self += big._rope[i].string + big._rope.formIndex(after: &i) + } + self += big._rope[endRopeIndex].string[..=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _insert( + contentsOf other: __owned Substring, + at index: Index + ) { + precondition(index <= endIndex, "Index out of bounds") + if other.isEmpty { return } + if index == endIndex { + self.append(contentsOf: other) + return + } + + // Note: we must disable the forward peeking optimization as we'll need to know the actual + // full grapheme breaking state of the ingester at the start of the insertion. + // (We'll use it to resync grapheme breaks in the parts that follow the insertion.) + let index = self.resolve(index, preferEnd: true) + var ingester = _ingester(forInserting: other, at: index, allowForwardPeek: false) + var ri = index._rope! + var ci = index._chunkIndex + let r = _rope.update(at: &ri) { $0.insert(from: &ingester, at: ci) } + switch r { + case let .inline(r): + assert(ingester.isAtEnd) + guard var r = r else { break } + ci = String.Index(_utf8Offset: ci._utf8Offset + r.increment) + let i = Index(baseUTF8Offset: index._utf8BaseOffset, _rope: ri, chunk: ci) + resyncBreaks(startingAt: i, old: &r.old, new: &r.new) + case let .split(spawn: spawn, endStates: r): + assert(ingester.isAtEnd) + _rope.formIndex(after: &ri) + _rope.insert(spawn, at: ri) + guard var r = r else { break } + let i = Index(_utf8Offset: index.utf8Offset + r.increment, _rope: ri, chunkOffset: spawn.utf8Count) + resyncBreaks(startingAt: i, old: &r.old, new: &r.new) + case .large: + var builder = self.split(at: index, state: ingester.state) + builder.append(from: &ingester) + self = builder.finalize() + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _insert(contentsOf other: __owned Self, at index: Index) { + guard index < endIndex else { + precondition(index == endIndex, "Index out of bounds") + self.append(contentsOf: other) + return + } + guard index > startIndex else { + self.prepend(contentsOf: other) + return + } + if other._rope.isSingleton { + // Fast path when `other` is tiny. + let chunk = other._rope.first! + insert(contentsOf: chunk.string, at: index) + return + } + var builder = self.split(at: index) + builder.append(other) + self = builder.finalize() + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _insert(contentsOf other: __owned Self, in range: Range, at index: Index) { + guard index < endIndex else { + precondition(index == endIndex, "Index out of bounds") + self._append(contentsOf: other, in: range) + return + } + guard index > startIndex else { + self.prepend(contentsOf: other, in: range) + return + } + guard !range._isEmptyUTF8 else { return } + // FIXME: Fast path when `other` is tiny. + var builder = self.split(at: index) + builder.append(other, in: range) + self = builder.finalize() + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+Managing Breaks.swift b/Sources/RopeModule/BigString/Operations/BigString+Managing Breaks.swift new file mode 100644 index 000000000..44d70a102 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+Managing Breaks.swift @@ -0,0 +1,271 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + func _breakState( + upTo index: Index, + nextScalarHint: Unicode.Scalar? = nil + ) -> _CharacterRecognizer { + assert(index == _unicodeScalarIndex(roundingDown: index)) + guard index > startIndex else { + return _CharacterRecognizer() + } + let index = resolve(index, preferEnd: true) + let ropeIndex = index._rope! + let chunkIndex = index._chunkIndex + let chunk = _rope[ropeIndex] + + guard ropeIndex > _rope.startIndex || chunkIndex > chunk.string.startIndex else { + return _CharacterRecognizer() + } + + if let next = nextScalarHint, chunkIndex > chunk.string.startIndex { + let i = chunk.string.unicodeScalars.index(before: chunkIndex) + let prev = chunk.string.unicodeScalars[i] + if _CharacterRecognizer.quickBreak(between: prev, and: next) == true { + return _CharacterRecognizer() + } + } + + if let r = chunk.immediateBreakState(upTo: chunkIndex) { + return r.state + } + // Find chunk that includes the start of the character. + var ri = ropeIndex + while ri > _rope.startIndex { + _rope.formIndex(before: &ri) + if _rope[ri].hasBreaks { break } + } + precondition(ri < ropeIndex) + + // Collect grapheme breaking state. + var state = _CharacterRecognizer(partialCharacter: _rope[ri].suffix) + _rope.formIndex(after: &ri) + while ri < ropeIndex { + state.consumePartialCharacter(_rope[ri].string[...]) + _rope.formIndex(after: &ri) + } + state.consumePartialCharacter(chunk.string[.. (ropeIndex: _Rope.Index, chunkIndex: String.Index)? { + guard index < endIndex else { return nil } + let i = resolve(index, preferEnd: false) + var ropeIndex = i._rope! + var chunkIndex = i._chunkIndex + let end = _rope.mutatingForEach(from: &ropeIndex) { chunk in + let start = chunkIndex + chunkIndex = String.Index(_utf8Offset: 0) + if let i = chunk.resyncBreaks(startingAt: start, old: &old, new: &new) { + return i + } + return nil + } + guard let end else { return nil } + return (ropeIndex, end) + } + + mutating func resyncBreaksToEnd( + startingAt index: Index, + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) { + guard let _ = resyncBreaks(startingAt: index, old: &old, new: &new) else { return } + let state = _breakState(upTo: endIndex) + old = state + new = state + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Rope { + mutating func resyncBreaks( + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) -> Bool { + _resyncBreaks(old: &old, new: &new) != nil + } + + mutating func _resyncBreaks( + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) -> (ropeIndex: Index, chunkIndex: String.Index)? { + var ropeIndex = startIndex + let chunkIndex = self.mutatingForEach(from: &ropeIndex) { + $0.resyncBreaksFromStart(old: &old, new: &new) + } + guard let chunkIndex else { return nil } + return (ropeIndex, chunkIndex) + } + + mutating func resyncBreaksToEnd( + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) { + guard let (ropeIndex, chunkIndex) = _resyncBreaks(old: &old, new: &new) else { return } + + let chars = self.summary.characters + if chars > 0 { + var i = endIndex + while i > ropeIndex { + formIndex(before: &i) + if self[i].hasBreaks { break } + } + if i > ropeIndex || self[i].lastBreak > chunkIndex { + new = self[i].immediateLastBreakState! + formIndex(after: &i) + while i < endIndex { + new.consumePartialCharacter(self[i].string[...]) + formIndex(after: &i) + } + old = new + return + } + } + + var ri = ropeIndex + let suffix = self[ri].string.unicodeScalars[chunkIndex...].dropFirst() + new.consumePartialCharacter(suffix) + formIndex(after: &ri) + while ri < endIndex { + new.consumePartialCharacter(self[ri].string[...]) + formIndex(after: &ri) + } + old = new + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString._Chunk { + /// Resyncronize chunk metadata with the (possibly) reshuffled grapheme + /// breaks after an insertion that ended at `index`. + /// + /// This assumes that the chunk's prefix and suffix counts have already + /// been adjusted to remain on Unicode scalar boundaries, and that they + /// are also in sync with the grapheme breaks up to `index`. If the + /// prefix ends after `index`, then this function may update it to address + /// the correct scalar. Similarly, the suffix count may be updated to + /// reflect the new position of the last grapheme break, if necessary. + mutating func resyncBreaks( + startingAt index: String.Index, + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) -> String.Index? { + var i = index + let u = string.unicodeScalars + assert(u._index(roundingDown: i) == i) + + // FIXME: Rewrite in terms of `firstBreak(in:)`. + var first: String.Index? = nil + var last: String.Index? = nil + loop: + while i < u.endIndex { + let scalar = u[i] + let a = old.hasBreak(before: scalar) + let b = new.hasBreak(before: scalar) + if b { + first = first ?? i + last = i + } + switch (a, b) { + case (true, true): + break loop // Resync complete ✨ + case (false, false): + if old._isKnownEqual(to: new) { break loop } + case (false, true): + counts._characters += 1 + case (true, false): + counts._characters -= 1 + } + u.formIndex(after: &i) + } + + // Grapheme breaks in `index...i` may have changed. Update `firstBreak` and `lastBreak` + // accordingly. + + assert((first == nil) == (last == nil)) + if index <= firstBreak { + if let first { + // We have seen the new first break. + firstBreak = first + } else if i >= firstBreak { + // The old firstBreak is no longer a break. + if i < lastBreak { + // The old lastBreak is still valid. Find the first break in i+1...endIndex. + let j = u.index(after: i) + var tmp = new + firstBreak = tmp.firstBreak(in: string[j...])!.lowerBound + } else { + // No breaks anywhere in the string. + firstBreak = u.endIndex + } + } + } + + if i >= lastBreak { + if let last { + // We have seen the new last break. + lastBreak = last + } else if firstBreak == u.endIndex { + // We already know there are no breaks anywhere in the string. + lastBreak = u.startIndex + } else { + // We have a `firstBreak`, but no break in `index...i`. + // Find last break in `firstBreak... String.Index? { + resyncBreaks(startingAt: string.startIndex, old: &old, new: &new) + } + + mutating func resyncBreaksFromStartToEnd( + old: inout _CharacterRecognizer, + new: inout _CharacterRecognizer + ) { + guard let i = resyncBreaks(startingAt: string.startIndex, old: &old, new: &new) else { + return + } + if i < lastBreak { + new = _CharacterRecognizer(partialCharacter: string[lastBreak...]) + } else { + //assert(old == new) + let j = string.unicodeScalars.index(after: i) + new.consumePartialCharacter(string[j...]) + } + old = new + return + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+RemoveSubrange.swift b/Sources/RopeModule/BigString/Operations/BigString+RemoveSubrange.swift new file mode 100644 index 000000000..0104f9e7f --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+RemoveSubrange.swift @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _removeSubrange(_ bounds: Range) { + precondition(bounds.upperBound <= endIndex, "Index out of bounds") + if bounds.isEmpty { return } + let lower = bounds.lowerBound.utf8Offset + let upper = bounds.upperBound.utf8Offset + _rope.removeSubrange(lower ..< upper, in: _UTF8Metric()) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func removeCharacter(at i: Index) -> Character { + let start = self.resolve(i, preferEnd: false) + let (character, end) = self._character(at: start) + self.removeSubrange(start ..< end) + return character + } + + mutating func removeUnicodeScalar(at i: Index) -> Unicode.Scalar { + precondition(i < endIndex, "Index out of bounds") + let start = _unicodeScalarIndex(roundingDown: i) + let ropeIndex = start._rope! + let chunkIndex = start._chunkIndex + let chunk = _rope[ropeIndex] + let scalar = chunk.string.unicodeScalars[chunkIndex] + let next = chunk.string.unicodeScalars.index(after: chunkIndex) + let end = Index(baseUTF8Offset: start._utf8BaseOffset, _rope: ropeIndex, chunk: next) + self.removeSubrange(start ..< end) + return scalar + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+ReplaceSubrange.swift b/Sources/RopeModule/BigString/Operations/BigString+ReplaceSubrange.swift new file mode 100644 index 000000000..c48e814f1 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+ReplaceSubrange.swift @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func _replaceSubrange(_ range: Range, with newElements: some StringProtocol) { + precondition(range.upperBound <= endIndex, "Index out of bounds") + if range.isEmpty { + insert(contentsOf: newElements, at: range.lowerBound) + return + } + var builder = _split(removing: range) + var ingester = _Ingester(Substring(newElements), startState: builder.prefixEndState) + builder.append(from: &ingester) + self = builder.finalize() + } + + mutating func _replaceSubrange(_ range: Range, with newElements: BigString) { + precondition(range.upperBound <= endIndex, "Index out of bounds") + if range.isEmpty { + insert(contentsOf: newElements, at: range.lowerBound) + return + } + var builder = _split(removing: range) + builder.append(newElements) + self = builder.finalize() + } + + mutating func _replaceSubrange( + _ targetRange: Range, + with newElements: BigSubstring + ) { + _replaceSubrange(targetRange, with: newElements._base, in: newElements._bounds) + } + + mutating func _replaceSubrange( + _ targetRange: Range, + with newElements: BigString, + in sourceRange: Range + ) { + if sourceRange._isEmptyUTF8 { + removeSubrange(targetRange) + return + } + if targetRange._isEmptyUTF8 { + _insert( + contentsOf: newElements, + in: sourceRange, + at: targetRange.lowerBound) + return + } + precondition(targetRange.upperBound <= endIndex, "Index out of bounds") + var builder = _split(removing: targetRange) + builder.append(newElements, in: sourceRange) + self = builder.finalize() + } + + mutating func _split(removing range: Range) -> Builder { + let lower = range.lowerBound.utf8Offset + let upper = range.upperBound.utf8Offset + + // FIXME: Don't split the indices twice -- they are currently split once here and once in the + // builder initializer below. + let startState = _breakState(upTo: range.lowerBound) + let endState = _breakState(upTo: range.upperBound) + + let b = _rope.builder(removing: lower ..< upper, in: _UTF8Metric()) + return Builder(base: b, prefixEndState: startState, suffixStartState: endState) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/BigString+Split.swift b/Sources/RopeModule/BigString/Operations/BigString+Split.swift new file mode 100644 index 000000000..13ddab000 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/BigString+Split.swift @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + mutating func split( + at index: Index + ) -> Builder { + let b = _ropeBuilder(at: index) + let state = b._breakState() + let builder = Builder(base: b, prefixEndState: state, suffixStartState: state) + return builder + } + + mutating func split( + at index: Index, + state: _CharacterRecognizer + ) -> Builder { + let b = _ropeBuilder(at: index) + let builder = Builder(base: b, prefixEndState: state, suffixStartState: state) + return builder + } + + mutating func _ropeBuilder(at index: Index) -> _Rope.Builder { + if let ri = index._rope, _rope.isValid(ri) { + return _rope.split(at: ri, index._chunkIndex) + } + return _rope.builder(splittingAt: index.utf8Offset, in: _UTF8Metric()) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Operations/Range+BigString.swift b/Sources/RopeModule/BigString/Operations/Range+BigString.swift new file mode 100644 index 000000000..6eb38a599 --- /dev/null +++ b/Sources/RopeModule/BigString/Operations/Range+BigString.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension Range { + internal var _isEmptyUTF8: Bool { + lowerBound.utf8Offset == upperBound.utf8Offset + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigString+UTF16View.swift b/Sources/RopeModule/BigString/Views/BigString+UTF16View.swift new file mode 100644 index 000000000..dab8a40f8 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigString+UTF16View.swift @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public struct UTF16View: Sendable { + var _base: BigString + + @inline(__always) + init(_base: BigString) { + self._base = _base + } + } + + @inline(__always) + public var utf16: UTF16View { + UTF16View(_base: self) + } + + public init(_ utf16: UTF16View) { + self = utf16._base + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + BigString.utf8IsEqual(left._base, to: right._base) + } + + public func isIdentical(to other: Self) -> Bool { + self._base.isIdentical(to: other._base) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View: Hashable { + public func hash(into hasher: inout Hasher) { + _base.hashUTF8(into: &hasher) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View: Sequence { + public typealias Element = UInt16 + + public struct Iterator { + internal let _base: BigString + internal var _index: BigString.Index + + internal init(_base: BigString, from start: BigString.Index) { + self._base = _base + self._index = _base._utf16Index(roundingDown: start) + } + } + + public func makeIterator() -> Iterator { + Iterator(_base: _base, from: startIndex) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View.Iterator: IteratorProtocol { + public typealias Element = UInt16 + + public mutating func next() -> UInt16? { + guard _index < _base.endIndex else { return nil } + let ri = _index._rope! + var ci = _index._chunkIndex + let chunk = _base._rope[ri] + let result = chunk.string.utf16[ci] + + chunk.string.utf16.formIndex(after: &ci) + if ci < chunk.string.endIndex { + _index = BigString.Index(baseUTF8Offset: _index._utf8BaseOffset, _rope: ri, chunk: ci) + } else { + _index = BigString.Index( + baseUTF8Offset: _index._utf8BaseOffset + chunk.utf8Count, + _rope: _base._rope.index(after: ri), + chunk: String.Index(_utf8Offset: 0)) + } + return result + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = BigSubstring.UTF16View + + @inline(__always) + public var startIndex: Index { _base.startIndex } + + @inline(__always) + public var endIndex: Index { _base.endIndex } + + public var count: Int { _base._utf16Count } + + @inline(__always) + public func index(after i: Index) -> Index { + _base._utf16Index(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + _base._utf16Index(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + _base._utf16Index(i, offsetBy: distance) + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _base._utf16Index(i, offsetBy: distance, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + _base._utf16Distance(from: start, to: end) + } + + public subscript(position: Index) -> UInt16 { + _base[_utf16: position] + } + + public subscript(bounds: Range) -> BigSubstring.UTF16View { + BigSubstring.UTF16View(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF16View { + public func index(roundingDown i: Index) -> Index { + _base._utf16Index(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + _base._utf16Index(roundingUp: i) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigString+UTF8View.swift b/Sources/RopeModule/BigString/Views/BigString+UTF8View.swift new file mode 100644 index 000000000..f4c8b74e3 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigString+UTF8View.swift @@ -0,0 +1,188 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public struct UTF8View: Sendable { + var _base: BigString + + @inline(__always) + init(_base: BigString) { + self._base = _base + } + } + + @inline(__always) + public var utf8: UTF8View { + UTF8View(_base: self) + } + + public init(_ utf8: UTF8View) { + self = utf8._base + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + BigString.utf8IsEqual(left._base, to: right._base) + } + + public func isIdentical(to other: Self) -> Bool { + self._base.isIdentical(to: other._base) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View: Hashable { + public func hash(into hasher: inout Hasher) { + _base.hashUTF8(into: &hasher) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View: Sequence { + public typealias Element = UInt8 + + public struct Iterator { + internal let _base: BigString + internal var _index: BigString.Index + + internal init(_base: BigString, from start: BigString.Index) { + self._base = _base + self._index = _base._utf8Index(roundingDown: start) + } + } + + public func makeIterator() -> Iterator { + Iterator(_base: _base, from: self.startIndex) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View.Iterator: IteratorProtocol { + public typealias Element = UInt8 + + public mutating func next() -> UInt8? { + guard _index < _base.endIndex else { return nil } + // Hand-optimized from `_base.subscript(utf8:)` and `_base.utf8Index(after:)`. + let ri = _index._rope! + var ci = _index._chunkIndex + let chunk = _base._rope[ri] + let result = chunk.string.utf8[ci] + + chunk.string.utf8.formIndex(after: &ci) + if ci < chunk.string.endIndex { + _index = BigString.Index(baseUTF8Offset: _index._utf8BaseOffset, _rope: ri, chunk: ci) + } else { + _index = BigString.Index( + baseUTF8Offset: _index._utf8BaseOffset + chunk.utf8Count, + _rope: _base._rope.index(after: ri), + chunk: String.Index(_utf8Offset: 0)) + } + return result + } + + public mutating func next( + maximumCount: Int, + with body: (UnsafeBufferPointer) -> (consumed: Int, result: R) + ) -> R { + guard _index < _base.endIndex else { + let r = body(UnsafeBufferPointer(start: nil, count: 0)) + precondition(r.consumed == 0) + return r.result + } + let ri = _index._rope! + var ci = _index._utf8ChunkOffset + var utf8Offset = _index.utf8Offset + var string = _base._rope[ri].string + let (haveMore, result) = string.withUTF8 { buffer in + let slice = buffer[ci...].prefix(maximumCount) + assert(!slice.isEmpty) + let (consumed, result) = body(UnsafeBufferPointer(rebasing: slice)) + precondition(consumed >= 0 && consumed <= slice.count) + utf8Offset += consumed + ci += consumed + return (ci < buffer.count, result) + } + if haveMore { + _index = BigString.Index(_utf8Offset: utf8Offset, _rope: ri, chunkOffset: ci) + } else { + _index = BigString.Index( + baseUTF8Offset: _index._utf8BaseOffset + string.utf8.count, + _rope: _base._rope.index(after: ri), + chunk: String.Index(_utf8Offset: 0)) + } + return result + } +} + + + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = BigSubstring.UTF8View + + @inline(__always) + public var startIndex: Index { _base.startIndex } + + @inline(__always) + public var endIndex: Index { _base.endIndex } + + public var count: Int { _base._utf8Count } + + @inline(__always) + public func index(after i: Index) -> Index { + _base._utf8Index(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + _base._utf8Index(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + _base._utf8Index(i, offsetBy: distance) + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _base._utf8Index(i, offsetBy: distance, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + _base._utf8Distance(from: start, to: end) + } + + public subscript(position: Index) -> UInt8 { + _base[_utf8: position] + } + + public subscript(bounds: Range) -> BigSubstring.UTF8View { + BigSubstring.UTF8View(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UTF8View { + public func index(roundingDown i: Index) -> Index { + _base._utf8Index(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + _base._utf8Index(roundingUp: i) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigString+UnicodeScalarView.swift b/Sources/RopeModule/BigString/Views/BigString+UnicodeScalarView.swift new file mode 100644 index 000000000..1c62d0add --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigString+UnicodeScalarView.swift @@ -0,0 +1,395 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public struct UnicodeScalarView: Sendable { + var _base: BigString + + @inline(__always) + init(_base: BigString) { + self._base = _base + } + } + + @inline(__always) + public var unicodeScalars: UnicodeScalarView { + get { + UnicodeScalarView(_base: self) + } + set { + self = newValue._base + } + _modify { + var view = UnicodeScalarView(_base: self) + self = .init() + defer { + self = view._base + } + yield &view + } + } + + public init(_ view: BigString.UnicodeScalarView) { + self = view._base + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value.unicodeScalars) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: CustomStringConvertible { + public var description: String { + String(_base) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: CustomDebugStringConvertible { + public var debugDescription: String { + description.debugDescription + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + BigString.utf8IsEqual(left._base, to: right._base) + } + + public func isIdentical(to other: Self) -> Bool { + self._base.isIdentical(to: other._base) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: Hashable { + public func hash(into hasher: inout Hasher) { + _base.hashUTF8(into: &hasher) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: Sequence { + public typealias Element = UnicodeScalar + + public struct Iterator { + internal let _base: BigString + internal var _index: BigString.Index + + internal init(_base: BigString, from start: BigString.Index) { + self._base = _base + self._index = _base._unicodeScalarIndex(roundingDown: start) + } + } + + public func makeIterator() -> Iterator { + Iterator(_base: self._base, from: self.startIndex) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView.Iterator: IteratorProtocol { + public typealias Element = UnicodeScalar + + public mutating func next() -> UnicodeScalar? { + guard _index < _base.endIndex else { return nil } + let ri = _index._rope! + var ci = _index._chunkIndex + let chunk = _base._rope[ri] + let result = chunk.string.unicodeScalars[ci] + + chunk.string.unicodeScalars.formIndex(after: &ci) + if ci < chunk.string.endIndex { + _index = BigString.Index(baseUTF8Offset: _index._utf8BaseOffset, _rope: ri, chunk: ci) + } else { + _index = BigString.Index( + baseUTF8Offset: _index._utf8BaseOffset + chunk.utf8Count, + _rope: _base._rope.index(after: ri), + chunk: String.Index(_utf8Offset: 0)) + } + return result + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = BigSubstring.UnicodeScalarView + + @inline(__always) + public var startIndex: Index { _base.startIndex } + + @inline(__always) + public var endIndex: Index { _base.endIndex } + + public var count: Int { _base._unicodeScalarCount } + + @inline(__always) + public func index(after i: Index) -> Index { + _base._unicodeScalarIndex(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + _base._unicodeScalarIndex(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + _base._unicodeScalarIndex(i, offsetBy: distance) + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + _base._unicodeScalarIndex(i, offsetBy: distance, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + _base._unicodeScalarDistance(from: start, to: end) + } + + public subscript(position: Index) -> UnicodeScalar { + _base[_unicodeScalar: position] + } + + public subscript(bounds: Range) -> BigSubstring.UnicodeScalarView { + BigSubstring.UnicodeScalarView(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView { + public func index(roundingDown i: Index) -> Index { + _base._unicodeScalarIndex(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + _base._unicodeScalarIndex(roundingUp: i) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString.UnicodeScalarView: RangeReplaceableCollection { + public init() { + self._base = BigString() + } + + public mutating func reserveCapacity(_ n: Int) { + // Do nothing. + } + + public mutating func replaceSubrange>( // Note: Sequence, not Collection + _ subrange: Range, with newElements: __owned C + ) { + if C.self == String.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: String.UnicodeScalarView.self) + _base._replaceSubrange(subrange, with: String(newElements)) + } else if C.self == Substring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: Substring.UnicodeScalarView.self) + _base._replaceSubrange(subrange, with: Substring(newElements)) + } else if C.self == BigString.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigString.UnicodeScalarView.self) + _base._replaceSubrange(subrange, with: newElements._base) + } else if C.self == BigSubstring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigSubstring.UnicodeScalarView.self) + _base._replaceSubrange(subrange, with: newElements._base, in: newElements._bounds) + } else { + _base._replaceSubrange(subrange, with: BigString(_from: newElements)) + } + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned String.UnicodeScalarView + ) { + _base._replaceSubrange(subrange, with: String(newElements)) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned Substring.UnicodeScalarView + ) { + _base._replaceSubrange(subrange, with: Substring(newElements)) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned BigString.UnicodeScalarView + ) { + _base._replaceSubrange(subrange, with: newElements._base) + } + + public mutating func replaceSubrange( + _ subrange: Range, with newElements: __owned BigSubstring.UnicodeScalarView + ) { + _base._replaceSubrange(subrange, with: newElements._base, in: newElements._bounds) + } + + public init>(_ elements: S) { + if S.self == String.UnicodeScalarView.self { + let elements = _identityCast(elements, to: String.UnicodeScalarView.self) + self.init(_base: BigString(_from: elements)) + } else if S.self == Substring.UnicodeScalarView.self { + let elements = _identityCast(elements, to: Substring.UnicodeScalarView.self) + self.init(_base: BigString(_from: elements)) + } else if S.self == BigString.UnicodeScalarView.self { + let elements = _identityCast(elements, to: BigString.UnicodeScalarView.self) + self.init(_base: elements._base) + } else if S.self == BigSubstring.UnicodeScalarView.self { + let elements = _identityCast(elements, to: BigSubstring.UnicodeScalarView.self) + self.init(_base: BigString(_from: elements._base, in: elements._bounds)) + } else { + self.init(_base: BigString(_from: elements)) + } + } + + public init(_ elements: String.UnicodeScalarView) { + self.init(_base: BigString(_from: elements)) + } + + public init(_ elements: Substring.UnicodeScalarView) { + self.init(_base: BigString(_from: elements)) + } + + public init(_ elements: BigString.UnicodeScalarView) { + self.init(_base: elements._base) + } + + public init(_ elements: BigSubstring.UnicodeScalarView) { + self.init(_base: BigString(_from: elements._base, in: elements._bounds)) + } + + public init(repeating repeatedValue: UnicodeScalar, count: Int) { + self.init(_base: BigString(repeating: BigString(String(repeatedValue)), count: count)) + } + + public init(repeating repeatedValue: some StringProtocol, count: Int) { + self.init(_base: BigString(repeating: BigString(_from: repeatedValue), count: count)) + } + + public init(repeating value: BigString.UnicodeScalarView, count: Int) { + self.init(_base: BigString(repeating: value._base, count: count)) + } + + public init(repeating value: BigSubstring.UnicodeScalarView, count: Int) { + let value = BigString(value) + self.init(_base: BigString(repeating: value, count: count)) + } + + public mutating func append(_ newElement: __owned UnicodeScalar) { + _base.append(contentsOf: String(newElement)) + } + + public mutating func append>(contentsOf newElements: __owned S) { + if S.self == String.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: String.UnicodeScalarView.self) + append(contentsOf: newElements) + } else if S.self == Substring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: Substring.UnicodeScalarView.self) + append(contentsOf: newElements) + } else if S.self == BigString.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigString.UnicodeScalarView.self) + append(contentsOf: newElements) + } else if S.self == BigSubstring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigSubstring.UnicodeScalarView.self) + append(contentsOf: newElements) + } else { + _base.append(contentsOf: BigString(_from: newElements)) + } + } + + public mutating func append(contentsOf newElements: __owned String.UnicodeScalarView) { + _base.append(contentsOf: String(newElements)) + } + + public mutating func append(contentsOf newElements: __owned Substring.UnicodeScalarView) { + _base.append(contentsOf: Substring(newElements)) + } + + public mutating func append(contentsOf newElements: __owned BigString.UnicodeScalarView) { + _base.append(contentsOf: newElements._base) + } + + public mutating func append(contentsOf newElements: __owned BigSubstring.UnicodeScalarView) { + _base._append(contentsOf: newElements._base, in: newElements._bounds) + } + + public mutating func insert(_ newElement: UnicodeScalar, at i: Index) { + _base.insert(contentsOf: String(newElement), at: i) + } + + public mutating func insert>( // Note: Sequence, not Collection + contentsOf newElements: __owned C, at i: Index + ) { + if C.self == String.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: String.UnicodeScalarView.self) + insert(contentsOf: newElements, at: i) + } else if C.self == Substring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: Substring.UnicodeScalarView.self) + insert(contentsOf: newElements, at: i) + } else if C.self == BigString.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigString.UnicodeScalarView.self) + insert(contentsOf: newElements, at: i) + } else if C.self == BigSubstring.UnicodeScalarView.self { + let newElements = _identityCast(newElements, to: BigSubstring.UnicodeScalarView.self) + insert(contentsOf: newElements, at: i) + } else { + _base.insert(contentsOf: BigString(_from: newElements), at: i) + } + } + + public mutating func insert( + contentsOf newElements: __owned String.UnicodeScalarView, + at i: Index + ) { + _base.insert(contentsOf: String(newElements), at: i) + } + + public mutating func insert( + contentsOf newElements: __owned Substring.UnicodeScalarView, + at i: Index + ) { + _base.insert(contentsOf: Substring(newElements), at: i) + } + + public mutating func insert( + contentsOf newElements: __owned BigString.UnicodeScalarView, + at i: Index + ) { + _base.insert(contentsOf: newElements._base, at: i) + } + + public mutating func insert( + contentsOf newElements: __owned BigSubstring.UnicodeScalarView, + at i: Index + ) { + _base._insert(contentsOf: newElements._base, in: newElements._bounds, at: i) + } + + @discardableResult + public mutating func remove(at i: Index) -> UnicodeScalar { + _base.removeUnicodeScalar(at: i) + } + + public mutating func removeSubrange(_ bounds: Range) { + _base._removeSubrange(bounds) + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { + self._base = BigString() + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigSubstring+UTF16View.swift b/Sources/RopeModule/BigString/Views/BigSubstring+UTF16View.swift new file mode 100644 index 000000000..3ce2150f4 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigSubstring+UTF16View.swift @@ -0,0 +1,203 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + public struct UTF16View: Sendable { + internal var _base: BigString + internal var _bounds: Range + + public init(_unchecked base: BigString, in bounds: Range) { + self._base = base + self._bounds = bounds + } + + public init(_ base: BigString, in bounds: Range) { + self._base = base + let lower = base._utf16Index(roundingDown: bounds.lowerBound) + let upper = base._utf16Index(roundingDown: bounds.upperBound) + self._bounds = Range(uncheckedBounds: (lower, upper)) + } + + internal init(_substring: BigSubstring) { + self.init(_unchecked: _substring._base, in: _substring._bounds) + } + } + + public var utf16: UTF16View { + UTF16View(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public init?(_ utf16: BigSubstring.UTF16View) { + guard + !utf16.startIndex._isUTF16TrailingSurrogate, + !utf16.endIndex._isUTF16TrailingSurrogate + else { + return nil + } + self.init(_from: utf16._base, in: utf16._bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View { + public var base: BigString.UTF16View { _base.utf16 } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + var i1 = left._bounds.lowerBound + var i2 = right._bounds.lowerBound + + var j1 = left._bounds.upperBound + var j2 = right._bounds.upperBound + + // Compare first code units, if they're trailing surrogates. + guard i1._isUTF16TrailingSurrogate == i2._isUTF16TrailingSurrogate else { return false } + if i1._isUTF16TrailingSurrogate { + guard left[i1] == right[i2] else { return false } + left.formIndex(after: &i1) + left.formIndex(after: &i2) + } + guard i1 < j1, i2 < j2 else { return i1 == j1 && i2 == j2 } + + // Compare last code units, if they're trailing surrogates. + guard j1._isUTF16TrailingSurrogate == j2._isUTF16TrailingSurrogate else { return false } + if j1._isUTF16TrailingSurrogate { + left.formIndex(before: &j1) + right.formIndex(before: &j2) + guard left[j1] == right[j2] else { return false} + } + + return BigString.utf8IsEqual(left._base, in: i1 ..< j1, to: right._base, in: i2 ..< j2) + } + + public func isIdentical(to other: Self) -> Bool { + guard self._base.isIdentical(to: other._base) else { return false } + return self._bounds == other._bounds + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View: Hashable { + public func hash(into hasher: inout Hasher) { + for codeUnit in self { + hasher.combine(codeUnit) + } + hasher.combine(0xFFFF as UInt16) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View: Sequence { + public typealias Element = UInt16 + + public struct Iterator: IteratorProtocol { + var _it: BigString.UTF16View.Iterator + var _end: BigString.Index + + init(_substring: BigSubstring.UTF16View) { + self._it = .init(_base: _substring._base, from: _substring.startIndex) + self._end = _substring.endIndex + } + + public mutating func next() -> UInt16? { + guard _it._index < _end else { return nil } + return _it.next() + } + } + + public func makeIterator() -> Iterator { + Iterator(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = Self + + @inline(__always) + public var startIndex: Index { _bounds.lowerBound } + + @inline(__always) + public var endIndex: Index { _bounds.upperBound } + + public var count: Int { + distance(from: _bounds.lowerBound, to: _bounds.upperBound) + } + + @inline(__always) + public func index(after i: Index) -> Index { + precondition(i < endIndex, "Can't advance above end index") + return _base._utf16Index(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + precondition(i > startIndex, "Can't advance below start index") + return _base._utf16Index(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + let j = _base._utf16Index(i, offsetBy: distance) + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + guard let j = _base._utf16Index(i, offsetBy: distance, limitedBy: limit) else { return nil } + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func distance(from start: Index, to end: Index) -> Int { + precondition(start >= startIndex && start <= endIndex, "Index out of bounds") + precondition(end >= startIndex && end <= endIndex, "Index out of bounds") + return _base._utf16Distance(from: start, to: end) + } + + public subscript(position: Index) -> UInt16 { + precondition(position >= startIndex && position < endIndex, "Index out of bounds") + return _base[_utf16: position] + } + + public subscript(bounds: Range) -> Self { + precondition( + bounds.lowerBound >= startIndex && bounds.upperBound <= endIndex, + "Range out of bounds") + return Self(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF16View { + public func index(roundingDown i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._utf16Index(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._utf16Index(roundingUp: i) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigSubstring+UTF8View.swift b/Sources/RopeModule/BigString/Views/BigSubstring+UTF8View.swift new file mode 100644 index 000000000..4d0c2a744 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigSubstring+UTF8View.swift @@ -0,0 +1,177 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + public struct UTF8View: Sendable { + internal var _base: BigString + internal var _bounds: Range + + public init(_unchecked base: BigString, in bounds: Range) { + self._base = base + self._bounds = bounds + } + + public init(_ base: BigString, in bounds: Range) { + self._base = base + let lower = base._utf8Index(roundingDown: bounds.lowerBound) + let upper = base._utf8Index(roundingDown: bounds.upperBound) + self._bounds = Range(uncheckedBounds: (lower, upper)) + } + + internal init(_substring: BigSubstring) { + self.init(_unchecked: _substring._base, in: _substring._bounds) + } + } + + public var utf8: UTF8View { + UTF8View(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public init?(_ utf8: BigSubstring.UTF8View) { + guard + utf8._base.unicodeScalars.index(roundingDown: utf8.startIndex) == utf8.startIndex, + utf8._base.unicodeScalars.index(roundingDown: utf8.endIndex) == utf8.endIndex + else { + return nil + } + self.init(_from: utf8._base, in: utf8._bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View { + public var base: BigString.UTF8View { _base.utf8 } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + BigString.utf8IsEqual(left._base, in: left._bounds, to: right._base, in: right._bounds) + } + + public func isIdentical(to other: Self) -> Bool { + guard self._base.isIdentical(to: other._base) else { return false } + return self._bounds == other._bounds + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View: Hashable { + public func hash(into hasher: inout Hasher) { + _base.hashUTF8(into: &hasher, from: _bounds.lowerBound, to: _bounds.upperBound) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View: Sequence { + public typealias Element = UInt8 + + public struct Iterator: IteratorProtocol { + var _it: BigString.UTF8View.Iterator + var _end: BigString.Index + + init(_substring: BigSubstring.UTF8View) { + self._it = .init(_base: _substring._base, from: _substring.startIndex) + self._end = _substring.endIndex + } + + public mutating func next() -> UInt8? { + guard _it._index < _end else { return nil } + return _it.next() + } + } + + public func makeIterator() -> Iterator { + Iterator(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = Self + + @inline(__always) + public var startIndex: Index { _bounds.lowerBound } + + @inline(__always) + public var endIndex: Index { _bounds.upperBound } + + public var count: Int { + distance(from: _bounds.lowerBound, to: _bounds.upperBound) + } + + @inline(__always) + public func index(after i: Index) -> Index { + precondition(i < endIndex, "Can't advance above end index") + return _base._utf8Index(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + precondition(i > startIndex, "Can't advance below start index") + return _base._utf8Index(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + let j = _base._utf8Index(i, offsetBy: distance) + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + guard let j = _base._utf8Index(i, offsetBy: distance, limitedBy: limit) else { return nil } + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func distance(from start: Index, to end: Index) -> Int { + precondition(start >= startIndex && start <= endIndex, "Index out of bounds") + precondition(end >= startIndex && end <= endIndex, "Index out of bounds") + return _base._utf8Distance(from: start, to: end) + } + + public subscript(position: Index) -> UInt8 { + precondition(position >= startIndex && position < endIndex, "Index out of bounds") + return _base[_utf8: position] + } + + public subscript(bounds: Range) -> Self { + precondition( + bounds.lowerBound >= startIndex && bounds.upperBound <= endIndex, + "Range out of bounds") + return Self(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UTF8View { + public func index(roundingDown i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._utf8Index(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._utf8Index(roundingUp: i) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigSubstring+UnicodeScalarView.swift b/Sources/RopeModule/BigString/Views/BigSubstring+UnicodeScalarView.swift new file mode 100644 index 000000000..9891a1928 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigSubstring+UnicodeScalarView.swift @@ -0,0 +1,320 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + public struct UnicodeScalarView: Sendable { + internal var _base: BigString + internal var _bounds: Range + + public init(_unchecked base: BigString, in bounds: Range) { + assert(bounds.lowerBound == base._unicodeScalarIndex(roundingDown: bounds.lowerBound)) + assert(bounds.upperBound == base._unicodeScalarIndex(roundingDown: bounds.upperBound)) + self._base = base + self._bounds = bounds + } + + public init(_ base: BigString, in bounds: Range) { + self._base = base + let lower = base._unicodeScalarIndex(roundingDown: bounds.lowerBound) + let upper = base._unicodeScalarIndex(roundingDown: bounds.upperBound) + self._bounds = Range(uncheckedBounds: (lower, upper)) + } + + internal init(_substring: BigSubstring) { + self.init(_unchecked: _substring._base, in: _substring._bounds) + } + } + + public var unicodeScalars: UnicodeScalarView { + get { + UnicodeScalarView(_substring: self) + } + set { + self = Self(newValue._base, in: newValue._bounds) + } + _modify { + var view = UnicodeScalarView(_unchecked: _base, in: _bounds) + self = Self() + defer { + self = Self(view._base, in: view._bounds) + } + yield &view + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigString { + public init(_ unicodeScalars: BigSubstring.UnicodeScalarView) { + self.init(_from: unicodeScalars._base, in: unicodeScalars._bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView { + public var base: BigString.UnicodeScalarView { _base.unicodeScalars } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value.unicodeScalars) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: CustomStringConvertible { + public var description: String { + String(_from: _base, in: _bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: CustomDebugStringConvertible { + public var debugDescription: String { + description.debugDescription + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + BigString.utf8IsEqual(left._base, in: left._bounds, to: right._base, in: right._bounds) + } + + public func isIdentical(to other: Self) -> Bool { + guard self._base.isIdentical(to: other._base) else { return false } + return self._bounds == other._bounds + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: Hashable { + public func hash(into hasher: inout Hasher) { + _base.hashUTF8(into: &hasher, from: _bounds.lowerBound, to: _bounds.upperBound) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: Sequence { + public typealias Element = UnicodeScalar + + public struct Iterator: IteratorProtocol { + var _it: BigString.UnicodeScalarView.Iterator + let _end: BigString.Index + + internal init(_substring: BigSubstring.UnicodeScalarView) { + self._it = .init(_base: _substring._base, from: _substring.startIndex) + self._end = _substring._base.resolve(_substring.endIndex, preferEnd: true) + } + + public mutating func next() -> UnicodeScalar? { + guard _it._index < _end else { return nil } + return _it.next() + } + } + + public func makeIterator() -> Iterator { + Iterator(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = Self + + @inline(__always) + public var startIndex: Index { _bounds.lowerBound } + + @inline(__always) + public var endIndex: Index { _bounds.upperBound } + + public var count: Int { + distance(from: _bounds.lowerBound, to: _bounds.upperBound) + } + + @inline(__always) + public func index(after i: Index) -> Index { + precondition(i < endIndex, "Can't advance above end index") + return _base._unicodeScalarIndex(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + precondition(i > startIndex, "Can't advance below start index") + return _base._unicodeScalarIndex(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + let j = _base._unicodeScalarIndex(i, offsetBy: distance) + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + guard let j = _base._unicodeScalarIndex(i, offsetBy: distance, limitedBy: limit) else { + return nil + } + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func distance(from start: Index, to end: Index) -> Int { + precondition(start >= startIndex && start <= endIndex, "Index out of bounds") + precondition(end >= startIndex && end <= endIndex, "Index out of bounds") + return _base._unicodeScalarDistance(from: start, to: end) + } + + public subscript(position: Index) -> UnicodeScalar { + precondition(position >= startIndex && position < endIndex, "Index out of bounds") + return _base[_unicodeScalar: position] + } + + public subscript(bounds: Range) -> Self { + precondition( + bounds.lowerBound >= startIndex && bounds.upperBound <= endIndex, + "Range out of bounds") + return Self(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView { + public func index(roundingDown i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._unicodeScalarIndex(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base._unicodeScalarIndex(roundingUp: i) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView { + /// Run the closure `body` to mutate the contents of this view within `range`, then update + /// the bounds of this view to maintain their logical position in the resulting string. + /// The `range` argument is validated to be within the original bounds of the substring. + internal mutating func _mutateBasePreservingBounds( + in range: Range, + with body: (inout BigString.UnicodeScalarView) -> R + ) -> R { + precondition( + range.lowerBound >= _bounds.lowerBound && range.upperBound <= _bounds.upperBound, + "Range out of bounds") + + let startOffset = self.startIndex.utf8Offset + let endOffset = self.endIndex.utf8Offset + let oldCount = self._base._utf8Count + + var view = BigString.UnicodeScalarView(_base: self._base) + self._base = BigString() + + defer { + // The Unicode scalar view is regular -- we just need to maintain the UTF-8 offsets of + // our bounds across the mutation. No extra adjustment/rounding is necessary. + self._base = view._base + let delta = self._base._utf8Count - oldCount + let start = _base._utf8Index(at: startOffset)._knownScalarAligned() + let end = _base._utf8Index(at: endOffset + delta)._knownScalarAligned() + self._bounds = start ..< end + } + return body(&view) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring.UnicodeScalarView: RangeReplaceableCollection { + public init() { + self.init(_substring: BigSubstring()) + } + + public mutating func reserveCapacity(_ n: Int) { + // Do nothing. + } + + public mutating func replaceSubrange>( // Note: Sequence, not Collection + _ subrange: Range, with newElements: __owned C + ) { + _mutateBasePreservingBounds(in: subrange) { $0.replaceSubrange(subrange, with: newElements) } + } + + public init>(_ elements: S) { + let base = BigString.UnicodeScalarView(elements) + self.init(base._base, in: base.startIndex ..< base.endIndex) + } + + public init(repeating repeatedValue: UnicodeScalar, count: Int) { + let base = BigString.UnicodeScalarView(repeating: repeatedValue, count: count) + self.init(base._base, in: base.startIndex ..< base.endIndex) + } + + public mutating func append(_ newElement: UnicodeScalar) { + let i = endIndex + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(newElement, at: i) + } + } + + public mutating func append>(contentsOf newElements: __owned S) { + let i = endIndex + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(contentsOf: newElements, at: i) + } + } + + public mutating func insert(_ newElement: UnicodeScalar, at i: Index) { + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(newElement, at: i) + } + } + + + public mutating func insert>( // Note: Sequence, not Collection + contentsOf newElements: __owned C, at i: Index + ) { + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(contentsOf: newElements, at: i) + } + } + + @discardableResult + public mutating func remove(at i: Index) -> UnicodeScalar { + let j = self.index(after: i) + return _mutateBasePreservingBounds(in: i ..< j) { + $0.remove(at: i) + } + } + + public mutating func removeSubrange(_ bounds: Range) { + _mutateBasePreservingBounds(in: bounds) { + $0.removeSubrange(bounds) + } + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { + let bounds = _bounds + _mutateBasePreservingBounds(in: bounds) { + $0.removeSubrange(bounds) + } + assert(_bounds.isEmpty) + } +} + +#endif diff --git a/Sources/RopeModule/BigString/Views/BigSubstring.swift b/Sources/RopeModule/BigString/Views/BigSubstring.swift new file mode 100644 index 000000000..38c0072b0 --- /dev/null +++ b/Sources/RopeModule/BigString/Views/BigSubstring.swift @@ -0,0 +1,370 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +public struct BigSubstring: Sendable { + var _base: BigString + var _bounds: Range + + public init(_unchecked base: BigString, in bounds: Range) { + assert(bounds.lowerBound == base.index(roundingDown: bounds.lowerBound)) + assert(bounds.upperBound == base.index(roundingDown: bounds.upperBound)) + self._base = base + self._bounds = bounds + } + + public init(_ base: BigString, in bounds: Range) { + self._base = base + // Sub-character slicing could change character boundaries in the tree, requiring + // resyncing metadata. This would not be acceptable to do during slicing, so let's + // round substring bounds down to the nearest character. + let start = base.index(roundingDown: bounds.lowerBound) + let end = base.index(roundingDown: bounds.upperBound) + self._bounds = Range(uncheckedBounds: (start, end)) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + public var base: BigString { _base } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + func _foreachChunk( + _ body: (Substring) -> Void + ) { + self._base._foreachChunk(from: _bounds.lowerBound, to: _bounds.upperBound, body) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: CustomStringConvertible { + public var description: String { + String(_from: _base, in: _bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: CustomDebugStringConvertible { + public var debugDescription: String { + description.debugDescription + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: LosslessStringConvertible { + // init?(_: String) is implemented by RangeReplaceableCollection.init(_:) +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: Equatable { + public static func ==(left: Self, right: Self) -> Bool { + // FIXME: Implement properly normalized comparisons & hashing. + // This is somewhat tricky as we shouldn't just normalize individual pieces of the string + // split up on random Character boundaries -- Unicode does not promise that + // norm(a + c) == norm(a) + norm(b) in this case. + // To do this properly, we'll probably need to expose new stdlib entry points. :-/ + if left.isIdentical(to: right) { return true } + + guard left.count == right.count else { return false } + + // FIXME: Even if we keep doing characterwise comparisons, we should skip over shared subtrees. + var it1 = left.makeIterator() + var it2 = right.makeIterator() + var a: Character? = nil + var b: Character? = nil + repeat { + a = it1.next() + b = it2.next() + guard a == b else { return false } + } while a != nil + return true + } + + public func isIdentical(to other: Self) -> Bool { + guard self._base.isIdentical(to: other._base) else { return false } + return self._bounds == other._bounds + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: Hashable { + public func hash(into hasher: inout Hasher) { + var it = self.makeIterator() + while let character = it.next() { + let s = String(character) + s._withNFCCodeUnits { hasher.combine($0) } + } + hasher.combine(0xFF as UInt8) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: Comparable { + public static func < (left: Self, right: Self) -> Bool { + // FIXME: Implement properly normalized comparisons & hashing. + // This is somewhat tricky as we shouldn't just normalize individual pieces of the string + // split up on random Character boundaries -- Unicode does not promise that + // norm(a + c) == norm(a) + norm(b) in this case. + // To do this properly, we'll probably need to expose new stdlib entry points. :-/ + if left.isIdentical(to: right) { return false } + // FIXME: Even if we keep doing characterwise comparisons, we should skip over shared subtrees. + var it1 = left.makeIterator() + var it2 = right.makeIterator() + while true { + switch (it1.next(), it2.next()) { + case (nil, nil): return false + case (nil, .some): return true + case (.some, nil): return false + case let (a?, b?): + if a == b { continue } + return a < b + } + } + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: Sequence { + public typealias Element = Character + + public struct Iterator: IteratorProtocol { + let _end: BigString.Index + var _it: BigString.Iterator + + init(_substring: BigSubstring) { + self._it = BigString.Iterator(_substring._base, from: _substring.startIndex) + self._end = _substring.endIndex + } + + public mutating func next() -> Character? { + guard _it.isBelow(_end) else { return nil } + return _it.next() + } + } + + public func makeIterator() -> Iterator { + Iterator(_substring: self) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: BidirectionalCollection { + public typealias Index = BigString.Index + public typealias SubSequence = Self + + @inline(__always) + public var startIndex: Index { _bounds.lowerBound } + + @inline(__always) + public var endIndex: Index { _bounds.upperBound } + + public var count: Int { + distance(from: _bounds.lowerBound, to: _bounds.upperBound) + } + + @inline(__always) + public func index(after i: Index) -> Index { + precondition(i < endIndex, "Can't advance above end index") + return _base.index(after: i) + } + + @inline(__always) + public func index(before i: Index) -> Index { + precondition(i > startIndex, "Can't advance below start index") + return _base.index(before: i) + } + + @inline(__always) + public func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + let j = _base.index(i, offsetBy: distance) + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + guard let j = _base.index(i, offsetBy: distance, limitedBy: limit) else { return nil } + precondition(j >= startIndex && j <= endIndex, "Index out of bounds") + return j + } + + public func distance(from start: Index, to end: Index) -> Int { + precondition(start >= startIndex && start <= endIndex, "Index out of bounds") + precondition(end >= startIndex && end <= endIndex, "Index out of bounds") + return _base.distance(from: start, to: end) + } + + public subscript(position: Index) -> Character { + precondition(position >= startIndex && position < endIndex, "Index out of bounds") + return _base[position] + } + + public subscript(bounds: Range) -> Self { + precondition( + bounds.lowerBound >= startIndex && bounds.upperBound <= endIndex, + "Range out of bounds") + return Self(_base, in: bounds) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + public func index(roundingDown i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base.index(roundingDown: i) + } + + public func index(roundingUp i: Index) -> Index { + precondition(i >= startIndex && i <= endIndex, "Index out of bounds") + return _base.index(roundingUp: i) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring { + /// Run the closure `body` to mutate the contents of this view within `range`, then update + /// the bounds of this view to maintain an approximation of their logical position in the + /// resulting string. + /// + /// The `range` argument is validated to be within the original bounds of the substring. + internal mutating func _mutateBasePreservingBounds( + in range: Range, + with body: (inout BigString) -> R + ) -> R { + precondition( + range.lowerBound >= _bounds.lowerBound && range.upperBound <= _bounds.upperBound, + "Range out of bounds") + + let startOffset = self.startIndex.utf8Offset + let endOffset = self.endIndex.utf8Offset + let oldCount = self._base._utf8Count + + defer { + // Substring mutations may change grapheme boundaries across the bounds of the original + // substring value, and we need to ensure that the substring's bounds remain well-aligned. + // Unfortunately, there are multiple ways of doing this, none of which are obviously + // superior to others. To keep the behavior easier to explan, we emulate substring + // initialization and round the start and end indices down to the nearest Character boundary + // after each mutation. + let delta = self._base._utf8Count - oldCount + let start = _base.index(roundingDown: Index(_utf8Offset: startOffset)) + let end = _base.index(roundingDown: Index(_utf8Offset: endOffset + delta)) + self._bounds = start ..< end + } + return body(&self._base) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension BigSubstring: RangeReplaceableCollection { + public init() { + let str = BigString() + let bounds = Range(uncheckedBounds: (str.startIndex, str.endIndex)) + self.init(_unchecked: str, in: bounds) + } + + public mutating func reserveCapacity(_ n: Int) { + // Do nothing. + } + + public mutating func replaceSubrange>( // Note: Sequence, not Collection + _ subrange: Range, with newElements: __owned C + ) { + _mutateBasePreservingBounds(in: subrange) { + $0.replaceSubrange(subrange, with: newElements) + } + } + + public init>(_ elements: S) { + let base = BigString(elements) + self.init(base, in: base.startIndex ..< base.endIndex) + } + + public init(repeating repeatedValue: Character, count: Int) { + self.init(BigString(repeating: repeatedValue, count: count)) + } + + public init(repeating repeatedValue: some StringProtocol, count: Int) { + self.init(BigString(repeating: repeatedValue, count: count)) + } + + public init(repeating repeatedValue: BigString, count: Int) { + self.init(BigString(repeating: repeatedValue, count: count)) + } + + public init(repeating repeatedValue: BigSubstring, count: Int) { + self.init(BigString(repeating: repeatedValue, count: count)) + } + + public mutating func append(_ newElement: Character) { + let i = endIndex + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(newElement, at: i) + } + } + + public mutating func append>(contentsOf newElements: __owned S) { + let i = endIndex + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(contentsOf: newElements, at: i) + } + } + + public mutating func insert(_ newElement: Character, at i: Index) { + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(newElement, at: i) + } + } + + public mutating func insert>( // Note: Sequence, not Collection + contentsOf newElements: __owned C, at i: Index + ) { + _mutateBasePreservingBounds(in: i ..< i) { + $0.insert(contentsOf: newElements, at: i) + } + } + + @discardableResult + public mutating func remove(at i: Index) -> Character { + let j = self.index(after: i) + return _mutateBasePreservingBounds(in: i ..< j) { + $0.remove(at: i) + } + } + + public mutating func removeSubrange(_ bounds: Range) { + _mutateBasePreservingBounds(in: bounds) { + $0.removeSubrange(bounds) + } + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { + let bounds = self._bounds + _mutateBasePreservingBounds(in: bounds) { + $0.removeSubrange(bounds) + } + assert(_bounds.isEmpty) + } +} + +#endif diff --git a/Sources/RopeModule/Rope/Basics/Rope+Builder.swift b/Sources/RopeModule/Rope/Basics/Rope+Builder.swift new file mode 100644 index 000000000..5ccc5b375 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+Builder.swift @@ -0,0 +1,480 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public init(_ items: some Sequence) { + if let items = items as? Self { + self = items + return + } + var builder = Builder() + for item in items { + builder.insertBeforeTip(item) + } + self = builder.finalize() + } +} + +extension Rope { + @frozen // Not really! This module isn't ABI stable. + public struct Builder { + // ║ ║ + // ║ ║ ║ ║ + // ║ ║ ║ ║ ║ ║ + // ║ ║ ║ ║ ║ ║ ║ ║ + // ──╨─╨─╨─╨──╨──╨──╨─╨─╨─╨── + // →prefixTrees→ ↑ ↑ ←suffixTrees← + // prefix suffix + + @usableFromInline internal var _prefixTrees: [Rope] = [] + @usableFromInline internal var _prefixLeaf: Rope._Node? + + @usableFromInline internal var _prefix: Rope._Item? + + @usableFromInline internal var _suffix: Rope._Item? + @usableFromInline internal var _suffixTrees: [Rope] = [] + + @inlinable + public init() {} + + @inlinable + public var isPrefixEmpty: Bool { + if _prefix != nil { return false } + if let leaf = self._prefixLeaf, !leaf.isEmpty { return false } + return _prefixTrees.isEmpty + } + + @inlinable + public var isSuffixEmpty: Bool { + if _suffix != nil { return false } + return _suffixTrees.isEmpty + } + + @inlinable + public var prefixSummary: Summary { + var sum = Summary.zero + for sapling in _prefixTrees { + sum.add(sapling.summary) + } + if let leaf = _prefixLeaf { sum.add(leaf.summary) } + if let item = _prefix { sum.add(item.summary) } + return sum + } + + @inlinable + public var suffixSummary: Summary { + var sum = Summary.zero + if let item = _suffix { sum.add(item.summary) } + for rope in _suffixTrees { + sum.add(rope.summary) + } + return sum + } + + @inlinable + var _lastPrefixItem: Rope._Item { + get { + assert(!isPrefixEmpty) + if let item = self._prefix { return item } + if let leaf = self._prefixLeaf { return leaf.lastItem } + return _prefixTrees.last!.root.lastItem + } + _modify { + assert(!isPrefixEmpty) + if _prefix != nil { + yield &_prefix! + } else if _prefixLeaf?.isEmpty == false { + yield &_prefixLeaf!.lastItem + } else { + yield &_prefixTrees[_prefixTrees.count - 1].root.lastItem + } + } + } + + @inlinable + var _firstSuffixItem: Rope._Item { + get { + assert(!isSuffixEmpty) + if let _suffix { return _suffix } + return _suffixTrees[_suffixTrees.count - 1].root.firstItem + } + _modify { + assert(!isSuffixEmpty) + if _suffix != nil { + yield &_suffix! + } else { + yield &_suffixTrees[_suffixTrees.count - 1].root.firstItem + } + } + } + + @inlinable + public func forEachElementInPrefix( + from position: Int, + in metric: some RopeMetric, + _ body: (Element, Element.Index?) -> Bool + ) -> Bool { + var position = position + var i = 0 + while i < _prefixTrees.count { + let size = metric.size(of: _prefixTrees[i].summary) + if position < size { break } + position -= size + i += 1 + } + if i < _prefixTrees.count { + guard _prefixTrees[i].forEachWhile(from: position, in: metric, body) else { return false } + i += 1 + while i < _prefixTrees.count { + guard _prefixTrees[i].forEachWhile({ body($0, nil) }) else { return false } + i += 1 + } + if let leaf = self._prefixLeaf { + guard leaf.forEachWhile({ body($0, nil) }) else { return false } + } + if let item = self._prefix { + guard body(item.value, nil) else { return false } + } + return true + } + + if let leaf = self._prefixLeaf { + let size = metric.size(of: leaf.summary) + if position < size { + guard leaf.forEachWhile(from: position, in: metric, body) else { return false } + if let item = self._prefix { + guard body(item.value, nil) else { return false } + } + return true + } + position -= size + } + if let item = self._prefix { + let i = metric.index(at: position, in: item.value) + guard body(item.value, i) else { return false} + } + return true + } + + @inlinable + public mutating func mutatingForEachSuffix( + _ body: (inout Element) -> R? + ) -> R? { + if self._suffix != nil, + let r = body(&self._suffix!.value) { + return r + } + for i in stride(from: _suffixTrees.count - 1, through: 0, by: -1) { + if let r = self._suffixTrees[i].mutatingForEach(body) { + return r + } + } + return nil + } + + @inlinable + public mutating func insertBeforeTip(_ item: __owned Element) { + _insertBeforeTip(Rope._Item(item)) + } + + @inlinable + mutating func _insertBeforeTip(_ item: __owned Rope._Item) { + guard !item.isEmpty else { return } + guard var prefix = self._prefix._take() else { + self._prefix = item + return + } + var item = item + if (prefix.isUndersized || item.isUndersized), prefix.rebalance(nextNeighbor: &item) { + self._prefix = prefix + return + } + _appendNow(prefix) + self._prefix = item + } + + @inlinable + mutating func _appendNow(_ item: __owned Rope._Item) { + assert(self._prefix == nil) + assert(!item.isUndersized) + var leaf = self._prefixLeaf._take() ?? .createLeaf() + leaf._appendItem(item) + if leaf.isFull { + self._appendNow(leaf) + } else { + self._prefixLeaf = leaf + } + _invariantCheck() + } + + @inlinable + public mutating func insertBeforeTip(_ rope: __owned Rope) { + guard rope._root != nil else { return } + _insertBeforeTip(rope.root) + } + + @inlinable + public mutating func insertBeforeTip>(_ items: __owned S) { + if S.self == Rope.self { + let items = _identityCast(items, to: Rope.self) + self.insertBeforeTip(items) + } else { + for item in items { + self.insertBeforeTip(item) + } + } + } + + @inlinable + mutating func _insertBeforeTip(_ node: __owned Rope._Node) { + defer { _invariantCheck() } + var node = node + if node.height == 0 { + if node.childCount == 1 { + _insertBeforeTip(node.firstItem) + return + } + if let item = self._prefix._take() { + if let spawn = node.prepend(item) { + _insertBeforeTip(node) + _insertBeforeTip(spawn) + return + } + } + if var leaf = self._prefixLeaf._take() { + if leaf.rebalance(nextNeighbor: &node), !leaf.isFull { + self._prefixLeaf = leaf + return + } + self._appendNow(leaf) + } + + if node.isFull { + self._appendNow(node) + } else { + self._prefixLeaf = node + } + return + } + + if var prefix = self._prefix._take() { + if !prefix.isUndersized || !node.firstItem.rebalance(prevNeighbor: &prefix) { + self._appendNow(prefix) + } + } + if let leaf = self._prefixLeaf._take() { + _appendNow(leaf) + } + _appendNow(node) + } + + @inlinable + mutating func _appendNow(_ rope: __owned Rope._Node) { + assert(self._prefix == nil && self._prefixLeaf == nil) + var new = rope + while !_prefixTrees.isEmpty { + // Join previous saplings together until they grow at least as deep as the new one. + var previous = _prefixTrees.removeLast() + while previous._height < new.height { + if _prefixTrees.isEmpty { + previous._append(new) + _prefixTrees.append(previous) + return + } + previous.prepend(_prefixTrees.removeLast()) + } + + if previous._height == new.height { + if previous.root.rebalance(nextNeighbor: &new) { + new = previous.root + } else { + new = .createInner(children: previous.root, new) + } + continue + } + + if new.isFull, !previous.root.isFull, previous._height == new.height + 1 { + // Graft node under the last sapling, as a new child branch. + previous.root._appendNode(new) + new = previous.root + continue + } + + // The new seedling can be appended to the line and we're done. + _prefixTrees.append(previous) + break + } + _prefixTrees.append(Rope(root: new)) + } + + @inlinable + public mutating func insertAfterTip(_ item: __owned Element) { + _insertAfterTip(_Item(item)) + } + + @inlinable + mutating func _insertAfterTip(_ item: __owned _Item) { + guard !item.isEmpty else { return } + if var suffixItem = self._suffix._take() { + var item = item + if !(suffixItem.isUndersized && item.rebalance(nextNeighbor: &suffixItem)) { + if _suffixTrees.isEmpty { + _suffixTrees.append(Rope(root: .createLeaf(suffixItem))) + } else { + _suffixTrees[_suffixTrees.count - 1].prepend(suffixItem.value) + } + } + } + self._suffix = item + } + + @inlinable + public mutating func insertAfterTip(_ rope: __owned Rope) { + assert(_suffix == nil) + assert(_suffixTrees.isEmpty || rope._height <= _suffixTrees.last!._height) + _suffixTrees.append(rope) + } + + @inlinable + mutating func _insertAfterTip(_ rope: __owned Rope._Node) { + insertAfterTip(Rope(root: rope)) + } + + @inlinable + mutating func _insertBeforeTip(slots: Range, in node: __owned Rope._Node) { + assert(slots.lowerBound >= 0 && slots.upperBound <= node.childCount) + let c = slots.count + guard c > 0 else { return } + if c == 1 { + if node.isLeaf { + let item = node.readLeaf { $0.children[slots.lowerBound] } + _insertBeforeTip(item) + } else { + let child = node.readInner { $0.children[slots.lowerBound] } + _insertBeforeTip(child) + } + return + } + let copy = node.copy(slots: slots) + _insertBeforeTip(copy) + } + + @inlinable + mutating func _insertAfterTip(slots: Range, in node: __owned Rope._Node) { + assert(slots.lowerBound >= 0 && slots.upperBound <= node.childCount) + let c = slots.count + guard c > 0 else { return } + if c == 1 { + if node.isLeaf { + let item = node.readLeaf { $0.children[slots.lowerBound] } + _insertAfterTip(item) + } else { + let child = node.readInner { $0.children[slots.lowerBound] } + _insertAfterTip(child) + } + return + } + let copy = node.copy(slots: slots) + _insertAfterTip(copy) + } + + @inlinable + public mutating func finalize() -> Rope { + // Integrate prefix & suffix chunks. + if let suffixItem = self._suffix._take() { + _insertBeforeTip(suffixItem) + } + if var prefix = self._prefix._take() { + if !prefix.isUndersized { + _appendNow(prefix) + } else if !self.isPrefixEmpty { + if !self._lastPrefixItem.rebalance(nextNeighbor: &prefix) { + _appendNow(prefix) + } + } else if !self.isSuffixEmpty { + if !self._firstSuffixItem.rebalance(prevNeighbor: &prefix) { + _appendNow(prefix) + } + } else { + // We only have the seed; allow undersized chunk + return Rope(root: .createLeaf(prefix)) + } + } + assert(self._prefix == nil && self._suffix == nil) + while let tree = _suffixTrees.popLast() { + insertBeforeTip(tree) + } + // Merge all saplings, the seedling and the seed into a single rope. + if let item = self._prefix._take() { + _appendNow(item) + } + var rope = Rope(root: _prefixLeaf._take()) + while let tree = _prefixTrees.popLast() { + rope.prepend(tree) + } + assert(_prefixLeaf == nil && _prefixTrees.isEmpty && _suffixTrees.isEmpty) + rope._invariantCheck() + return rope + } + + @inlinable + public func _invariantCheck() { +#if COLLECTIONS_INTERNAL_CHECKS + var h = UInt8.max + for sapling in _prefixTrees { + precondition(sapling._height <= h) + sapling._invariantCheck() + h = sapling._height + } + if let leaf = self._prefixLeaf { + precondition(leaf.height == 0) + precondition(!leaf.isFull) + leaf.invariantCheck(depth: 0, height: 0) + } + h = 0 + for tree in _suffixTrees.reversed() { + precondition(tree._height >= h) + tree._invariantCheck() + h = tree._height + } +#endif + + } + + public func _dump(heightLimit: Int = Int.max) { + for i in self._prefixTrees.indices { + print("Sapling \(i):") + self._prefixTrees[i]._dump(heightLimit: heightLimit, firstPrefix: " ", restPrefix: " ") + } + if let leaf = self._prefixLeaf { + print("Seedling:") + leaf.dump(heightLimit: heightLimit, firstPrefix: " ", restPrefix: " ") + } + if let item = self._prefix { + print("Seed:") + print(" \(item)") + } + print("---") + var i = 0 + if let item = self._suffix { + print("Suffix \(i):") + i += 1 + print(" \(item)") + } + for tree in self._suffixTrees.reversed() { + print("Suffix \(i)") + i += 1 + tree._dump(heightLimit: heightLimit, firstPrefix: " ", restPrefix: " ") + } + } + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+Debugging.swift b/Sources/RopeModule/Rope/Basics/Rope+Debugging.swift new file mode 100644 index 000000000..58c396154 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+Debugging.swift @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 !COLLECTIONS_SINGLE_MODULE +import _CollectionsUtilities +#endif + +extension Rope._UnmanagedLeaf: CustomStringConvertible { + @usableFromInline + internal var description: String { + _addressString(for: _ref.toOpaque()) + } +} + +extension Rope { + @inlinable + public var _nodeCount: Int { + _root?.nodeCount ?? 0 + } +} + +extension Rope._Node { + @inlinable + internal var nodeCount: Int { + guard !isLeaf else { return 1 } + return readInner { $0.children.reduce(into: 1) { $0 += $1.nodeCount } } + } +} + +extension Rope { + public func _dump( + heightLimit: Int = .max, + firstPrefix: String = "", + restPrefix: String = "" + ) { + guard _root != nil else { + print("") + return + } + root.dump(heightLimit: heightLimit, firstPrefix: firstPrefix, restPrefix: restPrefix) + } +} + +extension Rope._Node: CustomStringConvertible { + @usableFromInline + internal var description: String { + """ + \(height > 0 ? "Inner@\(height)" : "Leaf")(\ + at: \(_addressString(for: object)), \ + summary: \(summary), \ + childCount: \(childCount)/\(Summary.maxNodeSize)) + """ + } +} + +extension Rope._Node { + @usableFromInline + internal func dump( + heightLimit: Int = .max, + firstPrefix: String = "", + restPrefix: String = "" + ) { + print("\(firstPrefix)\(description)") + + guard heightLimit > 0 else { return } + + if height > 0 { + readInner { + let c = $0.children + for slot in 0 ..< c.count { + c[slot].dump( + heightLimit: heightLimit - 1, + firstPrefix: "\(restPrefix)\(slot): ", + restPrefix: "\(restPrefix) ") + } + } + } else { + readLeaf { + let c = $0.children + for slot in 0 ..< c.count { + print("\(restPrefix)\(slot): \(c[slot])") + } + } + } + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+Invariants.swift b/Sources/RopeModule/Rope/Basics/Rope+Invariants.swift new file mode 100644 index 000000000..2424ecd7b --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+Invariants.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable @inline(__always) + public func _invariantCheck() { +#if COLLECTIONS_INTERNAL_CHECKS + _root?.invariantCheck(depth: 0, height: root.height, recursive: true) +#endif + } +} + +extension Rope._Node { +#if COLLECTIONS_INTERNAL_CHECKS + @usableFromInline + internal func invariantCheck(depth: UInt8, height: UInt8, recursive: Bool = true) { + precondition(height == self.height, "Mismatching rope height") + if isLeaf { + precondition(self.childCount <= Summary.maxNodeSize, "Oversized leaf") + precondition(height == 0, "Leaf with height > 0") + precondition(depth == 0 || self.childCount >= Summary.minNodeSize, "Undersized leaf") + + let sum: Summary = readLeaf { + $0.children.reduce(into: .zero) { $0.add($1.summary) } + } + precondition(self.summary == sum, "Mismatching summary") + + guard recursive else { return } + readLeaf { leaf in + for child in leaf.children { + child.value.invariantCheck() + } + } + return + } + + precondition(self.childCount <= Summary.maxNodeSize, "Oversized node") + if depth == 0 { + precondition(self.childCount > 1, "Undersize root node") + } else { + precondition(self.childCount >= Summary.minNodeSize, "Undersized internal node") + } + + let sum: Summary = readInner { + $0.children.reduce(into: .zero) { $0.add($1.summary) } + } + precondition(self.summary == sum, "Mismatching summary") + + guard recursive else { return } + readInner { h in + for child in h.children { + child.invariantCheck(depth: depth + 1, height: height - 1, recursive: true) + } + } + } +#else + @inlinable @inline(__always) + internal func invariantCheck(depth: UInt8, height: UInt8, recursive: Bool = true) { + // Do nothing. + } +#endif +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+_Node.swift b/Sources/RopeModule/Rope/Basics/Rope+_Node.swift new file mode 100644 index 000000000..4d84e1683 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+_Node.swift @@ -0,0 +1,617 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(<5.8) && !COLLECTIONS_SINGLE_MODULE +import _CollectionsUtilities // for 5.8 polyfills +#endif + +extension Rope { + @usableFromInline + internal struct _Node: _RopeItem { + @usableFromInline internal typealias Element = Rope.Element + @usableFromInline internal typealias Summary = Rope.Summary + @usableFromInline internal typealias Index = Rope.Index + @usableFromInline internal typealias _Item = Rope._Item + @usableFromInline internal typealias _Storage = Rope._Storage + @usableFromInline internal typealias _UnsafeHandle = Rope._UnsafeHandle + @usableFromInline internal typealias _Path = Rope._Path + @usableFromInline internal typealias _UnmanagedLeaf = Rope._UnmanagedLeaf + + @usableFromInline + internal var object: AnyObject + + @usableFromInline + internal var summary: Summary + + @inlinable + internal init(leaf: _Storage<_Item>, summary: Summary? = nil) { + self.object = leaf + self.summary = .zero + self.summary = readLeaf { handle in + handle.children.reduce(into: .zero) { $0.add($1.summary) } + } + } + + @inlinable + internal init(inner: _Storage<_Node>, summary: Summary? = nil) { + assert(inner.header.height > 0) + self.object = inner + self.summary = .zero + self.summary = readInner { handle in + handle.children.reduce(into: .zero) { $0.add($1.summary) } + } + } + } +} + +extension Rope._Node: @unchecked Sendable where Element: Sendable { + // @unchecked because `object` is stored as an `AnyObject` above. +} + +extension Rope._Node { + @inlinable + internal var _headerPtr: UnsafePointer<_RopeStorageHeader> { + let p = _getUnsafePointerToStoredProperties(object) + .assumingMemoryBound(to: _RopeStorageHeader.self) + return .init(p) + } + + @inlinable + internal var header: _RopeStorageHeader { + _headerPtr.pointee + } + + @inlinable @inline(__always) + internal var height: UInt8 { header.height } + + @inlinable @inline(__always) + internal var isLeaf: Bool { height == 0 } + + @inlinable @inline(__always) + internal var asLeaf: _Storage<_Item> { + assert(height == 0) + return unsafeDowncast(object, to: _Storage<_Item>.self) + } + + @inlinable @inline(__always) + internal var asInner: _Storage { + assert(height > 0) + return unsafeDowncast(object, to: _Storage.self) + } + + @inlinable @inline(__always) + internal var childCount: Int { header.childCount } + + @inlinable + internal var isEmpty: Bool { childCount == 0 } + + @inlinable + internal var isSingleton: Bool { isLeaf && childCount == 1 } + + @inlinable + internal var isUndersized: Bool { childCount < Summary.minNodeSize } + + @inlinable + internal var isFull: Bool { childCount == Summary.maxNodeSize } +} + +extension Rope._Node { + @inlinable + internal static func createLeaf() -> Self { + Self(leaf: .create(height: 0), summary: Summary.zero) + } + + @inlinable + internal static func createLeaf(_ item: __owned _Item) -> Self { + var leaf = createLeaf() + leaf._appendItem(item) + return leaf + } + + @inlinable + internal static func createInner(height: UInt8) -> Self { + Self(inner: .create(height: height), summary: .zero) + } + + @inlinable + internal static func createInner( + children left: __owned Self, _ right: __owned Self + ) -> Self { + assert(left.height == right.height) + var new = createInner(height: left.height + 1) + new.summary = left.summary + new.summary.add(right.summary) + new.updateInner { h in + h._appendChild(left) + h._appendChild(right) + } + return new + } +} + +extension Rope._Node { + @inlinable @inline(__always) + internal mutating func isUnique() -> Bool { + isKnownUniquelyReferenced(&object) + } + + @inlinable + internal mutating func ensureUnique() { + guard !isKnownUniquelyReferenced(&object) else { return } + self = copy() + } + + @inlinable @inline(never) + internal func copy() -> Self { + if isLeaf { + return Self(leaf: readLeaf { $0.copy() }, summary: self.summary) + } + return Self(inner: readInner { $0.copy() }, summary: self.summary) + } + + @inlinable @inline(never) + internal func copy(slots: Range) -> Self { + if isLeaf { + let (object, summary) = readLeaf { $0.copy(slots: slots) } + return Self(leaf: object, summary: summary) + } + let (object, summary) = readInner { $0.copy(slots: slots) } + return Self(inner: object, summary: summary) + } + + @inlinable @inline(__always) + internal func readLeaf( + _ body: (_UnsafeHandle<_Item>) -> R + ) -> R { + asLeaf.withUnsafeMutablePointers { h, p in + let handle = _UnsafeHandle(isMutable: false, header: h, start: p) + return body(handle) + } + } + + @inlinable @inline(__always) + internal mutating func updateLeaf( + _ body: (_UnsafeHandle<_Item>) -> R + ) -> R { + asLeaf.withUnsafeMutablePointers { h, p in + let handle = _UnsafeHandle(isMutable: true, header: h, start: p) + return body(handle) + } + } + + @inlinable @inline(__always) + internal func readInner( + _ body: (_UnsafeHandle) -> R + ) -> R { + asInner.withUnsafeMutablePointers { h, p in + let handle = _UnsafeHandle(isMutable: false, header: h, start: p) + return body(handle) + } + } + + @inlinable @inline(__always) + internal mutating func updateInner( + _ body: (_UnsafeHandle) -> R + ) -> R { + asInner.withUnsafeMutablePointers { h, p in + let handle = _UnsafeHandle(isMutable: true, header: h, start: p) + return body(handle) + } + } +} + +extension Rope._Node { + @inlinable + internal mutating func _insertItem(_ item: __owned _Item, at slot: Int) { + assert(isLeaf) + ensureUnique() + self.summary.add(item.summary) + updateLeaf { $0._insertChild(item, at: slot) } + } + + @inlinable + internal mutating func _insertNode(_ node: __owned Self, at slot: Int) { + assert(!isLeaf) + assert(self.height == node.height + 1) + ensureUnique() + self.summary.add(node.summary) + updateInner { $0._insertChild(node, at: slot) } + } +} + +extension Rope._Node { + @inlinable + internal mutating func _appendItem(_ item: __owned _Item) { + assert(isLeaf) + ensureUnique() + self.summary.add(item.summary) + updateLeaf { $0._appendChild(item) } + } + + @inlinable + internal mutating func _appendNode(_ node: __owned Self) { + assert(!isLeaf) + ensureUnique() + self.summary.add(node.summary) + updateInner { $0._appendChild(node) } + } +} + +extension Rope._Node { + @inlinable + internal mutating func _removeItem( + at slot: Int + ) -> (removed: _Item, delta: Summary) { + assert(isLeaf) + ensureUnique() + let item = updateLeaf { $0._removeChild(at: slot) } + let delta = item.summary + self.summary.subtract(delta) + return (item, delta) + } + + @inlinable + internal mutating func _removeNode(at slot: Int) -> Self { + assert(!isLeaf) + ensureUnique() + let result = updateInner { $0._removeChild(at: slot) } + self.summary.subtract(result.summary) + return result + } +} + +extension Rope._Node { + @inlinable + internal mutating func split(keeping desiredCount: Int) -> Self { + assert(desiredCount >= 0 && desiredCount <= childCount) + var new = isLeaf ? Self.createLeaf() : Self.createInner(height: height) + guard desiredCount < childCount else { return new } + guard desiredCount > 0 else { + swap(&self, &new) + return new + } + ensureUnique() + new.prependChildren(movingFromSuffixOf: &self, count: childCount - desiredCount) + assert(childCount == desiredCount) + return new + } +} + +extension Rope._Node { + @inlinable + internal mutating func rebalance(nextNeighbor right: inout Rope._Node) -> Bool { + assert(self.height == right.height) + if self.isEmpty { + swap(&self, &right) + return true + } + guard self.isUndersized || right.isUndersized else { return false } + let c = self.childCount + right.childCount + let desired = ( + c <= Summary.maxNodeSize ? c + : c / 2 >= Summary.minNodeSize ? c / 2 + : Summary.minNodeSize + ) + Self.redistributeChildren(&self, &right, to: desired) + return right.isEmpty + } + + @inlinable + internal mutating func rebalance(prevNeighbor left: inout Self) -> Bool { + guard left.rebalance(nextNeighbor: &self) else { return false } + swap(&self, &left) + return true + } + + /// Shift children between `left` and `right` such that the number of children in `left` + /// becomes `target`. + @inlinable + internal static func redistributeChildren( + _ left: inout Self, + _ right: inout Self, + to target: Int + ) { + assert(left.height == right.height) + assert(target >= 0 && target <= Summary.maxNodeSize) + left.ensureUnique() + right.ensureUnique() + + let lc = left.childCount + let rc = right.childCount + let target = Swift.min(target, lc + rc) + let d = target - lc + if d == 0 { return } + + if d > 0 { + left.appendChildren(movingFromPrefixOf: &right, count: d) + } else { + right.prependChildren(movingFromSuffixOf: &left, count: -d) + } + } + + @inlinable + internal mutating func appendChildren( + movingFromPrefixOf other: inout Self, count: Int + ) { + assert(self.height == other.height) + let delta: Summary + if isLeaf { + delta = self.updateLeaf { dst in + other.updateLeaf { src in + dst._appendChildren(movingFromPrefixOf: src, count: count) + } + } + } else { + delta = self.updateInner { dst in + other.updateInner { src in + dst._appendChildren(movingFromPrefixOf: src, count: count) + } + } + } + self.summary.add(delta) + other.summary.subtract(delta) + } + + @inlinable + internal mutating func prependChildren( + movingFromSuffixOf other: inout Self, count: Int + ) { + assert(self.height == other.height) + let delta: Summary + if isLeaf { + delta = self.updateLeaf { dst in + other.updateLeaf { src in + dst._prependChildren(movingFromSuffixOf: src, count: count) + } + } + } else { + delta = self.updateInner { dst in + other.updateInner { src in + dst._prependChildren(movingFromSuffixOf: src, count: count) + } + } + } + self.summary.add(delta) + other.summary.subtract(delta) + } +} + +extension Rope._Node { + @inlinable + internal var _startPath: _Path { + _Path(height: self.height) + } + + @inlinable + internal var lastPath: _Path { + var path = _Path(height: self.height) + _ = descendToLastItem(under: &path) + return path + } + + @inlinable + internal func isAtEnd(_ path: _Path) -> Bool { + path[self.height] == childCount + } + + @inlinable + internal func descendToFirstItem(under path: inout _Path) -> _UnmanagedLeaf { + path.clear(below: self.height + 1) + return unmanagedLeaf(at: path) + } + + @inlinable + internal func descendToLastItem(under path: inout _Path) -> _UnmanagedLeaf { + let h = self.height + let slot = self.childCount - 1 + path[h] = slot + if h > 0 { + return readInner { $0.children[slot].descendToLastItem(under: &path) } + } + return asUnmanagedLeaf + } +} + +extension Rope { + @inlinable + internal func _unmanagedLeaf(at path: _Path) -> _UnmanagedLeaf? { + assert(path.height == self._height) + guard path < _endPath else { return nil } + return root.unmanagedLeaf(at: path) + } +} + +extension Rope._Node { + @inlinable + internal var asUnmanagedLeaf: _UnmanagedLeaf { + assert(height == 0) + return _UnmanagedLeaf(unsafeDowncast(self.object, to: _Storage<_Item>.self)) + } + + @inlinable + internal func unmanagedLeaf(at path: _Path) -> _UnmanagedLeaf { + if height == 0 { + return asUnmanagedLeaf + } + let slot = path[height] + return readInner { $0.children[slot].unmanagedLeaf(at: path) } + } +} + +extension Rope._Node { + @inlinable + internal func formSuccessor(of i: inout Index) -> Bool { + let h = self.height + var slot = i._path[h] + if h == 0 { + slot &+= 1 + guard slot < childCount else { + return false + } + i._path[h] = slot + i._leaf = asUnmanagedLeaf + return true + } + return readInner { + let c = $0.children + if c[slot].formSuccessor(of: &i) { + return true + } + slot += 1 + guard slot < childCount else { + return false + } + i._path[h] = slot + i._leaf = c[slot].descendToFirstItem(under: &i._path) + return true + } + } + + @inlinable + internal func formPredecessor(of i: inout Index) -> Bool { + let h = self.height + var slot = i._path[h] + if h == 0 { + guard slot > 0 else { + return false + } + i._path[h] = slot &- 1 + i._leaf = asUnmanagedLeaf + return true + } + return readInner { + let c = $0.children + if slot < c.count, c[slot].formPredecessor(of: &i) { + return true + } + guard slot > 0 else { + return false + } + slot -= 1 + i._path[h] = slot + i._leaf = c[slot].descendToLastItem(under: &i._path) + return true + } + } +} + +extension Rope._Node { + @inlinable + internal var lastItem: _Item { + get { + self[lastPath] + } + _modify { + assert(childCount > 0) + var state = _prepareModifyLast() + defer { + _ = _finalizeModify(&state) + } + yield &state.item + } + } + + @inlinable + internal var firstItem: _Item { + get { + self[_startPath] + } + _modify { + yield &self[_startPath] + } + } + + @inlinable + internal subscript(path: _Path) -> _Item { + get { + let h = height + let slot = path[h] + precondition(slot < childCount, "Path out of bounds") + guard h == 0 else { + return readInner { $0.children[slot][path] } + } + return readLeaf { $0.children[slot] } + } + @inline(__always) + _modify { + var state = _prepareModify(at: path) + defer { + _ = _finalizeModify(&state) + } + yield &state.item + } + } + + @usableFromInline + internal struct _ModifyState { + @usableFromInline internal var path: _Path + @usableFromInline internal var item: _Item + @usableFromInline internal var summary: Summary + + @inlinable + internal init(path: _Path, item: _Item, summary: Summary) { + self.path = path + self.item = item + self.summary = summary + } + } + + @inlinable + internal mutating func _prepareModify(at path: _Path) -> _ModifyState { + ensureUnique() + let h = height + let slot = path[h] + precondition(slot < childCount, "Path out of bounds") + guard h == 0 else { + return updateInner { $0.mutableChildren[slot]._prepareModify(at: path) } + } + let item = updateLeaf { $0.mutableChildren.moveElement(from: slot) } + return _ModifyState(path: path, item: item, summary: item.summary) + } + + @inlinable + internal mutating func _prepareModifyLast() -> _ModifyState { + var path = _Path(height: height) + return _prepareModifyLast(&path) + } + + @inlinable + internal mutating func _prepareModifyLast(_ path: inout _Path) -> _ModifyState { + ensureUnique() + let h = height + let slot = self.childCount - 1 + path[h] = slot + guard h == 0 else { + return updateInner { $0.mutableChildren[slot]._prepareModifyLast(&path) } + } + let item = updateLeaf { $0.mutableChildren.moveElement(from: slot) } + return _ModifyState(path: path, item: item, summary: item.summary) + } + + @inlinable + internal mutating func _finalizeModify( + _ state: inout _ModifyState + ) -> (delta: Summary, leaf: _UnmanagedLeaf) { + assert(isUnique()) + let h = height + let slot = state.path[h] + assert(slot < childCount, "Path out of bounds") + guard h == 0 else { + let r = updateInner { $0.mutableChildren[slot]._finalizeModify(&state) } + summary.add(r.delta) + return r + } + let delta = state.item.summary.subtracting(state.summary) + updateLeaf { $0.mutableChildren.initializeElement(at: slot, to: state.item) } + summary.add(delta) + return (delta, asUnmanagedLeaf) + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+_Storage.swift b/Sources/RopeModule/Rope/Basics/Rope+_Storage.swift new file mode 100644 index 000000000..c55999221 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+_Storage.swift @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +@usableFromInline +internal struct _RopeStorageHeader { + @usableFromInline var _childCount: UInt16 + @usableFromInline let height: UInt8 + + @inlinable + internal init(height: UInt8) { + self._childCount = 0 + self.height = height + } + + @inlinable + internal var childCount: Int { + get { + numericCast(_childCount) + } + set { + _childCount = numericCast(newValue) + } + } +} + +extension Rope { + @usableFromInline + internal final class _Storage>: + ManagedBuffer<_RopeStorageHeader, Child> + { + @usableFromInline internal typealias Summary = Element.Summary + @usableFromInline internal typealias _UnsafeHandle = Rope._UnsafeHandle + + @inlinable + internal static func create(height: UInt8) -> _Storage { + let object = create(minimumCapacity: Summary.maxNodeSize) { _ in .init(height: height) } + return unsafeDowncast(object, to: _Storage.self) + } + + @inlinable + deinit { + withUnsafeMutablePointers { h, p in + p.deinitialize(count: h.pointee.childCount) + h.pointee._childCount = .max + } + } + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+_UnmanagedLeaf.swift b/Sources/RopeModule/Rope/Basics/Rope+_UnmanagedLeaf.swift new file mode 100644 index 000000000..1c53c19aa --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+_UnmanagedLeaf.swift @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @usableFromInline + internal struct _UnmanagedLeaf { + @usableFromInline internal typealias _Item = Rope._Item + @usableFromInline internal typealias _Leaf = _Storage<_Item> + @usableFromInline internal typealias _UnsafeHandle = Rope._UnsafeHandle + + @usableFromInline var _ref: Unmanaged<_Leaf> + + @inlinable + internal init(_ leaf: __shared _Leaf) { + _ref = .passUnretained(leaf) + } + } +} + +extension Rope._UnmanagedLeaf: Equatable { + @inlinable + internal static func ==(left: Self, right: Self) -> Bool { + left._ref.toOpaque() == right._ref.toOpaque() + } +} + +extension Rope._UnmanagedLeaf { + @inlinable + internal func read( + body: (_UnsafeHandle<_Item>) -> R + ) -> R { + _ref._withUnsafeGuaranteedRef { leaf in + leaf.withUnsafeMutablePointers { h, p in + let handle = _UnsafeHandle(isMutable: false, header: h, start: p) + return body(handle) + } + } + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope+_UnsafeHandle.swift b/Sources/RopeModule/Rope/Basics/Rope+_UnsafeHandle.swift new file mode 100644 index 000000000..a7dcec476 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope+_UnsafeHandle.swift @@ -0,0 +1,229 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @usableFromInline + internal struct _UnsafeHandle> { + @usableFromInline internal typealias Summary = Rope.Summary + @usableFromInline internal typealias Element = Rope.Element + + @usableFromInline + internal let _header: UnsafeMutablePointer<_RopeStorageHeader> + + @usableFromInline + internal let _start: UnsafeMutablePointer +#if DEBUG + @usableFromInline + internal let _isMutable: Bool +#endif + + @inlinable + internal init( + isMutable: Bool, + header: UnsafeMutablePointer<_RopeStorageHeader>, + start: UnsafeMutablePointer + ) { + self._header = header + self._start = start +#if DEBUG + self._isMutable = isMutable +#endif + } + + @inlinable @inline(__always) + internal func assertMutable() { +#if DEBUG + assert(_isMutable) +#endif + } + } +} + +extension Rope._UnsafeHandle { + @inlinable @inline(__always) + internal var capacity: Int { Summary.maxNodeSize } + + @inlinable @inline(__always) + internal var height: UInt8 { _header.pointee.height } + + @inlinable + internal var childCount: Int { + get { _header.pointee.childCount } + nonmutating set { + assertMutable() + _header.pointee.childCount = newValue + } + } + + @inlinable + internal var children: UnsafeBufferPointer { + UnsafeBufferPointer(start: _start, count: childCount) + } + + @inlinable + internal func child(at slot: Int) -> Child? { + assert(slot >= 0) + guard slot < childCount else { return nil } + return (_start + slot).pointee + } + + @inlinable + internal var mutableChildren: UnsafeMutableBufferPointer { + assertMutable() + return UnsafeMutableBufferPointer(start: _start, count: childCount) + } + + @inlinable + internal func mutableChildPtr(at slot: Int) -> UnsafeMutablePointer { + assertMutable() + assert(slot >= 0 && slot < childCount) + return _start + slot + } + + @inlinable + internal var mutableBuffer: UnsafeMutableBufferPointer { + assertMutable() + return UnsafeMutableBufferPointer(start: _start, count: capacity) + } + + @inlinable + internal func copy() -> Rope._Storage { + let new = Rope._Storage.create(height: self.height) + let c = self.childCount + new.header.childCount = c + new.withUnsafeMutablePointerToElements { target in + target.initialize(from: self._start, count: c) + } + return new + } + + @inlinable + internal func copy( + slots: Range + ) -> (object: Rope._Storage, summary: Summary) { + assert(slots.lowerBound >= 0 && slots.upperBound <= childCount) + let object = Rope._Storage.create(height: self.height) + let c = slots.count + let summary = object.withUnsafeMutablePointers { h, p in + h.pointee.childCount = c + p.initialize(from: self._start + slots.lowerBound, count: slots.count) + return UnsafeBufferPointer(start: p, count: c)._sum() + } + return (object, summary) + } + + @inlinable + internal func _insertChild(_ child: __owned Child, at slot: Int) { + assertMutable() + assert(childCount < capacity) + assert(slot >= 0 && slot <= childCount) + (_start + slot + 1).moveInitialize(from: _start + slot, count: childCount - slot) + (_start + slot).initialize(to: child) + childCount += 1 + } + + @inlinable + internal func _appendChild(_ child: __owned Child) { + assertMutable() + assert(childCount < capacity) + (_start + childCount).initialize(to: child) + childCount += 1 + } + + @inlinable + internal func _removeChild(at slot: Int) -> Child { + assertMutable() + assert(slot >= 0 && slot < childCount) + let result = (_start + slot).move() + (_start + slot).moveInitialize(from: _start + slot + 1, count: childCount - slot - 1) + childCount -= 1 + return result + } + + @inlinable + internal func _removePrefix(_ n: Int) -> Summary { + assertMutable() + assert(n <= childCount) + var delta = Summary.zero + let c = mutableChildren + for i in 0 ..< n { + let child = c.moveElement(from: i) + delta.add(child.summary) + } + childCount -= n + _start.moveInitialize(from: _start + n, count: childCount) + return delta + } + + @inlinable + internal func _removeSuffix(_ n: Int) -> Summary { + assertMutable() + assert(n <= childCount) + var delta = Summary.zero + let c = mutableChildren + for i in childCount - n ..< childCount { + let child = c.moveElement(from: i) + delta.add(child.summary) + } + childCount -= n + return delta + } + + @inlinable + internal func _appendChildren( + movingFromPrefixOf src: Self, count: Int + ) -> Summary { + assertMutable() + src.assertMutable() + assert(self.height == src.height) + guard count > 0 else { return .zero } + assert(count >= 0 && count <= src.childCount) + assert(count <= capacity - self.childCount) + + (_start + childCount).moveInitialize(from: src._start, count: count) + src._start.moveInitialize(from: src._start + count, count: src.childCount - count) + childCount += count + src.childCount -= count + return children.suffix(count)._sum() + } + + @inlinable + internal func _prependChildren( + movingFromSuffixOf src: Self, count: Int + ) -> Summary { + assertMutable() + src.assertMutable() + assert(self.height == src.height) + guard count > 0 else { return .zero } + assert(count >= 0 && count <= src.childCount) + assert(count <= capacity - childCount) + + (_start + count).moveInitialize(from: _start, count: childCount) + _start.moveInitialize(from: src._start + src.childCount - count, count: count) + childCount += count + src.childCount -= count + return children.prefix(count)._sum() + } + + @inlinable + internal func distance( + from start: Int, to end: Int, in metric: some RopeMetric + ) -> Int { + if start <= end { + return children[start ..< end].reduce(into: 0) { + $0 += metric._nonnegativeSize(of: $1.summary) + } + } + return -children[end ..< start].reduce(into: 0) { + $0 += metric._nonnegativeSize(of: $1.summary) + } + } +} diff --git a/Sources/RopeModule/Rope/Basics/Rope.swift b/Sources/RopeModule/Rope/Basics/Rope.swift new file mode 100644 index 000000000..5411fb2a5 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/Rope.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +/// An ordered data structure of `Element` values that organizes itself into a tree. +/// The rope is augmented by the commutative group specified by `Element.Summary`, enabling +/// quick lookup operations. +public struct Rope { + @usableFromInline + internal var _root: _Node? + + @usableFromInline + internal var _version: _RopeVersion + + @inlinable + public init() { + self._root = nil + self._version = _RopeVersion() + } + + @inlinable + internal init(root: _Node?) { + self._root = root + self._version = _RopeVersion() + } + + @inlinable + internal var root: _Node { + @inline(__always) get { _root.unsafelyUnwrapped } + @inline(__always) _modify { yield &_root! } + } + + @inlinable + public init(_ value: Element) { + self._root = .createLeaf(_Item(value)) + self._version = _RopeVersion() + } +} + +extension Rope: Sendable where Element: Sendable {} + +extension Rope { + @inlinable + internal mutating func _ensureUnique() { + guard _root != nil else { return } + root.ensureUnique() + } +} + +extension Rope { + @inlinable + public var isSingleton: Bool { + guard _root != nil else { return false } + return root.isSingleton + } +} + +extension Rope { + @inlinable + public func isIdentical(to other: Self) -> Bool { + self._root?.object === other._root?.object + } +} diff --git a/Sources/RopeModule/Rope/Basics/RopeElement.swift b/Sources/RopeModule/Rope/Basics/RopeElement.swift new file mode 100644 index 000000000..1bfb0d84a --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/RopeElement.swift @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +/// The element type of a rope. Rope elements are expected to be container types +/// of their own, with logical positions within them addressed by an `Index` +/// type, similar to `Collection` indices. +/// +/// However, rope elements aren't required conform to `Collection`. In fact, +/// they often support multiple different ways to interpret/project their +/// contents, similar to `String`'s views. In some cases, they may just be +/// individual, undivisable items of varying sizes -- although it's usually +/// a better to use a simple fixed-size collection type instead. +/// +/// Each such projection may come with a different idea for how large a rope +/// element is -- this is modeled by the `RopeSummary` and `RopeMetric` +/// protocols. The `summary` property returns the size of the element as an +/// additive value, which can be projected to integer sizes using one of the +/// associated rope metrics. +public protocol RopeElement { + /// The commutative group that is used to augment the tree. + associatedtype Summary: RopeSummary + + /// A type whose values address a particular pieces of content in this rope + /// element. + associatedtype Index: Comparable + + /// Returns the summary of `self`. + var summary: Summary { get } + + var isEmpty: Bool { get } + var isUndersized: Bool { get } + + /// Check the consistency of `self`. + func invariantCheck() + + /// Rebalance contents between `self` and its next neighbor `right`, + /// eliminating an `isUndersized` condition on one of the inputs, if possible. + /// + /// On return, `self` is expected to be non-empty and well-sized. + /// + /// - Parameter right: The element immediately following `self` in some rope. + /// - Precondition: Either `self` or `right` must be undersized. + /// - Returns: A boolean value indicating whether `right` has become empty. + mutating func rebalance(nextNeighbor right: inout Self) -> Bool + + /// Rebalance contents between `self` and its previous neighbor `left`, + /// eliminating an `isUndersized` condition on one of the inputs, if possible. + /// + /// On return, `self` is expected to be non-empty and well-sized. + /// + /// - Parameter left: The element immediately preceding `self` in some rope. + /// - Precondition: Either `left` or `self` must be undersized. + /// - Returns: A boolean value indicating whether `left` has become empty. + mutating func rebalance(prevNeighbor left: inout Self) -> Bool + + /// Split `self` into two pieces at the specified index, keeping contents + /// up to `index` in `self`, and moving the rest of it into a new item. + mutating func split(at index: Index) -> Self +} + +extension RopeElement { + @inlinable + public mutating func rebalance(prevNeighbor left: inout Self) -> Bool { + guard left.rebalance(nextNeighbor: &self) else { return false } + swap(&self, &left) + return true + } +} + diff --git a/Sources/RopeModule/Rope/Basics/RopeMetric.swift b/Sources/RopeModule/Rope/Basics/RopeMetric.swift new file mode 100644 index 000000000..d6b052697 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/RopeMetric.swift @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +public protocol RopeMetric: Sendable { + associatedtype Element: RopeElement + + /// Returns the size of a summarized rope element in this metric. + func size(of summary: Element.Summary) -> Int + + /// Returns an index addressing the content at the given offset from + /// the start of the specified rope element. + /// + /// - Parameter offset: An integer offset from the start of `element` in this + /// metric, not exceeding `size(of: element.summary)`. + /// - Parameter element: An arbitary rope element. + /// - Returns: The index addressing the desired position in the input element. + func index(at offset: Int, in element: Element) -> Element.Index +} + +extension RopeMetric { + @inlinable @inline(__always) + internal func _nonnegativeSize(of summary: Element.Summary) -> Int { + let r = size(of: summary) + assert(r >= 0) + return r + } +} diff --git a/Sources/RopeModule/Rope/Basics/RopeSummary.swift b/Sources/RopeModule/Rope/Basics/RopeSummary.swift new file mode 100644 index 000000000..bbad6fc5b --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/RopeSummary.swift @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +/// A commutative group that is used to augment a tree, enabling quick lookup operations. +public protocol RopeSummary: Equatable, Sendable { + static var maxNodeSize: Int { get } + static var nodeSizeBitWidth: Int { get } + + /// The identity element of the group. + static var zero: Self { get } + + /// Returns a Boolean value that indicates whether `self` is the identity element. + var isZero: Bool { get } + + /// A commutative and associative operation that combines two instances. + /// + /// (As is usually the case, this operation is not necessarily closed over `Self` in practice -- + /// e.g., some results may not be representable.) + mutating func add(_ other: Self) + + /// A (potentially partial) subtraction function that undoes a previous combination of the given + /// element to `self`. + /// + /// The inverse of any instance can be calculated by subtracting it from the `zero` instance. + /// (However, conforming types are free to require that `subtract` only be called on values + /// that "include" the given `other`.) + mutating func subtract(_ other: Self) +} + +extension RopeSummary { + @inlinable @inline(__always) + public static var nodeSizeBitWidth: Int { + Int.bitWidth - maxNodeSize.leadingZeroBitCount + } + + @inlinable @inline(__always) + public static var minNodeSize: Int { (maxNodeSize + 1) / 2 } +} + +extension RopeSummary { + @inlinable + public func adding(_ other: Self) -> Self { + var c = self + c.add(other) + return c + } + + @inlinable + public func subtracting(_ other: Self) -> Self { + var c = self + c.subtract(other) + return c + } +} diff --git a/Sources/RopeModule/Rope/Basics/_RopeItem.swift b/Sources/RopeModule/Rope/Basics/_RopeItem.swift new file mode 100644 index 000000000..db24ab041 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/_RopeItem.swift @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +/// An internal protocol describing a summarizable entity that isn't a full `RopeElement`. +/// +/// Used as an implementation detail to increase code reuse across internal nodes and leaf nodes. +/// (Ideally `Rope._Node` would just conform to the full `RopeElement` protocol on its own, but +/// while that's an obvious refactoring idea, it hasn't happened yet.) +@usableFromInline +internal protocol _RopeItem { + associatedtype Summary: RopeSummary + + var summary: Summary { get } +} + +extension Sequence where Element: _RopeItem { + @inlinable + internal func _sum() -> Element.Summary { + self.reduce(into: .zero) { $0.add($1.summary) } + } +} + +extension Rope: _RopeItem { + public typealias Summary = Element.Summary + + @inlinable + public var summary: Summary { + guard _root != nil else { return .zero } + return root.summary + } +} + +extension Rope { + /// A trivial wrapper around a rope's Element type, giving it `_RopeItem` conformance without + /// having to make the protocol public. + @usableFromInline + internal struct _Item { + @usableFromInline internal var value: Element + + @inlinable + internal init(_ value: Element) { self.value = value } + } +} + +extension Rope._Item: _RopeItem { + @usableFromInline internal typealias Summary = Rope.Summary + + @inlinable + internal var summary: Summary { value.summary } +} + +extension Rope._Item: CustomStringConvertible { + @usableFromInline + internal var description: String { + "\(value)" + } +} + +extension Rope._Item { + @inlinable + internal var isEmpty: Bool { value.isEmpty } + + @inlinable + internal var isUndersized: Bool { value.isUndersized } + + @inlinable + internal mutating func rebalance(nextNeighbor right: inout Self) -> Bool { + value.rebalance(nextNeighbor: &right.value) + } + + @inlinable + internal mutating func rebalance(prevNeighbor left: inout Self) -> Bool { + value.rebalance(prevNeighbor: &left.value) + } + + @usableFromInline internal typealias Index = Element.Index + + @inlinable + internal mutating func split(at index: Index) -> Self { + Self(self.value.split(at: index)) + } +} diff --git a/Sources/RopeModule/Rope/Basics/_RopePath.swift b/Sources/RopeModule/Rope/Basics/_RopePath.swift new file mode 100644 index 000000000..492db787e --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/_RopePath.swift @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +@usableFromInline +@frozen // Not really! This module isn't ABI stable +internal struct _RopePath { + // ┌──────────────────────────────────┬────────┐ + // │ b63:b8 │ b7:b0 │ + // ├──────────────────────────────────┼────────┤ + // │ path │ height │ + // └──────────────────────────────────┴────────┘ + @usableFromInline internal var _value: UInt64 + + @inlinable @inline(__always) + internal static var _pathBitWidth: Int { 56 } + + @inlinable + internal init(_value: UInt64) { + self._value = _value + } + + @inlinable + internal init(height: UInt8) { + self._value = UInt64(truncatingIfNeeded: height) + assert((Int(height) + 1) * Summary.nodeSizeBitWidth <= Self._pathBitWidth) + } +} + +extension Rope { + @usableFromInline internal typealias _Path = _RopePath +} + +extension _RopePath: Equatable { + @inlinable + internal static func ==(left: Self, right: Self) -> Bool { + left._value == right._value + } +} +extension _RopePath: Hashable { + @inlinable + internal func hash(into hasher: inout Hasher) { + hasher.combine(_value) + } +} + +extension _RopePath: Comparable { + @inlinable + internal static func <(left: Self, right: Self) -> Bool { + left._value < right._value + } +} + +extension _RopePath: CustomStringConvertible { + @usableFromInline + internal var description: String { + var r = "<" + for h in stride(from: height, through: 0, by: -1) { + r += "\(self[h])" + if h > 0 { r += ", " } + } + r += ">" + return r + } +} + +extension _RopePath { + @inlinable + internal var height: UInt8 { + UInt8(truncatingIfNeeded: _value) + } + + @inlinable + internal mutating func popRoot() { + let heightMask: UInt64 = 255 + let h = height + assert(h > 0 && self[h] == 0) + _value &= ~heightMask + _value |= UInt64(truncatingIfNeeded: h - 1) & heightMask + } + + @inlinable + internal subscript(height: UInt8) -> Int { + get { + assert(height <= self.height) + let shift = 8 + Int(height) * Summary.nodeSizeBitWidth + let mask: UInt64 = (1 &<< Summary.nodeSizeBitWidth) &- 1 + return numericCast((_value &>> shift) & mask) + } + set { + assert(height <= self.height) + assert(newValue >= 0 && newValue <= Summary.maxNodeSize) + let shift = 8 + Int(height) * Summary.nodeSizeBitWidth + let mask: UInt64 = (1 &<< Summary.nodeSizeBitWidth) &- 1 + _value &= ~(mask &<< shift) + _value |= numericCast(newValue) &<< shift + } + } + + @inlinable + internal func isEmpty(below height: UInt8) -> Bool { + let shift = Int(height) * Summary.nodeSizeBitWidth + assert(shift + Summary.nodeSizeBitWidth <= Self._pathBitWidth) + let mask: UInt64 = ((1 &<< shift) - 1) &<< 8 + return (_value & mask) == 0 + } + + @inlinable + internal mutating func clear(below height: UInt8) { + let shift = Int(height) * Summary.nodeSizeBitWidth + assert(shift + Summary.nodeSizeBitWidth <= Self._pathBitWidth) + let mask: UInt64 = ((1 &<< shift) - 1) &<< 8 + _value &= ~mask + } +} + diff --git a/Sources/RopeModule/Rope/Basics/_RopeVersion.swift b/Sources/RopeModule/Rope/Basics/_RopeVersion.swift new file mode 100644 index 000000000..26e7098b1 --- /dev/null +++ b/Sources/RopeModule/Rope/Basics/_RopeVersion.swift @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +@usableFromInline +@frozen // Not really! This module isn't ABI stable. +internal struct _RopeVersion { + // FIXME: Replace this probabilistic mess with atomics when Swift gets its act together. + @usableFromInline internal var _value: UInt + + @inlinable + internal init() { + var rng = SystemRandomNumberGenerator() + _value = rng.next() + } + + @inlinable + internal init(_ value: UInt) { + self._value = value + } +} + +extension _RopeVersion: Equatable { + @inlinable + internal static func ==(left: Self, right: Self) -> Bool { + left._value == right._value + } +} + +extension _RopeVersion { + @inlinable + internal mutating func bump() { + _value &+= 1 + } + + @inlinable + internal mutating func reset() { + var rng = SystemRandomNumberGenerator() + _value = rng.next() + } +} diff --git a/Sources/RopeModule/Rope/Conformances/Rope+Collection.swift b/Sources/RopeModule/Rope/Conformances/Rope+Collection.swift new file mode 100644 index 000000000..44ab25ab6 --- /dev/null +++ b/Sources/RopeModule/Rope/Conformances/Rope+Collection.swift @@ -0,0 +1,493 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public func isValid(_ index: Index) -> Bool { + index._version == _version + } + + @inlinable + public func validate(_ index: Index) { + precondition(isValid(index), "Invalid index") + } + + @inlinable + internal mutating func _invalidateIndices() { + _version.bump() + } + + /// Validate `index` and fill out all cached information in it, + /// to speed up subsequent lookups. + @inlinable + public func grease(_ index: inout Index) { + validate(index) + guard index._leaf == nil else { return } + index._leaf = _unmanagedLeaf(at: index._path) + } +} + +extension Rope { + @inlinable + public var _height: UInt8 { + _root?.height ?? 0 + } + + @inlinable + internal var _startPath: _Path { + _Path(height: _height) + } + + @inlinable + internal var _endPath: _Path { + guard let root = _root else { return _startPath } + var path = _Path(height: _height) + path[_height] = root.childCount + return path + } +} + +extension Rope: BidirectionalCollection { + public typealias SubSequence = Slice + + @inlinable + public var isEmpty: Bool { + guard _root != nil else { return true } + return root.childCount == 0 + } + + @inlinable + public var startIndex: Index { + // Note: `leaf` is intentionally not set here, to speed up accessing this property. + return Index(version: _version, path: _startPath, leaf: nil) + } + + @inlinable + public var endIndex: Index { + Index(version: _version, path: _endPath, leaf: nil) + } + + @inlinable + public func index(after i: Index) -> Index { + var i = i + formIndex(after: &i) + return i + } + + @inlinable + public func index(before i: Index) -> Index { + var i = i + formIndex(before: &i) + return i + } + + @inlinable + public func formIndex(after i: inout Index) { + validate(i) + precondition(i < endIndex, "Can't move after endIndex") + if let leaf = i._leaf { + let done = leaf.read { + let slot = i._path[$0.height] &+ 1 + guard slot < $0.childCount else { return false } + i._path[$0.height] = slot + return true + } + if done { return } + } + if !root.formSuccessor(of: &i) { + i = endIndex + } + } + + @inlinable + public func formIndex(before i: inout Index) { + validate(i) + precondition(i > startIndex, "Can't move before startIndex") + if let leaf = i._leaf { + let done = leaf.read { + let slot = i._path[$0.height] + guard slot > 0 else { return false } + i._path[$0.height] = slot &- 1 + return true + } + if done { return } + } + let success = root.formPredecessor(of: &i) + precondition(success, "Invalid index") + } + + @inlinable + public subscript(i: Index) -> Element { + get { + validate(i) + if let ref = i._leaf { + return ref.read { + $0.children[i._path[$0.height]].value + } + } + return root[i._path].value + } + @inline(__always) _modify { + validate(i) + // Note: we must not use _leaf -- it may not be on a unique path. + defer { _invalidateIndices() } + yield &root[i._path].value + } + } +} + +extension Rope { + /// Update the element at the given index, while keeping the index valid. + @inlinable + public mutating func update( + at index: inout Index, + by body: (inout Element) -> R + ) -> R { + validate(index) + var state = root._prepareModify(at: index._path) + defer { + _invalidateIndices() + index._version = self._version + index._leaf = root._finalizeModify(&state).leaf + } + return body(&state.item.value) + } +} + +extension Rope { + @inlinable @inline(__always) + public static var _maxHeight: Int { + _Path._pathBitWidth / Summary.nodeSizeBitWidth + } + + /// The estimated maximum number of items that can fit in this rope in the worst possible case, + /// i.e., when the tree consists of minimum-sized nodes. (The data structure itself has no + /// inherent limit, but this implementation of it is limited by the fixed 56-bit path + /// representation used in the `Index` type.) + /// + /// This is one less than the minimum possible size for a rope whose size exceeds the maximum. + @inlinable + public static var _minimumCapacity: Int { + var c = 2 + for _ in 0 ..< _maxHeight { + let (r, overflow) = c.multipliedReportingOverflow(by: Summary.minNodeSize) + if overflow { return .max } + c = r + } + return c - 1 + } + + /// The maximum number of items that can fit in this rope in the best possible case, i.e., when + /// the tree consists of maximum-sized nodes. (The data structure itself has no inherent limit, + /// but this implementation of it is limited by the fixed 56-bit path representation used in + /// the `Index` type.) + @inlinable + public static var _maximumCapacity: Int { + var c = 1 + for _ in 0 ... _maxHeight { + let (r, overflow) = c.multipliedReportingOverflow(by: Summary.maxNodeSize) + if overflow { return .max } + c = r + } + return c + } +} + +extension Rope { + @inlinable + public func count(in metric: some RopeMetric) -> Int { + guard _root != nil else { return 0 } + return root.count(in: metric) + } +} + +extension Rope._Node { + @inlinable + public func count(in metric: some RopeMetric) -> Int { + metric._nonnegativeSize(of: self.summary) + } +} + +extension Rope { + @inlinable + public func distance(from start: Index, to end: Index, in metric: some RopeMetric) -> Int { + validate(start) + validate(end) + if start == end { return 0 } + precondition(_root != nil, "Invalid index") + if start._leaf == end._leaf, let leaf = start._leaf { + // Fast path: both indices are pointing within the same leaf. + return leaf.read { + let h = $0.height + let a = start._path[h] + let b = end._path[h] + return $0.distance(from: a, to: b, in: metric) + } + } + if start < end { + return root.distance(from: start, to: end, in: metric) + } + return -root.distance(from: end, to: start, in: metric) + } + + @inlinable + public func offset(of index: Index, in metric: some RopeMetric) -> Int { + validate(index) + if _root == nil { return 0 } + return root.distanceFromStart(to: index, in: metric) + } +} + +extension Rope._Node { + @inlinable + internal func distanceFromStart( + to index: Index, in metric: some RopeMetric + ) -> Int { + let slot = index._path[height] + precondition(slot <= childCount, "Invalid index") + if slot == childCount { + precondition(index._isEmpty(below: height), "Invalid index") + return metric._nonnegativeSize(of: self.summary) + } + if height == 0 { + return readLeaf { $0.distance(from: 0, to: slot, in: metric) } + } + return readInner { + var distance = $0.distance(from: 0, to: slot, in: metric) + distance += $0.children[slot].distanceFromStart(to: index, in: metric) + return distance + } + } + + @inlinable + internal func distanceToEnd( + from index: Index, in metric: some RopeMetric + ) -> Int { + let d = metric._nonnegativeSize(of: self.summary) - self.distanceFromStart(to: index, in: metric) + assert(d >= 0) + return d + } + + @inlinable + internal func distance( + from start: Index, to end: Index, in metric: some RopeMetric + ) -> Int { + assert(start < end) + let a = start._path[height] + let b = end._path[height] + precondition(a < childCount, "Invalid index") + precondition(b <= childCount, "Invalid index") + assert(a <= b) + if b == childCount { + precondition(end._isEmpty(below: height), "Invalid index") + return distanceToEnd(from: start, in: metric) + } + if height == 0 { + assert(a < b) + return readLeaf { $0.distance(from: a, to: b, in: metric) } + } + return readInner { + let c = $0.children + if a == b { + return c[a].distance(from: start, to: end, in: metric) + } + var d = c[a].distanceToEnd(from: start, in: metric) + d += $0.distance(from: a + 1, to: b, in: metric) + d += c[b].distanceFromStart(to: end, in: metric) + return d + } + } +} + +extension Rope { + @inlinable + public func formIndex( + _ i: inout Index, + offsetBy distance: inout Int, + in metric: some RopeMetric, + preferEnd: Bool + ) { + validate(i) + if _root == nil { + precondition(distance == 0, "Position out of bounds") + return + } + if distance <= 0 { + distance = -distance + let success = root.seekBackward( + from: &i, by: &distance, in: metric, preferEnd: preferEnd) + precondition(success, "Position out of bounds") + return + } + if let leaf = i._leaf { + // Fast path: move within a single leaf + let r = leaf.read { + $0._seekForwardInLeaf(from: &i._path, by: &distance, in: metric, preferEnd: preferEnd) + } + if r { return } + } + if root.seekForward(from: &i, by: &distance, in: metric, preferEnd: preferEnd) { + return + } + precondition(distance == 0, "Position out of bounds") + i = endIndex + } + + @inlinable + public func index( + _ i: Index, + offsetBy distance: Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> (index: Index, remaining: Int) { + var i = i + var distance = distance + formIndex(&i, offsetBy: &distance, in: metric, preferEnd: preferEnd) + return (i, distance) + } +} + +extension Rope._UnsafeHandle { + @inlinable + func _seekForwardInLeaf( + from path: inout Rope._Path, + by distance: inout Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> Bool { + assert(distance >= 0) + assert(height == 0) + let c = children + var slot = path[0] + defer { path[0] = slot } + while slot < c.count { + let d = metric._nonnegativeSize(of: c[slot].summary) + if preferEnd ? d >= distance : d > distance { + return true + } + distance &-= d + slot &+= 1 + } + return false + } + + @inlinable + func _seekBackwardInLeaf( + from path: inout Rope._Path, + by distance: inout Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> Bool { + assert(distance >= 0) + assert(height == 0) + let c = children + var slot = path[0] &- 1 + while slot >= 0 { + let d = metric._nonnegativeSize(of: c[slot].summary) + if preferEnd ? d > distance : d >= distance { + path[0] = slot + distance = d &- distance + return true + } + distance &-= d + slot &-= 1 + } + return false + } +} + +extension Rope._Node { + @inlinable + func seekForward( + from i: inout Index, + by distance: inout Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> Bool { + assert(distance >= 0) + + if height == 0 { + let r = readLeaf { + $0._seekForwardInLeaf(from: &i._path, by: &distance, in: metric, preferEnd: preferEnd) + } + if r { + i._leaf = asUnmanagedLeaf + } + return r + } + + return readInner { + var slot = i._path[height] + precondition(slot < childCount, "Invalid index") + let c = $0.children + if c[slot].seekForward(from: &i, by: &distance, in: metric, preferEnd: preferEnd) { + return true + } + slot &+= 1 + while slot < c.count { + let d = metric.size(of: c[slot].summary) + if preferEnd ? d >= distance : d > distance { + i._path[$0.height] = slot + i._clear(below: $0.height) + let success = c[slot].seekForward( + from: &i, by: &distance, in: metric, preferEnd: preferEnd) + precondition(success) + return true + } + distance &-= d + slot &+= 1 + } + return false + } + } + + @inlinable + func seekBackward( + from i: inout Index, + by distance: inout Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> Bool { + assert(distance >= 0) + guard distance > 0 || preferEnd else { return true } + if height == 0 { + return readLeaf { + $0._seekBackwardInLeaf(from: &i._path, by: &distance, in: metric, preferEnd: preferEnd) + } + } + + return readInner { + var slot = i._path[height] + precondition(slot <= childCount, "Invalid index") + let c = $0.children + if slot < childCount, + c[slot].seekBackward(from: &i, by: &distance, in: metric, preferEnd: preferEnd) { + return true + } + slot -= 1 + while slot >= 0 { + let d = metric.size(of: c[slot].summary) + if preferEnd ? d > distance : d >= distance { + i._path[$0.height] = slot + i._clear(below: $0.height) + distance = d - distance + let success = c[slot].seekForward( + from: &i, by: &distance, in: metric, preferEnd: preferEnd) + precondition(success) + return true + } + distance -= d + slot -= 1 + } + return false + } + } +} diff --git a/Sources/RopeModule/Rope/Conformances/Rope+Index.swift b/Sources/RopeModule/Rope/Conformances/Rope+Index.swift new file mode 100644 index 000000000..1e6866886 --- /dev/null +++ b/Sources/RopeModule/Rope/Conformances/Rope+Index.swift @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @frozen // Not really! This module isn't ABI stable. + public struct Index: @unchecked Sendable { + @usableFromInline internal typealias Summary = Rope.Summary + @usableFromInline internal typealias _Path = Rope._Path + + @usableFromInline + internal var _version: _RopeVersion + + @usableFromInline + internal var _path: _Path + + /// A direct reference to the leaf node addressed by this index. + /// This must only be dereferenced while we own a tree with a matching + /// version. + @usableFromInline + internal var _leaf: _UnmanagedLeaf? + + @inlinable + internal init( + version: _RopeVersion, path: _Path, leaf: __shared _UnmanagedLeaf? + ) { + self._version = version + self._path = path + self._leaf = leaf + } + } +} + +extension Rope.Index { + @inlinable + internal static var _invalid: Self { + Self(version: _RopeVersion(0), path: _RopePath(_value: .max), leaf: nil) + } + + @inlinable + internal var _isValid: Bool { + _path._value != .max + } +} + +extension Rope.Index: Equatable { + @inlinable + public static func ==(left: Self, right: Self) -> Bool { + left._path == right._path + } +} +extension Rope.Index: Hashable { + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(_path) + } +} + +extension Rope.Index: Comparable { + @inlinable + public static func <(left: Self, right: Self) -> Bool { + left._path < right._path + } +} + +extension Rope.Index: CustomStringConvertible { + public var description: String { + "\(_path)" + } +} + +extension Rope.Index { + @inlinable + internal var _height: UInt8 { + _path.height + } + + @inlinable + internal func _isEmpty(below height: UInt8) -> Bool { + _path.isEmpty(below: height) + } + + @inlinable + internal mutating func _clear(below height: UInt8) { + _path.clear(below: height) + } +} diff --git a/Sources/RopeModule/Rope/Conformances/Rope+Sequence.swift b/Sources/RopeModule/Rope/Conformances/Rope+Sequence.swift new file mode 100644 index 000000000..471064b48 --- /dev/null +++ b/Sources/RopeModule/Rope/Conformances/Rope+Sequence.swift @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope: Sequence { + @inlinable + public func makeIterator() -> Iterator { + Iterator(self, from: self.startIndex) + } + + @inlinable + public func makeIterator(from start: Index) -> Iterator { + Iterator(self, from: start) + } + + @frozen // Not really! This module isn't ABI stable. + public struct Iterator: IteratorProtocol { + @usableFromInline + internal let _rope: Rope + + @usableFromInline + internal var _index: Index + + @inlinable + internal init(_ rope: Rope, from start: Index) { + rope.validate(start) + self._rope = rope + self._index = start + self._rope.grease(&_index) + } + + @inlinable + public mutating func next() -> Element? { + guard let leaf = _index._leaf else { return nil } + let item = leaf.read { $0.children[_index._path[0]].value } + _rope.formIndex(after: &_index) + return item + } + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Append.swift b/Sources/RopeModule/Rope/Operations/Rope+Append.swift new file mode 100644 index 000000000..ddc455249 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Append.swift @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public mutating func append(_ item: __owned Element) { + _invalidateIndices() + if _root == nil { + _root = .createLeaf(_Item(item)) + return + } + if let spawn = root.append(_Item(item)) { + _root = .createInner(children: root, spawn) + } + } +} + +extension Rope._Node { + @inlinable + internal mutating func append(_ item: __owned _Item) -> Self? { + var item = item + if item.isUndersized, !self.isEmpty, self.lastItem.rebalance(nextNeighbor: &item) { + return nil + } + ensureUnique() + if height > 0 { + var summary = self.summary + let spawn = updateInner { + let p = $0.mutableChildPtr(at: $0.childCount - 1) + summary.subtract(p.pointee.summary) + let spawn = p.pointee.append(item) + summary.add(p.pointee.summary) + return spawn + } + self.summary = summary + guard let spawn = spawn else { return nil } + +#if true // Compress existing nodes if possible. + updateInner { + let c = $0.mutableChildren + let s = c[c.count - 2].childCount + c[c.count - 1].childCount + if s <= Summary.maxNodeSize { + Self.redistributeChildren(&c[c.count - 2], &c[c.count - 1], to: s) + let removed = $0._removeChild(at: c.count - 1) + assert(removed.childCount == 0) + } + } +#endif + guard isFull else { + _appendNode(spawn) + return nil + } + + var spawn2 = split(keeping: Summary.minNodeSize) + spawn2._appendNode(spawn) + return spawn2 + } + guard isFull else { + _appendItem(item) + return nil + } + var spawn = split(keeping: Summary.minNodeSize) + spawn._appendItem(item) + return spawn + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Extract.swift b/Sources/RopeModule/Rope/Operations/Rope+Extract.swift new file mode 100644 index 000000000..7749b3cf0 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Extract.swift @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public func extract(_ offsetRange: Range, in metric: some RopeMetric) -> Self { + extract(from: offsetRange.lowerBound, to: offsetRange.upperBound, in: metric) + } + + @inlinable + public func extract(from start: Int, to end: Int, in metric: some RopeMetric) -> Self { + if _root == nil { + precondition(start == 0 && end == 0, "Invalid range") + return Self() + } + var builder = Builder() + root.extract(from: start, to: end, in: metric, into: &builder) + return builder.finalize() + } +} + +extension Rope._Node { + @inlinable + internal func extract( + from start: Int, + to end: Int, + in metric: some RopeMetric, + into builder: inout Rope.Builder + ) { + let size = metric.size(of: summary) + precondition(start >= 0 && start <= end && end <= size, "Range out of bounds") + + guard start != end else { return } + + if self.isLeaf { + self.readLeaf { + let l = $0.findSlot(at: start, in: metric, preferEnd: false) + let u = $0.findSlot(from: l, offsetBy: end - start, in: metric, preferEnd: true) + let c = $0.children + if l.slot == u.slot { + var item = c[l.slot] + let i = metric.index(at: l.remaining, in: item.value) + var item2 = item.split(at: i) + let j = metric.index( + at: u.remaining - metric._nonnegativeSize(of: item.summary), + in: item2.value) + _ = item2.split(at: j) + builder._insertBeforeTip(item2) + return + } + assert(l.slot < u.slot) + var left = c[l.slot] + left = left.split(at: metric.index(at: l.remaining, in: left.value)) + builder._insertBeforeTip(left) + for i in l.slot + 1 ..< u.slot { + builder._insertBeforeTip(c[i]) + } + var right = c[u.slot] + _ = right.split(at: metric.index(at: u.remaining, in: right.value)) + builder._insertBeforeTip(right) + } + return + } + + self.readInner { + let l = $0.findSlot(at: start, in: metric, preferEnd: false) + let u = $0.findSlot(from: l, offsetBy: end - start, in: metric, preferEnd: true) + let c = $0.children + if l.slot == u.slot { + c[l.slot].extract( + from: l.remaining, to: u.remaining, in: metric, into: &builder) + return + } + assert(l.slot < u.slot) + let lsize = metric._nonnegativeSize(of: c[l.slot].summary) + c[l.slot].extract(from: l.remaining, to: lsize, in: metric, into: &builder) + for i in l.slot + 1 ..< u.slot { + builder._insertBeforeTip(c[i]) + } + c[u.slot].extract(from: 0, to: u.remaining, in: metric, into: &builder) + } + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Find.swift b/Sources/RopeModule/Rope/Operations/Rope+Find.swift new file mode 100644 index 000000000..a6d4ff448 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Find.swift @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public func find( + at position: Int, + in metric: some RopeMetric, + preferEnd: Bool + ) -> (index: Index, remaining: Int) { + let wholeSize = _root == nil ? 0 : metric.size(of: root.summary) + precondition(position >= 0 && position <= wholeSize, "Position out of bounds") + guard !isEmpty, preferEnd || position < wholeSize else { + return (endIndex, position) + } + var position = position + var node = root + var path = _Path(height: node.height) + while node.height > 0 { + node = node.readInner { + let r = $0.findSlot(at: position, in: metric, preferEnd: preferEnd) + position = r.remaining + path[$0.height] = r.slot + return $0.children[r.slot] + } + } + let r = node.readLeaf { $0.findSlot(at: position, in: metric, preferEnd: preferEnd) } + path[0] = r.slot + let index = Index(version: _version, path: path, leaf: node.asUnmanagedLeaf) + return (index, r.remaining) + } +} + +extension Rope._UnsafeHandle { + @inlinable + internal func findSlot( + at position: Int, + in metric: some RopeMetric, + preferEnd: Bool = true + ) -> (slot: Int, remaining: Int) { + var remaining = position + var size = 0 + for slot in 0 ..< childCount { + size = metric.size(of: children[slot].summary) + let next = remaining - size + let adjustment = (preferEnd ? 0 : 1) + if next + adjustment <= 0 { + return (slot, remaining) + } + remaining = next + } + precondition(remaining == 0, "Position out of bounds") + return preferEnd ? (childCount - 1, remaining + size) : (childCount, 0) + } + + @inlinable + internal func findSlot( + from p: (slot: Int, remaining: Int), + offsetBy distance: Int, + in metric: some RopeMetric, + preferEnd: Bool = true + ) -> (slot: Int, remaining: Int) { + assert(p.slot >= 0 && p.slot < childCount) + assert(p.remaining >= 0 && p.remaining <= metric.size(of: children[p.slot].summary)) + assert(distance >= 0) + let adjustment = (preferEnd ? 0 : 1) + var d = p.remaining + distance + var slot = p.slot + while slot < childCount { + let size = metric.size(of: children[slot].summary) + if d + adjustment <= size { break } + d -= size + slot += 1 + } + assert(slot < childCount || d == 0) + return (slot, d) + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+ForEachWhile.swift b/Sources/RopeModule/Rope/Operations/Rope+ForEachWhile.swift new file mode 100644 index 000000000..b6cf04733 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+ForEachWhile.swift @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public func forEachWhile( + _ body: (Element) -> Bool + ) -> Bool { + guard _root != nil else { return true } + return root.forEachWhile(body) + } + + @inlinable + public func forEachWhile( + from position: Int, + in metric: some RopeMetric, + _ body: (Element, Element.Index?) -> Bool + ) -> Bool { + guard _root != nil else { + precondition(position == 0, "Position out of bounds") + return true + } + return root.forEachWhile(from: position, in: metric, body) + } +} + +extension Rope._Node { + @inlinable + internal func forEachWhile( + _ body: (Element) -> Bool + ) -> Bool { + if isLeaf { + return readLeaf { + let c = $0.children + for i in 0 ..< c.count { + guard body(c[i].value) else { return false } + } + return true + } + } + return readInner { + let c = $0.children + for i in 0 ..< c.count { + guard c[i].forEachWhile(body) else { return false } + } + return true + } + } + + @inlinable + internal func forEachWhile( + from position: Int, + in metric: some RopeMetric, + _ body: (Element, Element.Index?) -> Bool + ) -> Bool { + if isLeaf { + return readLeaf { + let c = $0.children + var (slot, rem) = $0.findSlot(at: position, in: metric, preferEnd: false) + let i = metric.index(at: rem, in: c[slot].value) + if !body(c[slot].value, i) { return false } + slot += 1 + while slot < c.count { + if !body(c[slot].value, nil) { return false } + slot += 1 + } + return true + } + } + return readInner { + let c = $0.children + var (slot, rem) = $0.findSlot(at: position, in: metric, preferEnd: false) + if !c[slot].forEachWhile(from: rem, in: metric, body) { return false } + slot += 1 + while slot < c.count { + if !c[slot].forEachWhile({ body($0, nil) }) { return false } + slot += 1 + } + return true + } + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Insert.swift b/Sources/RopeModule/Rope/Operations/Rope+Insert.swift new file mode 100644 index 000000000..e1c06daa3 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Insert.swift @@ -0,0 +1,221 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public mutating func prepend(_ item: __owned Element) { + _invalidateIndices() + insert(item, at: startIndex) + } + + @inlinable + public mutating func insert( + _ item: __owned Element, + at index: Index + ) { + validate(index) + insert(item, at: index._path) + } + + @inlinable + mutating func insert( + _ item: __owned Element, + at path: _Path + ) { + if path == _endPath { + append(item) + return + } + if let spawn = root.insert(_Item(item), at: path) { + _root = .createInner(children: root, spawn) + } + _invalidateIndices() + } + + @inlinable + public mutating func insert( + _ item: __owned Element, + at position: Int, + in metric: some RopeMetric + ) { + if position == metric.size(of: summary) { + append(item) + return + } + if let spawn = root.insert(_Item(item), at: position, in: metric) { + _root = .createInner(children: root, spawn) + } + _invalidateIndices() + } +} + +extension Rope._Node { + @inlinable + internal mutating func prepend(_ item: __owned _Item) -> Self? { + insert(item, at: _startPath) + } + + @inlinable + internal mutating func insert( + _ item: __owned _Item, + at path: _Path + ) -> Self? { + ensureUnique() + let h = height + let slot = path[h] + if h > 0 { + precondition(slot < childCount, "Index out of bounds") + return _innerInsert(at: slot) { $0.insert(item, at: path) } + } + precondition(slot <= childCount, "Index out of bounds") + return _leafInsert(item, at: slot) + } + + @inlinable + internal mutating func insert( + _ item: __owned _Item, + at position: Int, + in metric: some RopeMetric + ) -> Self? { + ensureUnique() + if height > 0 { + let (slot, remaining) = readInner { + $0.findSlot(at: position, in: metric, preferEnd: false) + } + return _innerInsert(at: slot) { $0.insert(item, at: remaining, in: metric) } + } + let (slot, remaining) = readLeaf { + $0.findSlot(at: position, in: metric, preferEnd: false) + } + precondition(remaining == 0, "Inserted element doesn't fall on an element boundary") + return _leafInsert(item, at: slot) + } +} + +extension Rope._Node { + @inlinable + internal mutating func _innerInsert( + at slot: Int, + with body: (inout Self) -> Self? + ) -> Self? { + assert(slot < childCount) + var summary = self.summary + let spawn = updateInner { + let p = $0.mutableChildPtr(at: slot) + summary.subtract(p.pointee.summary) + let spawn = body(&p.pointee) + summary.add(p.pointee.summary) + return spawn + } + self.summary = summary + guard let spawn = spawn else { return nil } + return _applySpawn(spawn, of: slot) + } + + @inlinable + internal mutating func _applySpawn( + _ spawn: __owned Self, of slot: Int + ) -> Self? { + var spawn = spawn + var nextSlot = slot + 1 +#if true // Compress existing nodes if possible. + if slot > 0 { + // Try merging remainder into previous child. + updateInner { + let c = $0.mutableChildren + let s = c[slot - 1].childCount + c[slot].childCount + guard s <= Summary.maxNodeSize else { return } + Self.redistributeChildren(&c[slot - 1], &c[slot], to: s) + let removed = $0._removeChild(at: slot) + assert(removed.childCount == 0) + nextSlot -= 1 + } + } + if nextSlot < childCount { + // Try merging new spawn into subsequent child. + let merged: Summary? = updateInner { + let c = $0.mutableChildren + let s = spawn.childCount + c[nextSlot].childCount + guard s <= Summary.maxNodeSize else { return nil } + let summary = spawn.summary + Self.redistributeChildren(&spawn, &c[nextSlot], to: 0) + assert(spawn.childCount == 0) + return summary + } + if let merged = merged { + self.summary.add(merged) + return nil + } + } +#endif + guard isFull else { + _insertNode(spawn, at: nextSlot) + return nil + } + if nextSlot < Summary.minNodeSize { + let spawn2 = split(keeping: childCount / 2) + _insertNode(spawn, at: nextSlot) + return spawn2 + } + var spawn2 = split(keeping: childCount / 2) + spawn2._insertNode(spawn, at: nextSlot - childCount) + return spawn2 + } +} + +extension Rope._Node { + @inlinable + internal mutating func _leafInsert( + _ item: __owned _Item, at slot: Int + ) -> Self? { + assert(slot <= childCount) + var item = item + if item.isUndersized, childCount > 0, _rebalanceBeforeInsert(&item, at: slot) { + return nil + } + + guard isFull else { + _insertItem(item, at: slot) + return nil + } + if slot < Summary.minNodeSize { + let spawn = split(keeping: childCount - Summary.minNodeSize) + _insertItem(item, at: slot) + return spawn + } + var spawn = split(keeping: Summary.minNodeSize) + spawn._insertItem(item, at: slot - childCount) + return spawn + } + + @inlinable + internal mutating func _rebalanceBeforeInsert( + _ item: inout _Item, at slot: Int + ) -> Bool { + assert(item.isUndersized) + let r = updateLeaf { (h) -> (merged: Bool, delta: Summary) in + if slot > 0 { + let p = h.mutableChildPtr(at: slot - 1) + let sum = p.pointee.summary + let merged = p.pointee.rebalance(nextNeighbor: &item) + let delta = p.pointee.summary.subtracting(sum) + return (merged, delta) + } + let p = h.mutableChildPtr(at: slot) + let sum = p.pointee.summary + let merged = p.pointee.rebalance(prevNeighbor: &item) + let delta = p.pointee.summary.subtracting(sum) + return (merged, delta) + } + self.summary.add(r.delta) + return r.merged + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Join.swift b/Sources/RopeModule/Rope/Operations/Rope+Join.swift new file mode 100644 index 000000000..4fcea99d1 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Join.swift @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public mutating func append(_ other: __owned Self) { + self = Rope.join(self, other) + } + + @inlinable + public mutating func prepend(_ other: __owned Self) { + self = Rope.join(other, self) + } + + @inlinable + internal mutating func _append(_ other: __owned _Node) { + append(Self(root: other)) + } + + @inlinable + internal mutating func _prepend(_ other: __owned _Node) { + prepend(Self(root: other)) + } + + /// Concatenate `left` and `right` by linking up the two trees. + @inlinable + public static func join(_ left: __owned Self, _ right: __owned Self) -> Self { + guard !right.isEmpty else { return left } + guard !left.isEmpty else { return right } + + var left = left.root + var right = right.root + + left.ensureUnique() + right.ensureUnique() + + if left.height >= right.height { + let r = left._graftBack(&right) + guard let remainder = r.remainder else { return Self(root: left) } + assert(left.height == remainder.height) + let root = _Node.createInner(children: left, remainder) + return Self(root: root) + + } + let r = right._graftFront(&left) + guard let remainder = r.remainder else { return Self(root: right) } + assert(right.height == remainder.height) + let root = _Node.createInner(children: remainder, right) + return Self(root: root) + } +} + +extension Rope._Node { + @inlinable + internal mutating func _graftFront( + _ scion: inout Self + ) -> (remainder: Self?, delta: Summary) { + assert(self.height >= scion.height) + guard self.height > scion.height else { + assert(self.height == scion.height) + let d = scion.summary + if self.rebalance(prevNeighbor: &scion) { + return (nil, d) + } + assert(!scion.isEmpty) + return (scion, d.subtracting(scion.summary)) + } + + var (remainder, delta) = self.updateInner { h in + h.mutableChildren[0]._graftFront(&scion) + } + self.summary.add(delta) + guard let remainder = remainder else { return (nil, delta) } + assert(self.height == remainder.height + 1) + assert(!remainder.isUndersized) + guard self.isFull else { + delta.add(remainder.summary) + self._insertNode(remainder, at: 0) + return (nil, delta) + } + var splinter = self.split(keeping: self.childCount / 2) + + swap(&self, &splinter) + delta.subtract(splinter.summary) + splinter._insertNode(remainder, at: 0) + return (splinter, delta) + } + + @inlinable + internal mutating func _graftBack( + _ scion: inout Self + ) -> (remainder: Self?, delta: Summary) { + assert(self.height >= scion.height) + guard self.height > scion.height else { + assert(self.height == scion.height) + let origSum = self.summary + let emptied = self.rebalance(nextNeighbor: &scion) + return (emptied ? nil : scion, self.summary.subtracting(origSum)) + } + + var (remainder, delta) = self.updateInner { h in + h.mutableChildren[h.childCount - 1]._graftBack(&scion) + } + self.summary.add(delta) + guard let remainder = remainder else { return (nil, delta) } + assert(self.height == remainder.height + 1) + assert(!remainder.isUndersized) + guard self.isFull else { + delta.add(remainder.summary) + self._appendNode(remainder) + return (nil, delta) + } + var splinter = self.split(keeping: self.childCount / 2) + delta.subtract(splinter.summary) + splinter._appendNode(remainder) + return (splinter, delta) + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+MutatingForEach.swift b/Sources/RopeModule/Rope/Operations/Rope+MutatingForEach.swift new file mode 100644 index 000000000..06a3723f8 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+MutatingForEach.swift @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable @inline(__always) + @discardableResult + public mutating func mutatingForEach( + _ body: (inout Element) -> R? + ) -> R? { + var i = startIndex + return mutatingForEach(from: &i, body) + } + + @inlinable @inline(__always) + @discardableResult + public mutating func mutatingForEach( + from index: inout Index, + _ body: (inout Element) -> R? + ) -> R? { + var r: R? = nil + let completed = _mutatingForEach(from: &index) { + r = body(&$0) + return r == nil + } + assert(completed == (r == nil)) + return r + } + + @inlinable + internal mutating func _mutatingForEach( + from index: inout Index, + _ body: (inout Element) -> Bool + ) -> Bool { + validate(index) + guard _root != nil else { return true } + defer { + _invalidateIndices() + index._version = _version + } + let r = root.mutatingForEach(from: &index, body: body).continue + return r + } +} + +extension Rope._Node { + @inlinable + internal mutating func mutatingForEach( + from index: inout Index, + body: (inout Element) -> Bool + ) -> (continue: Bool, delta: Summary) { + ensureUnique() + let h = height + var slot = index._path[h] + precondition(slot <= childCount, "Index out of bounds") + guard slot < childCount else { return (true, .zero) } + var delta = Summary.zero + defer { self.summary.add(delta) } + if h > 0 { + let r = updateInner { + let c = $0.mutableChildren + while slot < c.count { + let (r, d) = c[slot].mutatingForEach(from: &index, body: body) + delta.add(d) + guard r else { return false } + slot += 1 + index._path.clear(below: h) + index._path[h] = slot + } + index._leaf = nil + return true + } + return (r, delta) + } + index._leaf = asUnmanagedLeaf + let r = updateLeaf { + let c = $0.mutableChildren + while slot < c.count { + let sum = c[slot].summary + let r = body(&c[slot].value) + delta.add(c[slot].summary.subtracting(sum)) + guard r else { return false } + slot += 1 + index._path[h] = slot + } + return true + } + return (r, delta) + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Remove.swift b/Sources/RopeModule/Rope/Operations/Rope+Remove.swift new file mode 100644 index 000000000..220bca4c0 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Remove.swift @@ -0,0 +1,210 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + @discardableResult + public mutating func remove(at index: Index) -> Element { + _remove(at: index).removed + } + + /// Remove the element at the specified index, and update `index` to address the subsequent + /// element in the new collection. (Or the `endIndex` if it originally addressed the last item.) + @inlinable + @discardableResult + public mutating func remove(at index: inout Index) -> Element { + let (old, path) = _remove(at: index) + index = Index(version: _version, path: path, leaf: _unmanagedLeaf(at: path)) + return old + } + + @inlinable + @discardableResult + internal mutating func _remove(at index: Index) -> (removed: Element, path: _Path) { + validate(index) + var path = index._path + let r = root.remove(at: &path) + if root.isEmpty { + _root = nil + assert(r.pathIsAtEnd) + } else if root.childCount == 1, root.height > 0 { + root = root.readInner { $0.children.first! } + path.popRoot() + } + _invalidateIndices() + return (r.removed.value, r.pathIsAtEnd ? _endPath : path) + } +} + +extension Rope._Node { + @inlinable + internal mutating func remove( + at path: inout _Path + ) -> (removed: _Item, delta: Summary, needsFixing: Bool, pathIsAtEnd: Bool) { + ensureUnique() + let h = height + let slot = path[h] + precondition(slot < childCount, "Invalid index") + guard h > 0 else { + let r = _removeItem(at: slot) + return (r.removed, r.delta, self.isUndersized, slot == childCount) + } + let r = updateInner { $0.mutableChildren[slot].remove(at: &path) } + self.summary.subtract(r.delta) + var isAtEnd = r.pathIsAtEnd + if r.needsFixing { + let prepended = fixDeficiency(on: &path) + isAtEnd = isAtEnd && prepended + } + if isAtEnd, path[h] < childCount - 1 { + path[h] += 1 + path.clear(below: h) + isAtEnd = false + } + return (r.removed, r.delta, self.isUndersized, isAtEnd) + } +} + +extension Rope { + @inlinable + @discardableResult + public mutating func remove( + at position: Int, + in metric: some RopeMetric + ) -> (removed: Element, next: Index) { + _invalidateIndices() + var path = _Path(height: self._height) + let r = root.remove(at: position, in: metric, initializing: &path) + if root.isEmpty { + _root = nil + } else if root.childCount == 1, root.height > 0 { + root = root.readInner { $0.children.first! } + } + if r.pathIsAtEnd { + return (r.removed.value, endIndex) + } + let i = Index(version: _version, path: path, leaf: nil) + return (r.removed.value, i) + } +} + +extension Rope._Node { + /// Note: `self` may be left undersized after calling this function, which + /// is expected to be resolved by the caller. This is indicated by the `needsFixing` component + /// in the return value. + /// + /// - Returns: A tuple `(removed, delta, needsFixing, pathIsAtEnd)`, where + /// `removed` is the element that got removed, + /// `delta` is its summary, + /// `needsFixing` indicates whether the node was left undersized, and + /// `pathIsAtEnd` indicates if `path` now addresses the end of the node's subtree. + @inlinable + internal mutating func remove( + at position: Int, + in metric: some RopeMetric, + initializing path: inout _Path + ) -> (removed: _Item, delta: Summary, needsFixing: Bool, pathIsAtEnd: Bool) { + ensureUnique() + let h = height + guard h > 0 else { + let (slot, remaining) = readLeaf { + $0.findSlot(at: position, in: metric, preferEnd: false) + } + precondition(remaining == 0, "Element to be removed doesn't fall on an element boundary") + path[h] = slot + let r = _removeItem(at: slot) + return (r.removed, r.delta, self.isUndersized, slot == childCount) + } + let r = updateInner { + let (slot, remaining) = $0.findSlot(at: position, in: metric, preferEnd: false) + path[h] = slot + return $0.mutableChildren[slot].remove(at: remaining, in: metric, initializing: &path) + } + self.summary.subtract(r.delta) + var isAtEnd = r.pathIsAtEnd + if r.needsFixing { + let prepended = fixDeficiency(on: &path) + isAtEnd = isAtEnd && prepended + } + if isAtEnd, path[h] < childCount - 1 { + path[h] += 1 + path.clear(below: h) + isAtEnd = false + } + return (r.removed, r.delta, self.isUndersized, isAtEnd) + } +} + +extension Rope._Node { + /// Returns: `true` if new items got prepended to the child addressed by `path`. + /// `false` if new items got appended. + @inlinable + @discardableResult + internal mutating func fixDeficiency(on path: inout _Path) -> Bool { + assert(isUnique()) + return updateInner { + let c = $0.mutableChildren + let h = $0.height + let slot = path[h] + assert(c[slot].isUndersized) + guard c.count > 1 else { return true } + let prev = slot - 1 + let prevSum: Int + if prev >= 0 { + let prevCount = c[prev].childCount + prevSum = prevCount + c[slot].childCount + if prevSum <= Summary.maxNodeSize { + Self.redistributeChildren(&c[prev], &c[slot], to: prevSum) + assert(c[slot].isEmpty) + _ = $0._removeChild(at: slot) + path[h] = prev + path[h - 1] += prevCount + return true + } + } else { + prevSum = 0 + } + + let next = slot + 1 + let nextSum: Int + if next < c.count { + let nextCount = c[next].childCount + nextSum = c[slot].childCount + nextCount + if nextSum <= Summary.maxNodeSize { + Self.redistributeChildren(&c[slot], &c[next], to: nextSum) + assert(c[next].isEmpty) + _ = $0._removeChild(at: next) + // `path` doesn't need updating. + return false + } + } else { + nextSum = 0 + } + + if prev >= 0 { + assert(c[prev].childCount > Summary.minNodeSize) + let origCount = c[slot].childCount + Self.redistributeChildren(&c[prev], &c[slot], to: prevSum / 2) + path[h - 1] += c[slot].childCount - origCount + assert(!c[prev].isUndersized) + assert(!c[slot].isUndersized) + return true + } + assert(next < c.count) + assert(c[next].childCount > Summary.minNodeSize) + Self.redistributeChildren(&c[slot], &c[next], to: nextSum / 2) + // `path` doesn't need updating. + assert(!c[slot].isUndersized) + assert(!c[next].isUndersized) + return false + } + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+RemoveSubrange.swift b/Sources/RopeModule/Rope/Operations/Rope+RemoveSubrange.swift new file mode 100644 index 000000000..13b52b348 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+RemoveSubrange.swift @@ -0,0 +1,321 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public mutating func removeSubrange( + _ bounds: Range, + in metric: some RopeMetric + ) { + _invalidateIndices() + precondition( + bounds.lowerBound >= 0 && bounds.upperBound <= count(in: metric), + "Position out of bounds") + guard !bounds.isEmpty else { return } + // FIXME: Add fast path for tiny removals + var builder = builder(removing: bounds, in: metric) + self = builder.finalize() + } + + @inlinable + public mutating func replaceSubrange( + _ bounds: Range, + in metric: some RopeMetric, + with newElements: __owned some Sequence + ) { + // FIXME: Implement insert(contentsOf:at:in:) and dispatch to it when bounds.isEmpty. + // FIXME: Add fast path for replacing tiny ranges with tiny data. + // FIXME: Add special cases if newElements is itself a _Rope etc. + _invalidateIndices() + var builder = builder(removing: bounds, in: metric) + builder.insertBeforeTip(newElements) + self = builder.finalize() + } + + @inlinable + public mutating func builder( + removing bounds: Range, + in metric: some RopeMetric + ) -> Builder { + _invalidateIndices() + let size = metric.size(of: summary) + precondition( + bounds.lowerBound >= 0 && bounds.upperBound <= size, + "Range out of bounds") + + guard !bounds.isEmpty else { + return builder(splittingAt: bounds.lowerBound, in: metric) + } + + var builder = Builder() + var node = root + _root = nil + var lower = bounds.lowerBound + var upper = bounds.upperBound + while !node.isLeaf { + let (l, u) = node.readInner { + let l = $0.findSlot(at: lower, in: metric, preferEnd: false) + let u = $0.findSlot(from: l, offsetBy: upper - lower, in: metric, preferEnd: true) + return (l, u) + } + if l.slot < u.slot { + node._removeSubrange(from: l, to: u, in: metric, into: &builder) + return builder + } + assert(l.slot == u.slot) + node._innerSplit(at: l.slot, into: &builder) + lower = l.remaining + upper = u.remaining + } + + let (l, u) = node.readLeaf { + let l = $0.findSlot(at: lower, in: metric, preferEnd: false) + let u = $0.findSlot(from: l, offsetBy: bounds.count, in: metric, preferEnd: true) + return (l, u) + } + if l.slot < u.slot { + node._removeSubrange(from: l, to: u, in: metric, into: &builder) + return builder + } + assert(l.slot == u.slot) + var item = node._leafSplit(at: l.slot, into: &builder) + let i2 = metric.index(at: u.remaining, in: item.value) + builder._insertAfterTip(item.split(at: i2)) + let i1 = metric.index(at: l.remaining, in: item.value) + _ = item.split(at: i1) + builder._insertBeforeTip(item) + return builder + } +} + +extension Rope._Node { + @inlinable + internal __consuming func _removeSubrange( + from start: (slot: Int, remaining: Int), + to end: (slot: Int, remaining: Int), + in metric: some RopeMetric, + into builder: inout Rope.Builder + ) { + assert(start.slot >= 0 && start.slot < end.slot && end.slot < childCount) + assert(start.remaining >= 0) + assert(end.remaining >= 0) + + builder._insertBeforeTip(slots: 0 ..< start.slot, in: self) + if end.slot < childCount { + builder._insertAfterTip(slots: end.slot + 1 ..< childCount, in: self) + } + + guard isLeaf else { + // Extract children on boundaries. + let (lower, upper) = readInner { ($0.children[start.slot], $0.children[end.slot]) } + + // Descend a lever lower. + lower.removeSuffix(from: start.remaining, in: metric, into: &builder) + upper.removePrefix(upTo: end.remaining, in: metric, into: &builder) + return + } + // Extract items on boundaries. + var (lower, upper) = readLeaf { ($0.children[start.slot], $0.children[end.slot]) } + + let i1 = metric.index(at: start.remaining, in: lower.value) + let i2 = metric.index(at: end.remaining, in: upper.value) + _ = lower.split(at: i1) + builder._insertBeforeTip(lower) + builder._insertAfterTip(upper.split(at: i2)) + } + + @inlinable + internal __consuming func removeSuffix( + from position: Int, + in metric: some RopeMetric, + into builder: inout Rope.Builder + ) { + + var node = self + var position = position + while true { + guard position > 0 else { return } + guard position < metric.size(of: node.summary) else { + builder._insertBeforeTip(node) + return + } + + guard !node.isLeaf else { break } + + let r = node.readInner { $0.findSlot(at: position, in: metric) } + position = r.remaining + node._innerRemoveSuffix(descending: r.slot, into: &builder) + } + let r = node.readLeaf { $0.findSlot(at: position, in: metric, preferEnd: false) } + var item = node._leafRemoveSuffix(returning: r.slot, into: &builder) + let i = metric.index(at: r.remaining, in: item.value) + _ = item.split(at: i) + builder._insertBeforeTip(item) + } + + @inlinable + internal __consuming func removePrefix( + upTo position: Int, + in metric: some RopeMetric, + into builder: inout Rope.Builder + ) { + var node = self + var position = position + while true { + guard position > 0 else { + builder._insertAfterTip(node) + return + } + guard position < metric.size(of: node.summary) else { return } + + guard !node.isLeaf else { break } + + let r = node.readInner { $0.findSlot(at: position, in: metric) } + position = r.remaining + node._innerRemovePrefix(descending: r.slot, into: &builder) + } + let r = node.readLeaf { $0.findSlot(at: position, in: metric) } + var item = node._leafRemovePrefix(returning: r.slot, into: &builder) + let i = metric.index(at: r.remaining, in: item.value) + builder._insertAfterTip(item.split(at: i)) + } + + @inlinable + internal mutating func _innerRemoveSuffix( + descending slot: Int, + into builder: inout Rope.Builder + ) { + assert(!self.isLeaf) + assert(slot >= 0 && slot <= childCount) + + if slot == 0 { + self = readInner { $0.children[0] } + return + } + if slot == 1 { + let (remaining, new) = readInner { + let c = $0.children + return (c[0], c[1]) + } + builder._insertBeforeTip(remaining) + self = new + return + } + + ensureUnique() + if slot < childCount - 1 { + let delta = updateInner { $0._removeSuffix($0.childCount - slot - 1) } + self.summary.subtract(delta) + } + var n = _removeNode(at: slot) + swap(&self, &n) + assert(n.childCount > 1) + builder._insertBeforeTip(n) + } + + @inlinable + internal __consuming func _leafRemoveSuffix( + returning slot: Int, + into builder: inout Rope.Builder + ) -> _Item { + assert(self.isLeaf) + assert(slot >= 0 && slot < childCount) + + if slot == 0 { + return readLeaf { $0.children[0] } + } + if slot == 1 { + let (remaining, new) = readLeaf { + let c = $0.children + return (c[0], c[1]) + } + builder._insertBeforeTip(remaining) + return new + } + + var n = self + n.ensureUnique() + if slot < n.childCount - 1 { + let delta = n.updateLeaf { $0._removeSuffix($0.childCount - slot - 1) } + n.summary.subtract(delta) + } + let item = n._removeItem(at: slot).removed + builder._insertBeforeTip(n) + return item + } + + @inlinable + internal mutating func _innerRemovePrefix( + descending slot: Int, + into builder: inout Rope.Builder + ) { + assert(!self.isLeaf) + assert(slot >= 0 && slot < childCount) + + if slot == childCount - 1 { + self = readInner { $0.children[$0.childCount - 1] } + return + } + if slot == childCount - 2 { + let (new, remaining) = readInner { + let c = $0.children + return (c[$0.childCount - 2], c[$0.childCount - 1]) + } + builder._insertAfterTip(remaining) + self = new + return + } + + ensureUnique() + var (delta, n) = updateInner { + let n = $0.children[slot] + let delta = $0._removePrefix(slot + 1) + return (delta, n) + } + self.summary.subtract(delta) + assert(self.childCount > 1) + swap(&self, &n) + builder._insertAfterTip(n) + } + + @inlinable + internal __consuming func _leafRemovePrefix( + returning slot: Int, + into builder: inout Rope.Builder + ) -> _Item { + assert(self.isLeaf) + assert(slot >= 0 && slot <= childCount) + + if slot == childCount - 1 { + return readLeaf { $0.children[$0.childCount - 1] } + } + if slot == childCount - 2 { + let (new, remaining) = readLeaf { + let c = $0.children + return (c[$0.childCount - 2], c[$0.childCount - 1]) + } + builder._insertAfterTip(remaining) + return new + } + + var n = self + n.ensureUnique() + let (delta, item) = n.updateLeaf { + let n = $0.children[slot] + let delta = $0._removePrefix(slot + 1) + return (delta, n) + } + n.summary.subtract(delta) + assert(n.childCount > 1) + builder._insertAfterTip(n) + return item + } +} diff --git a/Sources/RopeModule/Rope/Operations/Rope+Split.swift b/Sources/RopeModule/Rope/Operations/Rope+Split.swift new file mode 100644 index 000000000..dab683545 --- /dev/null +++ b/Sources/RopeModule/Rope/Operations/Rope+Split.swift @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Rope { + @inlinable + public mutating func builder( + splittingAt position: Int, + in metric: some RopeMetric + ) -> Builder { + _invalidateIndices() + var builder = Builder() + + var position = position + var node = root + _root = nil + while !node.isLeaf { + let r = node.readInner { $0.findSlot(at: position, in: metric) } + position = r.remaining + node._innerSplit(at: r.slot, into: &builder) + } + + let r = node.readLeaf { $0.findSlot(at: position, in: metric) } + var item = node._leafSplit(at: r.slot, into: &builder) + let index = metric.index(at: r.remaining, in: item.value) + let suffix = item.split(at: index) + builder._insertAfterTip(suffix) + builder._insertBeforeTip(item) + return builder + } + + @inlinable + public mutating func split(at index: Index) -> (builder: Builder, item: Element) { + validate(index) + precondition(index < endIndex) + var builder = Builder() + + var node = root + _root = nil + while !node.isLeaf { + let slot = index._path[node.height] + precondition(slot < node.childCount, "Index out of bounds") + node._innerSplit(at: slot, into: &builder) + } + + let slot = index._path[node.height] + precondition(slot < node.childCount, "Index out of bounds") + let item = node._leafSplit(at: slot, into: &builder) + _invalidateIndices() + return (builder, item.value) + } + + @inlinable + public mutating func split(at ropeIndex: Index, _ itemIndex: Element.Index) -> Builder { + var (builder, item) = self.split(at: ropeIndex) + let suffix = item.split(at: itemIndex) + if !suffix.isEmpty { + builder.insertAfterTip(suffix) + } + if !item.isEmpty { + builder.insertBeforeTip(item) + } + return builder + } +} + +extension Rope._Node { + @inlinable + internal mutating func _innerSplit( + at slot: Int, + into builder: inout Rope.Builder + ) { + assert(!self.isLeaf) + assert(slot >= 0 && slot < childCount) + ensureUnique() + + var slot = slot + if slot == childCount - 2 { + builder._insertAfterTip(_removeNode(at: childCount - 1)) + } + if slot == 1 { + builder._insertBeforeTip(_removeNode(at: 0)) + slot -= 1 + } + + var n = _removeNode(at: slot) + swap(&self, &n) + + guard n.childCount > 0 else { return } + if slot == 0 { + builder._insertAfterTip(n) + return + } + if slot == n.childCount { + builder._insertBeforeTip(n) + return + } + let suffix = n.split(keeping: slot) + builder._insertBeforeTip(n) + builder._insertAfterTip(suffix) + } + + @inlinable + internal __consuming func _leafSplit( + at slot: Int, + into builder: inout Rope.Builder + ) -> _Item { + var n = self + n.ensureUnique() + + assert(n.isLeaf) + assert(slot >= 0 && slot < n.childCount) + + var slot = slot + if slot == n.childCount - 2 { + builder._insertAfterTip(n._removeItem(at: childCount - 1).removed) + } + if slot == 1 { + builder.insertBeforeTip(n._removeItem(at: 0).removed.value) + slot -= 1 + } + + let item = n._removeItem(at: slot).removed + + guard n.childCount > 0 else { return item } + if slot == 0 { + builder._insertAfterTip(n) + } else if slot == n.childCount { + builder._insertBeforeTip(n) + } else { + let suffix = n.split(keeping: slot) + builder._insertBeforeTip(n) + builder._insertAfterTip(suffix) + } + return item + } +} diff --git a/Sources/RopeModule/Utilities/Optional Utilities.swift b/Sources/RopeModule/Utilities/Optional Utilities.swift new file mode 100644 index 000000000..c5f54f283 --- /dev/null +++ b/Sources/RopeModule/Utilities/Optional Utilities.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +extension Optional { + @inlinable + internal mutating func _take() -> Self { + let r = self + self = nil + return r + } +} diff --git a/Sources/RopeModule/Utilities/String Utilities.swift b/Sources/RopeModule/Utilities/String Utilities.swift new file mode 100644 index 000000000..57e6b924b --- /dev/null +++ b/Sources/RopeModule/Utilities/String Utilities.swift @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension StringProtocol { + @inline(__always) + var _indexOfLastCharacter: Index { + guard !isEmpty else { return endIndex } + return index(before: endIndex) + } + + @inline(__always) + func _index(at offset: Int) -> Index { + self.index(self.startIndex, offsetBy: offset) + } + + @inline(__always) + func _utf8Index(at offset: Int) -> Index { + self.utf8.index(startIndex, offsetBy: offset) + } + + @inline(__always) + func _utf8ClampedIndex(at offset: Int) -> Index { + self.utf8.index(startIndex, offsetBy: offset, limitedBy: endIndex) ?? endIndex + } + + @inline(__always) + func _utf8Offset(of index: Index) -> Int { + self.utf8.distance(from: startIndex, to: index) + } + + @inline(__always) + var _lastCharacter: (index: Index, utf8Length: Int) { + let i = _indexOfLastCharacter + let length = utf8.distance(from: i, to: endIndex) + return (i, length) + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension String { + internal func _lpad(to width: Int, with pad: Character = " ") -> String { + let c = self.count + if c >= width { return self } + return String(repeating: pad, count: width - c) + self + } + + internal func _rpad(to width: Int, with pad: Character = " ") -> String { + let c = self.count + if c >= width { return self } + return self + String(repeating: pad, count: width - c) + } +} + +#if swift(>=5.8) // _CharacterRecognizer +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension String { + @discardableResult + mutating func _appendQuotedProtectingLeft( + _ str: String, + with state: inout _CharacterRecognizer, + maxLength: Int = Int.max + ) -> String.Index { + guard !str.isEmpty else { return str.endIndex } + let startUTF8 = self.utf8.count + var i = str.unicodeScalars.startIndex + var needsBreak = true + while i < str.endIndex { + let us = str.unicodeScalars[i] + var scalar = us.escaped(asASCII: false) + if needsBreak { + var t = state + if let r = t.firstBreak(in: scalar[...]), r.lowerBound == scalar.startIndex { + } else { + scalar = us.escaped(asASCII: true) + } + } + needsBreak = (scalar != String(us)) + self.append(scalar) + _ = state.consume(scalar[...]) + + str.unicodeScalars.formIndex(after: &i) + + let start = self._utf8Index(at: startUTF8) + if self.distance(from: start, to: self.endIndex) >= maxLength { + break + } + } + return i + } + + mutating func _appendProtectingRight(_ str: String, with state: inout _CharacterRecognizer) { + var suffix = str + while !self.isEmpty { + guard let first = suffix.unicodeScalars.first else { return } + self.unicodeScalars.append(first) + let i = self.index(before: self.endIndex) + if self.unicodeScalars.distance(from: i, to: self.endIndex) == 1 { + self.unicodeScalars.append(contentsOf: suffix.unicodeScalars.dropFirst()) + break + } + self.unicodeScalars.removeLast() + let last = self.unicodeScalars.removeLast() + suffix.insert(contentsOf: last.escaped(asASCII: true), at: suffix.startIndex) + } + } + + /// A representation of the string that is suitable for debugging. + /// This implementation differs from `String.debugDescription` by properly quoting + /// continuation characters after the opening quotation mark and similar meta-characters. + var _properDebugDescription: String { + var result = "\"" + var state = _CharacterRecognizer(consuming: result) + result._appendQuotedProtectingLeft(self, with: &state) + result._appendProtectingRight("\"", with: &state) + return result + } +} +#endif diff --git a/Sources/RopeModule/Utilities/String.Index+ABI.swift b/Sources/RopeModule/Utilities/String.Index+ABI.swift new file mode 100644 index 000000000..5c241da88 --- /dev/null +++ b/Sources/RopeModule/Utilities/String.Index+ABI.swift @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +// Bits of String.Index that are ABI but aren't exposed by public API. +extension String.Index { + @inline(__always) + var _abi_rawBits: UInt64 { + unsafeBitCast(self, to: UInt64.self) + } + + @inline(__always) + var _abi_encodedOffset: Int { + Int(truncatingIfNeeded: _abi_rawBits &>> 16) + } + + @inline(__always) + var _abi_transcodedOffset: Int { + Int(truncatingIfNeeded: (_abi_rawBits &>> 14) & 0x3) + } + + @inline(__always) + static var _abi_scalarAlignmentBit: UInt64 { 0x1 } + + @inline(__always) + static var _abi_characterAlignmentBit: UInt64 { 0x2 } + + @inline(__always) + static var _abi_utf8Bit: UInt64 { 0x4 } + + @inline(__always) + static var _abi_utf16Bit: UInt64 { 0x8 } + + + @inline(__always) + var _encodingBits: UInt64 { + _abi_rawBits & (Self._abi_utf8Bit | Self._abi_utf16Bit) + } + + @inline(__always) + var _canBeUTF8: Bool { + // The only way an index cannot be UTF-8 is it has only the UTF-16 flag set. + _encodingBits != Self._abi_utf16Bit + } + + var _isKnownScalarAligned: Bool { + 0 != _abi_rawBits & Self._abi_scalarAlignmentBit + } + + var _isKnownCharacterAligned: Bool { + 0 != _abi_rawBits & Self._abi_characterAlignmentBit + } + + var _knownCharacterAligned: String.Index { + let r = _abi_rawBits | Self._abi_characterAlignmentBit | Self._abi_scalarAlignmentBit + return unsafeBitCast(r, to: String.Index.self) + } + + var _knownScalarAligned: String.Index { + let r = _abi_rawBits | Self._abi_scalarAlignmentBit + return unsafeBitCast(r, to: String.Index.self) + } +} + +extension String.Index { + @inline(__always) + var _utf8Offset: Int { + assert(_canBeUTF8) + return _abi_encodedOffset + } + + @inline(__always) + var _isUTF16TrailingSurrogate: Bool { + assert(_canBeUTF8) + let r = _abi_transcodedOffset + assert(r <= 1) + return r > 0 + } + + @inline(__always) + init(_rawBits: UInt64) { + self = unsafeBitCast(_rawBits, to: String.Index.self) + } + + @inline(__always) + init(_utf8Offset: Int) { + self.init(_rawBits: (UInt64(_utf8Offset) &<< 16) | Self._abi_utf8Bit) + } + + init(_utf8Offset: Int, utf16TrailingSurrogate: Bool) { + let transcodedOffset: UInt64 = (utf16TrailingSurrogate ? 1 &<< 14 : 0) + self.init(_rawBits: (UInt64(_utf8Offset) &<< 16) | transcodedOffset | Self._abi_utf8Bit) + } +} + +extension String.Index { + @inline(__always) + var _chunkData: UInt16 { + UInt16(_abi_rawBits &>> 14) + } + + @inline(__always) + init(_chunkData: UInt16) { + self.init(_rawBits: UInt64(_chunkData) &<< 14) + } +} diff --git a/Sources/RopeModule/Utilities/_CharacterRecognizer.swift b/Sources/RopeModule/Utilities/_CharacterRecognizer.swift new file mode 100644 index 000000000..447f686c8 --- /dev/null +++ b/Sources/RopeModule/Utilities/_CharacterRecognizer.swift @@ -0,0 +1,172 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +internal typealias _CharacterRecognizer = Unicode._CharacterRecognizer + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension _CharacterRecognizer { + internal func _isKnownEqual(to other: Self) -> Bool { + // FIXME: Enable when Swift 5.9 ships. +// #if swift(>=5.9) +// if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { // SwiftStdlib 5.9 +// return self == other +// } +// #endif + return false + } +} + + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension _CharacterRecognizer { + mutating func firstBreak( + in str: Substring + ) -> Range? { + let r = str.utf8.withContiguousStorageIfAvailable { buffer in + self._firstBreak(inUncheckedUnsafeUTF8Buffer: buffer) + } + if let r { + guard let scalarRange = r else { return nil } + let lower = str._utf8Index(at: scalarRange.lowerBound) + let upper = str._utf8Index(at: scalarRange.upperBound) + return lower ..< upper + } + guard !str.isEmpty else { return nil } + + var i = str.startIndex + while i < str.endIndex { + let next = str.unicodeScalars.index(after: i) + let scalar = str.unicodeScalars[i] + if self.hasBreak(before: scalar) { + return i ..< next + } + i = next + } + return nil + } +} + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +extension _CharacterRecognizer { + init(partialCharacter: Substring.UnicodeScalarView) { + self.init() + var it = partialCharacter.makeIterator() + guard let first = it.next() else { return } + _ = hasBreak(before: first) + while let next = it.next() { + let b = hasBreak(before: next) + assert(!b) + } + } + + init(partialCharacter: Substring) { + self.init(partialCharacter: partialCharacter.unicodeScalars) + } + + mutating func consumePartialCharacter(_ s: String) { + for scalar in s.unicodeScalars { + let b = hasBreak(before: scalar) + assert(!b) + } + } + + mutating func consumePartialCharacter(_ s: Substring) { + for scalar in s.unicodeScalars { + let b = hasBreak(before: scalar) + assert(!b) + } + } + + mutating func consumePartialCharacter(_ s: Substring.UnicodeScalarView) { + for scalar in s { + let b = hasBreak(before: scalar) + assert(!b) + } + } + + mutating func consumeUntilFirstBreak( + in s: Substring.UnicodeScalarView, + from i: inout String.Index + ) -> String.Index? { + while i < s.endIndex { + defer { s.formIndex(after: &i) } + if hasBreak(before: s[i]) { + return i + } + } + return nil + } + + init(consuming str: some StringProtocol) { + self.init() + _ = self.consume(str) + } + + mutating func consume( + _ s: some StringProtocol + ) -> (characters: Int, firstBreak: String.Index, lastBreak: String.Index)? { + consume(Substring(s)) + } + + mutating func consume( + _ s: Substring + ) -> (characters: Int, firstBreak: String.Index, lastBreak: String.Index)? { + consume(s.unicodeScalars) + } + + mutating func consume( + _ s: Substring.UnicodeScalarView + ) -> (characters: Int, firstBreak: String.Index, lastBreak: String.Index)? { + var i = s.startIndex + guard let first = consumeUntilFirstBreak(in: s, from: &i) else { + return nil + } + var characters = 1 + var last = first + while let next = consumeUntilFirstBreak(in: s, from: &i) { + characters += 1 + last = next + } + return (characters, first, last) + } + + mutating func consume( + _ chunk: BigString._Chunk, upTo index: String.Index + ) -> (firstBreak: String.Index, prevBreak: String.Index)? { + let index = chunk.string.unicodeScalars._index(roundingDown: index) + let first = chunk.firstBreak + guard index > first else { + consumePartialCharacter(chunk.string[.. (characters: Int, prefixCount: Int, suffixCount: Int) { + let c = s.utf8.count + guard let (chars, first, last) = consume(s[...]) else { + return (0, c, c) + } + let prefix = s._utf8Offset(of: first) + let suffix = c - s._utf8Offset(of: last) + return (chars, prefix, suffix) + } +} + +#endif diff --git a/Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift b/Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift.gyb similarity index 87% rename from Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift rename to Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift.gyb index 2cb6cddb6..332305e15 100644 --- a/Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift +++ b/Sources/_CollectionsUtilities/Compatibility/Array+WithContiguousStorage Compatibility.swift.gyb @@ -2,19 +2,24 @@ // // 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 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + extension Array { /// Returns true if `Array.withContiguousStorageIfAvailable` is broken /// in the stdlib we're currently running on. /// /// See https://bugs.swift.org/browse/SR-14663. - @inlinable + @inlinable @inline(__always) internal static func _isWCSIABroken() -> Bool { #if _runtime(_ObjC) guard _isBridgedVerbatimToObjectiveC(Element.self) else { @@ -53,11 +58,13 @@ extension Array { } } +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension Sequence { // An adjusted version of the standard `withContiguousStorageIfAvailable` // method that works around https://bugs.swift.org/browse/SR-14663. @inlinable @inline(__always) - public func _withContiguousStorageIfAvailable_SR14663( + ${modifier} func _withContiguousStorageIfAvailable_SR14663( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { if Self.self == Array.self && Array._isWCSIABroken() { @@ -67,3 +74,5 @@ extension Sequence { return try self.withContiguousStorageIfAvailable(body) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift b/Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb similarity index 92% rename from Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift rename to Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb index db28325bf..867f276ee 100644 --- a/Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift +++ b/Sources/_CollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb @@ -2,7 +2,7 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 @@ -11,6 +11,14 @@ // Note: These are adapted from SE-0370 in the Swift 5.8 Standard Library. +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} +#if swift(<5.8) extension UnsafeMutableBufferPointer { /// Deinitializes every instance in this buffer. /// @@ -24,14 +32,16 @@ extension UnsafeMutableBufferPointer { /// The range of memory is still bound to `Element`. @discardableResult @inlinable - public func deinitialize() -> UnsafeMutableRawBufferPointer { + ${modifier} func deinitialize() -> UnsafeMutableRawBufferPointer { guard let start = baseAddress else { return .init(start: nil, count: 0) } start.deinitialize(count: count) return .init(start: UnsafeMutableRawPointer(start), count: count * MemoryLayout.stride) } } +#endif +// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( extension UnsafeMutableBufferPointer { /// Initializes the buffer's memory with /// every element of the source. @@ -60,7 +70,7 @@ extension UnsafeMutableBufferPointer { /// - Returns: The index one past the last element of the buffer initialized /// by this function. @inlinable - public func initialize( + ${modifier} func initialize( fromContentsOf source: C ) -> Index where C.Element == Element { @@ -86,7 +96,10 @@ extension UnsafeMutableBufferPointer { ) return startIndex.advanced(by: copied) } +} +#if swift(<5.8) +extension UnsafeMutableBufferPointer { /// Moves every element of an initialized source buffer into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. @@ -117,7 +130,7 @@ extension UnsafeMutableBufferPointer { /// by this function. @inlinable @_alwaysEmitIntoClient - public func moveInitialize(fromContentsOf source: Self) -> Index { + ${modifier} func moveInitialize(fromContentsOf source: Self) -> Index { guard let sourceAddress = source.baseAddress, !source.isEmpty else { return startIndex } @@ -159,7 +172,7 @@ extension UnsafeMutableBufferPointer { /// by this function. @inlinable @_alwaysEmitIntoClient - public func moveInitialize(fromContentsOf source: Slice) -> Index { + ${modifier} func moveInitialize(fromContentsOf source: Slice) -> Index { return moveInitialize(fromContentsOf: Self(rebasing: source)) } @@ -174,7 +187,7 @@ extension UnsafeMutableBufferPointer { /// - index: The index of the element to initialize @inlinable @_alwaysEmitIntoClient - public func initializeElement(at index: Index, to value: Element) { + ${modifier} func initializeElement(at index: Index, to value: Element) { assert(startIndex <= index && index < endIndex) let p = baseAddress.unsafelyUnwrapped.advanced(by: index) p.initialize(to: value) @@ -192,7 +205,7 @@ extension UnsafeMutableBufferPointer { /// - Returns: The instance referenced by this index in this buffer. @inlinable @_alwaysEmitIntoClient - public func moveElement(from index: Index) -> Element { + ${modifier} func moveElement(from index: Index) -> Element { assert(startIndex <= index && index < endIndex) return baseAddress.unsafelyUnwrapped.advanced(by: index).move() } @@ -207,13 +220,15 @@ extension UnsafeMutableBufferPointer { /// - index: The index of the buffer element to deinitialize. @inlinable @_alwaysEmitIntoClient - public func deinitializeElement(at index: Index) { + ${modifier} func deinitializeElement(at index: Index) { assert(startIndex <= index && index < endIndex) let p = baseAddress.unsafelyUnwrapped.advanced(by: index) p.deinitialize(count: 1) } } +#endif +#if swift(<5.8) extension Slice { /// Initializes the buffer slice's memory with with /// every element of the source. @@ -244,7 +259,7 @@ extension Slice { /// initialized by this function. @inlinable @_alwaysEmitIntoClient - public func initialize( + ${modifier} func initialize( fromContentsOf source: C ) -> Index where Base == UnsafeMutableBufferPointer { let buffer = Base(rebasing: self) @@ -283,7 +298,7 @@ extension Slice { /// initialized by this function. @inlinable @_alwaysEmitIntoClient - public func moveInitialize( + ${modifier} func moveInitialize( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index where Base == UnsafeMutableBufferPointer { let buffer = Base(rebasing: self) @@ -322,7 +337,7 @@ extension Slice { /// initialized by this function. @inlinable @_alwaysEmitIntoClient - public func moveInitialize( + ${modifier} func moveInitialize( fromContentsOf source: Slice> ) -> Index where Base == UnsafeMutableBufferPointer { let buffer = Base(rebasing: self) @@ -344,7 +359,7 @@ extension Slice { @discardableResult @inlinable @_alwaysEmitIntoClient - public func deinitialize() -> UnsafeMutableRawBufferPointer + ${modifier} func deinitialize() -> UnsafeMutableRawBufferPointer where Base == UnsafeMutableBufferPointer { Base(rebasing: self).deinitialize() } @@ -360,12 +375,13 @@ extension Slice { /// - index: The index of the element to initialize @inlinable @_alwaysEmitIntoClient - public func initializeElement(at index: Int, to value: Element) + ${modifier} func initializeElement(at index: Int, to value: Element) where Base == UnsafeMutableBufferPointer { assert(startIndex <= index && index < endIndex) base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) } } +#endif #if swift(<5.8) extension UnsafeMutableBufferPointer { @@ -379,7 +395,7 @@ extension UnsafeMutableBufferPointer { /// - Parameters: /// - repeatedValue: The value used when updating this pointer's memory. @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Element) { + ${modifier} func update(repeating repeatedValue: Element) { guard let dstBase = baseAddress else { return } dstBase.update(repeating: repeatedValue, count: count) } @@ -398,9 +414,11 @@ extension Slice { /// - Parameters: /// - repeatedValue: The value used when updating this pointer's memory. @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Element) + ${modifier} func update(repeating repeatedValue: Element) where Base == UnsafeMutableBufferPointer { Base(rebasing: self).update(repeating: repeatedValue) } } #endif +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift b/Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb similarity index 79% rename from Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift rename to Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb index 6f32ffe73..0a90384ad 100644 --- a/Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift +++ b/Sources/_CollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb @@ -2,13 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} #if swift(<5.8) extension UnsafeMutablePointer { /// Update this pointer's initialized memory with the specified number of @@ -24,7 +31,7 @@ extension UnsafeMutablePointer { /// - count: The number of consecutive elements to update. /// `count` must not be negative. @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Pointee, count: Int) { + ${modifier} func update(repeating repeatedValue: Pointee, count: Int) { assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") for i in 0 ..< count { self[i] = repeatedValue @@ -32,3 +39,5 @@ extension UnsafeMutablePointer { } } #endif +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift b/Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb similarity index 84% rename from Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift rename to Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb index dfd4f455d..7810e0e82 100644 --- a/Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift +++ b/Sources/_CollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb @@ -2,13 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} #if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 extension UnsafeRawPointer { /// Obtain the next pointer properly aligned to store a value of type `T`. @@ -21,7 +28,7 @@ extension UnsafeRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { + ${modifier} func alignedUp(for type: T.Type) -> Self { let mask = UInt(MemoryLayout.alignment) &- 1 let bits = (UInt(bitPattern: self) &+ mask) & ~mask return Self(bitPattern: bits)! @@ -37,7 +44,7 @@ extension UnsafeRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { + ${modifier} func alignedDown(for type: T.Type) -> Self { let mask = UInt(MemoryLayout.alignment) &- 1 let bits = UInt(bitPattern: self) & ~mask return Self(bitPattern: bits)! @@ -55,7 +62,7 @@ extension UnsafeMutableRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { + ${modifier} func alignedUp(for type: T.Type) -> Self { let mask = UInt(MemoryLayout.alignment) &- 1 let bits = (UInt(bitPattern: self) &+ mask) & ~mask return Self(bitPattern: bits)! @@ -71,10 +78,12 @@ extension UnsafeMutableRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { + ${modifier} func alignedDown(for type: T.Type) -> Self { let mask = UInt(MemoryLayout.alignment) &- 1 let bits = UInt(bitPattern: self) & ~mask return Self(bitPattern: bits)! } } #endif +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/Compatibility/autogenerated/Array+WithContiguousStorage Compatibility.swift b/Sources/_CollectionsUtilities/Compatibility/autogenerated/Array+WithContiguousStorage Compatibility.swift new file mode 100644 index 000000000..f91a90887 --- /dev/null +++ b/Sources/_CollectionsUtilities/Compatibility/autogenerated/Array+WithContiguousStorage Compatibility.swift @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + +extension Array { + /// Returns true if `Array.withContiguousStorageIfAvailable` is broken + /// in the stdlib we're currently running on. + /// + /// See https://bugs.swift.org/browse/SR-14663. + @inlinable @inline(__always) + internal static func _isWCSIABroken() -> Bool { + #if _runtime(_ObjC) + guard _isBridgedVerbatimToObjectiveC(Element.self) else { + // SR-14663 only triggers on array values that are verbatim bridged + // from Objective-C, so it cannot ever trigger for element types + // that aren't verbatim bridged. + return false + } + + // SR-14663 was introduced in Swift 5.1, and it was resolved in Swift 5.5. + // Check if we have a broken stdlib. + + // The bug is caused by a bogus precondition inside a non-inlinable stdlib + // method, so to determine if we're affected, we need to check the currently + // running OS version. + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { + // The OS is too new to be affected by this bug. (>= 5.5 stdlib) + return false + } +// guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13, *) else { +// // The OS is too old to be affected by this bug. (< 5.1 stdlib) +// return false +// } + return true + #else + // Assume that other platforms aren't affected. + return false + #endif + + #else + // Platforms that don't have an Objective-C runtime don't have verbatim + // bridged array values, so the bug doesn't apply to them. + return false + #endif + } +} + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension Sequence { + // An adjusted version of the standard `withContiguousStorageIfAvailable` + // method that works around https://bugs.swift.org/browse/SR-14663. + @inlinable @inline(__always) + internal func _withContiguousStorageIfAvailable_SR14663( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + if Self.self == Array.self && Array._isWCSIABroken() { + return nil + } + + return try self.withContiguousStorageIfAvailable(body) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension Sequence { + // An adjusted version of the standard `withContiguousStorageIfAvailable` + // method that works around https://bugs.swift.org/browse/SR-14663. + @inlinable @inline(__always) + public func _withContiguousStorageIfAvailable_SR14663( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + if Self.self == Array.self && Array._isWCSIABroken() { + return nil + } + + return try self.withContiguousStorageIfAvailable(body) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift new file mode 100644 index 000000000..267c56083 --- /dev/null +++ b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift @@ -0,0 +1,832 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + +// Note: These are adapted from SE-0370 in the Swift 5.8 Standard Library. + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Deinitializes every instance in this buffer. + /// + /// The region of memory underlying this buffer must be fully initialized. + /// After calling `deinitialize(count:)`, the memory is uninitialized, + /// but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + @inlinable + internal func deinitialize() -> UnsafeMutableRawBufferPointer { + guard let start = baseAddress else { return .init(start: nil, count: 0) } + start.deinitialize(count: count) + return .init(start: UnsafeMutableRawPointer(start), + count: count * MemoryLayout.stride) + } +} +#endif + +// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( +extension UnsafeMutableBufferPointer { + /// Initializes the buffer's memory with + /// every element of the source. + /// + /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, + /// the memory referenced by the buffer must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// must not overlap. + /// + /// - Parameter source: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + internal func initialize( + fromContentsOf source: C + ) -> Index + where C.Element == Element { + let count: Int? = source._withContiguousStorageIfAvailable_SR14663 { + guard let sourceAddress = $0.baseAddress, !$0.isEmpty else { + return 0 + } + precondition( + $0.count <= self.count, + "buffer cannot contain every element from source." + ) + baseAddress?.initialize(from: sourceAddress, count: $0.count) + return $0.count + } + if let count = count { + return startIndex.advanced(by: count) + } + + var (iterator, copied) = source._copyContents(initializing: self) + precondition( + iterator.next() == nil, + "buffer cannot contain every element from source." + ) + return startIndex.advanced(by: copied) + } +} + +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// may overlap. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + @_alwaysEmitIntoClient + internal func moveInitialize(fromContentsOf source: Self) -> Index { + guard let sourceAddress = source.baseAddress, !source.isEmpty else { + return startIndex + } + precondition( + source.count <= self.count, + "buffer cannot contain every element from source." + ) + baseAddress?.moveInitialize(from: sourceAddress, count: source.count) + return startIndex.advanced(by: source.count) + } + + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// may overlap. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + @_alwaysEmitIntoClient + internal func moveInitialize(fromContentsOf source: Slice) -> Index { + return moveInitialize(fromContentsOf: Self(rebasing: source)) + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + @inlinable + @_alwaysEmitIntoClient + internal func initializeElement(at index: Index, to value: Element) { + assert(startIndex <= index && index < endIndex) + let p = baseAddress.unsafelyUnwrapped.advanced(by: index) + p.initialize(to: value) + } + + /// Retrieves and returns the element at `index`, + /// leaving that element's underlying memory uninitialized. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `moveElement(from:)`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to retrieve and deinitialize. + /// - Returns: The instance referenced by this index in this buffer. + @inlinable + @_alwaysEmitIntoClient + internal func moveElement(from index: Index) -> Element { + assert(startIndex <= index && index < endIndex) + return baseAddress.unsafelyUnwrapped.advanced(by: index).move() + } + + /// Deinitializes the memory underlying the element at `index`. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `deinitializeElement()`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to deinitialize. + @inlinable + @_alwaysEmitIntoClient + internal func deinitializeElement(at index: Index) { + assert(startIndex <= index && index < endIndex) + let p = baseAddress.unsafelyUnwrapped.advanced(by: index) + p.deinitialize(count: 1) + } +} +#endif + +#if swift(<5.8) +extension Slice { + /// Initializes the buffer slice's memory with with + /// every element of the source. + /// + /// Prior to calling the `initialize(fromContentsOf:)` method + /// on a buffer slice, the memory it references must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the index of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to + /// the buffer slice's `startIndex`. If `source` contains as many elements + /// as the buffer slice can hold, the returned index is equal to + /// to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// must not overlap. + /// + /// - Parameter source: A collection of elements to be used to + /// initialize the buffer slice's storage. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + internal func initialize( + fromContentsOf source: C + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.initialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// may overlap. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer containing the values to copy. + /// The memory region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + internal func moveInitialize( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// may overlap. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer slice containing the values to copy. + /// The memory region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + internal func moveInitialize( + fromContentsOf source: Slice> + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Deinitializes every instance in this buffer slice. + /// + /// The region of memory underlying this buffer slice must be fully + /// initialized. After calling `deinitialize(count:)`, the memory + /// is uninitialized, but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + @inlinable + @_alwaysEmitIntoClient + internal func deinitialize() -> UnsafeMutableRawBufferPointer + where Base == UnsafeMutableBufferPointer { + Base(rebasing: self).deinitialize() + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer slice is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + @inlinable + @_alwaysEmitIntoClient + internal func initializeElement(at index: Int, to value: Element) + where Base == UnsafeMutableBufferPointer { + assert(startIndex <= index && index < endIndex) + base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) + } +} +#endif + +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Updates every element of this buffer's initialized memory. + /// + /// The buffer’s memory must be initialized or its `Element` type + /// must be a trivial type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + @_alwaysEmitIntoClient + internal func update(repeating repeatedValue: Element) { + guard let dstBase = baseAddress else { return } + dstBase.update(repeating: repeatedValue, count: count) + } +} +#endif + +#if swift(<5.8) +extension Slice { + /// Updates every element of this buffer slice's initialized memory. + /// + /// The buffer slice’s memory must be initialized or its `Element` + /// must be a trivial type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + @_alwaysEmitIntoClient + internal func update(repeating repeatedValue: Element) + where Base == UnsafeMutableBufferPointer { + Base(rebasing: self).update(repeating: repeatedValue) + } +} +#endif +#else // !COLLECTIONS_SINGLE_MODULE +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Deinitializes every instance in this buffer. + /// + /// The region of memory underlying this buffer must be fully initialized. + /// After calling `deinitialize(count:)`, the memory is uninitialized, + /// but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + @inlinable + public func deinitialize() -> UnsafeMutableRawBufferPointer { + guard let start = baseAddress else { return .init(start: nil, count: 0) } + start.deinitialize(count: count) + return .init(start: UnsafeMutableRawPointer(start), + count: count * MemoryLayout.stride) + } +} +#endif + +// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( +extension UnsafeMutableBufferPointer { + /// Initializes the buffer's memory with + /// every element of the source. + /// + /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, + /// the memory referenced by the buffer must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// must not overlap. + /// + /// - Parameter source: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + public func initialize( + fromContentsOf source: C + ) -> Index + where C.Element == Element { + let count: Int? = source._withContiguousStorageIfAvailable_SR14663 { + guard let sourceAddress = $0.baseAddress, !$0.isEmpty else { + return 0 + } + precondition( + $0.count <= self.count, + "buffer cannot contain every element from source." + ) + baseAddress?.initialize(from: sourceAddress, count: $0.count) + return $0.count + } + if let count = count { + return startIndex.advanced(by: count) + } + + var (iterator, copied) = source._copyContents(initializing: self) + precondition( + iterator.next() == nil, + "buffer cannot contain every element from source." + ) + return startIndex.advanced(by: copied) + } +} + +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// may overlap. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + @_alwaysEmitIntoClient + public func moveInitialize(fromContentsOf source: Self) -> Index { + guard let sourceAddress = source.baseAddress, !source.isEmpty else { + return startIndex + } + precondition( + source.count <= self.count, + "buffer cannot contain every element from source." + ) + baseAddress?.moveInitialize(from: sourceAddress, count: source.count) + return startIndex.advanced(by: source.count) + } + + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer + /// may overlap. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer initialized + /// by this function. + @inlinable + @_alwaysEmitIntoClient + public func moveInitialize(fromContentsOf source: Slice) -> Index { + return moveInitialize(fromContentsOf: Self(rebasing: source)) + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + @inlinable + @_alwaysEmitIntoClient + public func initializeElement(at index: Index, to value: Element) { + assert(startIndex <= index && index < endIndex) + let p = baseAddress.unsafelyUnwrapped.advanced(by: index) + p.initialize(to: value) + } + + /// Retrieves and returns the element at `index`, + /// leaving that element's underlying memory uninitialized. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `moveElement(from:)`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to retrieve and deinitialize. + /// - Returns: The instance referenced by this index in this buffer. + @inlinable + @_alwaysEmitIntoClient + public func moveElement(from index: Index) -> Element { + assert(startIndex <= index && index < endIndex) + return baseAddress.unsafelyUnwrapped.advanced(by: index).move() + } + + /// Deinitializes the memory underlying the element at `index`. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `deinitializeElement()`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to deinitialize. + @inlinable + @_alwaysEmitIntoClient + public func deinitializeElement(at index: Index) { + assert(startIndex <= index && index < endIndex) + let p = baseAddress.unsafelyUnwrapped.advanced(by: index) + p.deinitialize(count: 1) + } +} +#endif + +#if swift(<5.8) +extension Slice { + /// Initializes the buffer slice's memory with with + /// every element of the source. + /// + /// Prior to calling the `initialize(fromContentsOf:)` method + /// on a buffer slice, the memory it references must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the index of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to + /// the buffer slice's `startIndex`. If `source` contains as many elements + /// as the buffer slice can hold, the returned index is equal to + /// to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// must not overlap. + /// + /// - Parameter source: A collection of elements to be used to + /// initialize the buffer slice's storage. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + public func initialize( + fromContentsOf source: C + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.initialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// may overlap. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer containing the values to copy. + /// The memory region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + public func moveInitialize( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Note: The memory regions referenced by `source` and this buffer slice + /// may overlap. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer slice containing the values to copy. + /// The memory region underlying `source` must be initialized. + /// - Returns: The index one past the last element of the buffer slice + /// initialized by this function. + @inlinable + @_alwaysEmitIntoClient + public func moveInitialize( + fromContentsOf source: Slice> + ) -> Index where Base == UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromContentsOf: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Deinitializes every instance in this buffer slice. + /// + /// The region of memory underlying this buffer slice must be fully + /// initialized. After calling `deinitialize(count:)`, the memory + /// is uninitialized, but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + @inlinable + @_alwaysEmitIntoClient + public func deinitialize() -> UnsafeMutableRawBufferPointer + where Base == UnsafeMutableBufferPointer { + Base(rebasing: self).deinitialize() + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer slice is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + @inlinable + @_alwaysEmitIntoClient + public func initializeElement(at index: Int, to value: Element) + where Base == UnsafeMutableBufferPointer { + assert(startIndex <= index && index < endIndex) + base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) + } +} +#endif + +#if swift(<5.8) +extension UnsafeMutableBufferPointer { + /// Updates every element of this buffer's initialized memory. + /// + /// The buffer’s memory must be initialized or its `Element` type + /// must be a trivial type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + @_alwaysEmitIntoClient + public func update(repeating repeatedValue: Element) { + guard let dstBase = baseAddress else { return } + dstBase.update(repeating: repeatedValue, count: count) + } +} +#endif + +#if swift(<5.8) +extension Slice { + /// Updates every element of this buffer slice's initialized memory. + /// + /// The buffer slice’s memory must be initialized or its `Element` + /// must be a trivial type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + @_alwaysEmitIntoClient + public func update(repeating repeatedValue: Element) + where Base == UnsafeMutableBufferPointer { + Base(rebasing: self).update(repeating: repeatedValue) + } +} +#endif +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift new file mode 100644 index 000000000..e7ea2d268 --- /dev/null +++ b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +#if swift(<5.8) +extension UnsafeMutablePointer { + /// Update this pointer's initialized memory with the specified number of + /// consecutive copies of the given value. + /// + /// The region of memory starting at this pointer and covering `count` + /// instances of the pointer's `Pointee` type must be initialized or + /// `Pointee` must be a trivial type. After calling + /// `update(repeating:count:)`, the region is initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + /// - count: The number of consecutive elements to update. + /// `count` must not be negative. + @_alwaysEmitIntoClient + internal func update(repeating repeatedValue: Pointee, count: Int) { + assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") + for i in 0 ..< count { + self[i] = repeatedValue + } + } +} +#endif +#else // !COLLECTIONS_SINGLE_MODULE +#if swift(<5.8) +extension UnsafeMutablePointer { + /// Update this pointer's initialized memory with the specified number of + /// consecutive copies of the given value. + /// + /// The region of memory starting at this pointer and covering `count` + /// instances of the pointer's `Pointee` type must be initialized or + /// `Pointee` must be a trivial type. After calling + /// `update(repeating:count:)`, the region is initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + /// - count: The number of consecutive elements to update. + /// `count` must not be negative. + @_alwaysEmitIntoClient + public func update(repeating repeatedValue: Pointee, count: Int) { + assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") + for i in 0 ..< count { + self[i] = repeatedValue + } + } +} +#endif +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift new file mode 100644 index 000000000..f28f29151 --- /dev/null +++ b/Sources/_CollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +#if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 +extension UnsafeRawPointer { + /// Obtain the next pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + internal func alignedUp(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = (UInt(bitPattern: self) &+ mask) & ~mask + return Self(bitPattern: bits)! + } + + /// Obtain the preceding pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + internal func alignedDown(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = UInt(bitPattern: self) & ~mask + return Self(bitPattern: bits)! + } +} + +extension UnsafeMutableRawPointer { + /// Obtain the next pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + internal func alignedUp(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = (UInt(bitPattern: self) &+ mask) & ~mask + return Self(bitPattern: bits)! + } + + /// Obtain the preceding pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + internal func alignedDown(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = UInt(bitPattern: self) & ~mask + return Self(bitPattern: bits)! + } +} +#endif +#else // !COLLECTIONS_SINGLE_MODULE +#if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 +extension UnsafeRawPointer { + /// Obtain the next pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + public func alignedUp(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = (UInt(bitPattern: self) &+ mask) & ~mask + return Self(bitPattern: bits)! + } + + /// Obtain the preceding pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + public func alignedDown(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = UInt(bitPattern: self) & ~mask + return Self(bitPattern: bits)! + } +} + +extension UnsafeMutableRawPointer { + /// Obtain the next pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + public func alignedUp(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = (UInt(bitPattern: self) &+ mask) & ~mask + return Self(bitPattern: bits)! + } + + /// Obtain the preceding pointer properly aligned to store a value of type `T`. + /// + /// If `self` is properly aligned for accessing `T`, + /// this function returns `self`. + /// + /// - Parameters: + /// - type: the type to be stored at the returned address. + /// - Returns: a pointer properly aligned to store a value of type `T`. + @inlinable + @_alwaysEmitIntoClient + public func alignedDown(for type: T.Type) -> Self { + let mask = UInt(MemoryLayout.alignment) &- 1 + let bits = UInt(bitPattern: self) & ~mask + return Self(bitPattern: bits)! + } +} +#endif +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/Debugging.swift b/Sources/_CollectionsUtilities/Debugging.swift.gyb similarity index 72% rename from Sources/_CollectionsUtilities/Debugging.swift rename to Sources/_CollectionsUtilities/Debugging.swift.gyb index 90470b0df..09dc0e2ed 100644 --- a/Sources/_CollectionsUtilities/Debugging.swift +++ b/Sources/_CollectionsUtilities/Debugging.swift.gyb @@ -2,13 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} /// True if consistency checking is enabled in the implementation of the /// Swift Collections package, false otherwise. /// @@ -16,10 +23,12 @@ /// returns true -- for example, operations that are documented to take /// O(1) time might take O(*n*) time, or worse. @inlinable @inline(__always) -public var _isCollectionsInternalCheckingEnabled: Bool { +${modifier} var _isCollectionsInternalCheckingEnabled: Bool { #if COLLECTIONS_INTERNAL_CHECKS return true #else return false #endif } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/Descriptions.swift b/Sources/_CollectionsUtilities/Descriptions.swift.gyb similarity index 69% rename from Sources/_CollectionsUtilities/Descriptions.swift rename to Sources/_CollectionsUtilities/Descriptions.swift.gyb index 73f9ad996..73e10e11e 100644 --- a/Sources/_CollectionsUtilities/Descriptions.swift +++ b/Sources/_CollectionsUtilities/Descriptions.swift.gyb @@ -2,29 +2,39 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// -public func _addressString(for pointer: UnsafeRawPointer) -> String { +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} + +${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } +${modifier} func _addressString(for pointer: UnsafeRawPointer) -> String { let address = UInt(bitPattern: pointer) return "0x\(String(address, radix: 16))" } -public func _addressString(for object: AnyObject) -> String { +${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } +${modifier} func _addressString(for object: AnyObject) -> String { _addressString(for: Unmanaged.passUnretained(object).toOpaque()) } -@inlinable -public func _addressString(for object: Unmanaged) -> String { +${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } +${modifier} func _addressString(for object: Unmanaged) -> String { _addressString(for: object.toOpaque()) } @inlinable -public func _arrayDescription( +${modifier} func _arrayDescription( for elements: C, debug: Bool = false, typeName: String? = nil @@ -53,7 +63,7 @@ public func _arrayDescription( } @inlinable -public func _dictionaryDescription( +${modifier} func _dictionaryDescription( for elements: C, debug: Bool = false, typeName: String? = nil @@ -90,3 +100,5 @@ public func _dictionaryDescription( } return result } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift b/Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb similarity index 69% rename from Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift rename to Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb index 518e257eb..7462a8a97 100644 --- a/Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift +++ b/Sources/_CollectionsUtilities/IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb @@ -2,20 +2,29 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension FixedWidthInteger { /// Round up `self` to the nearest power of two, assuming it's representable. /// Returns 0 if `self` isn't positive. @inlinable - public func _roundUpToPowerOfTwo() -> Self { + ${modifier} func _roundUpToPowerOfTwo() -> Self { guard self > 0 else { return 0 } let l = Self.bitWidth - (self &- 1).leadingZeroBitCount return 1 << l } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift b/Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift.gyb similarity index 84% rename from Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift rename to Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift.gyb index bd8e638fd..de75e1f73 100644 --- a/Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift +++ b/Sources/_CollectionsUtilities/IntegerTricks/Integer rank.swift.gyb @@ -2,14 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension FixedWidthInteger { @inlinable @inline(__always) internal var _nonzeroBitCount: Self { @@ -17,7 +23,7 @@ extension FixedWidthInteger { } @inlinable @inline(__always) - public func _rank(ofBit bit: UInt) -> Int { + ${modifier} func _rank(ofBit bit: UInt) -> Int { assert(bit < Self.bitWidth) let mask: Self = (1 &<< bit) &- 1 return (self & mask).nonzeroBitCount @@ -26,7 +32,8 @@ extension FixedWidthInteger { extension UInt { @_effects(releasenone) - public func _bit(ranked n: Int) -> UInt? { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func _bit(ranked n: Int) -> UInt? { // FIXME: Use bit deposit instruction when available (PDEP on Intel). assert(UInt.bitWidth == 64 || UInt.bitWidth == 32, "Unsupported UInt bitWidth") @@ -75,7 +82,8 @@ extension UInt32 { // Returns the position of the `n`th set bit in `self`, i.e., the bit with // rank `n`. @_effects(releasenone) - public func _bit(ranked n: Int) -> UInt? { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func _bit(ranked n: Int) -> UInt? { // FIXME: Use bit deposit instruction when available (PDEP on Intel). assert(n >= 0 && n < Self.bitWidth) var shift: Self = 0 @@ -114,7 +122,8 @@ extension UInt16 { // Returns the position of the `n`th set bit in `self`, i.e., the bit with // rank `n`. @_effects(releasenone) - public func _bit(ranked n: Int) -> UInt? { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func _bit(ranked n: Int) -> UInt? { // FIXME: Use bit deposit instruction when available (PDEP on Intel). assert(n >= 0 && n < Self.bitWidth) var shift: Self = 0 @@ -143,3 +152,5 @@ extension UInt16 { return UInt(truncatingIfNeeded: shift) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift b/Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift.gyb similarity index 68% rename from Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift rename to Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift.gyb index 4a0be877b..86c463910 100644 --- a/Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift +++ b/Sources/_CollectionsUtilities/IntegerTricks/UInt+first and last set bit.swift.gyb @@ -2,24 +2,33 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension UInt { @inlinable @inline(__always) - public var _firstSetBit: UInt? { + ${modifier} var _firstSetBit: UInt? { guard self != 0 else { return nil } let v = UInt.bitWidth &- 1 &- self.leadingZeroBitCount return UInt(truncatingIfNeeded: v) } @inlinable @inline(__always) - public var _lastSetBit: UInt? { + ${modifier} var _lastSetBit: UInt? { guard self != 0 else { return nil } return UInt(truncatingIfNeeded: self.trailingZeroBitCount) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift b/Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift.gyb similarity index 73% rename from Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift rename to Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift.gyb index 4d48c5014..c24bdffcd 100644 --- a/Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift +++ b/Sources/_CollectionsUtilities/IntegerTricks/UInt+reversed.swift.gyb @@ -2,16 +2,23 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension UInt { @inlinable - public var _reversed: UInt { + ${modifier} var _reversed: UInt { // https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel var shift: UInt = UInt(UInt.bitWidth) var mask: UInt = ~0; @@ -25,3 +32,5 @@ extension UInt { return result } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/FixedWidthInteger+roundUpToPowerOfTwo.swift b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/FixedWidthInteger+roundUpToPowerOfTwo.swift new file mode 100644 index 000000000..a20e38068 --- /dev/null +++ b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/FixedWidthInteger+roundUpToPowerOfTwo.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension FixedWidthInteger { + /// Round up `self` to the nearest power of two, assuming it's representable. + /// Returns 0 if `self` isn't positive. + @inlinable + internal func _roundUpToPowerOfTwo() -> Self { + guard self > 0 else { return 0 } + let l = Self.bitWidth - (self &- 1).leadingZeroBitCount + return 1 << l + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension FixedWidthInteger { + /// Round up `self` to the nearest power of two, assuming it's representable. + /// Returns 0 if `self` isn't positive. + @inlinable + public func _roundUpToPowerOfTwo() -> Self { + guard self > 0 else { return 0 } + let l = Self.bitWidth - (self &- 1).leadingZeroBitCount + return 1 << l + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/Integer rank.swift b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/Integer rank.swift new file mode 100644 index 000000000..02790d35a --- /dev/null +++ b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/Integer rank.swift @@ -0,0 +1,298 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension FixedWidthInteger { + @inlinable @inline(__always) + internal var _nonzeroBitCount: Self { + Self(truncatingIfNeeded: nonzeroBitCount) + } + + @inlinable @inline(__always) + internal func _rank(ofBit bit: UInt) -> Int { + assert(bit < Self.bitWidth) + let mask: Self = (1 &<< bit) &- 1 + return (self & mask).nonzeroBitCount + } +} + +extension UInt { + @_effects(releasenone) + @usableFromInline + internal func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(UInt.bitWidth == 64 || UInt.bitWidth == 32, + "Unsupported UInt bitWidth") + + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + + if Self.bitWidth == 64 { + let c32 = (self & 0xFFFFFFFF)._nonzeroBitCount + if n >= c32 { + shift &+= 32 + n &-= c32 + } + } + let c16 = ((self &>> shift) & 0xFFFF)._nonzeroBitCount + if n >= c16 { + shift &+= 16 + n &-= c16 + } + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n &-= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n &-= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n &-= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n &-= c1 + } + guard n == 0 && (self &>> shift) & 0x1 == 1 else { return nil } + return shift + } +} + +extension UInt32 { + // Returns the position of the `n`th set bit in `self`, i.e., the bit with + // rank `n`. + @_effects(releasenone) + @usableFromInline + internal func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(n >= 0 && n < Self.bitWidth) + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + let c16 = (self & 0xFFFF)._nonzeroBitCount + if n >= c16 { + shift = 16 + n -= c16 + } + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n -= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n -= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n -= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n -= c1 + } + guard n == 0, (self &>> shift) & 0x1 == 1 else { return nil } + return UInt(truncatingIfNeeded: shift) + } +} + +extension UInt16 { + // Returns the position of the `n`th set bit in `self`, i.e., the bit with + // rank `n`. + @_effects(releasenone) + @usableFromInline + internal func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(n >= 0 && n < Self.bitWidth) + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n -= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n -= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n -= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n -= c1 + } + guard n == 0, (self &>> shift) & 0x1 == 1 else { return nil } + return UInt(truncatingIfNeeded: shift) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension FixedWidthInteger { + @inlinable @inline(__always) + internal var _nonzeroBitCount: Self { + Self(truncatingIfNeeded: nonzeroBitCount) + } + + @inlinable @inline(__always) + public func _rank(ofBit bit: UInt) -> Int { + assert(bit < Self.bitWidth) + let mask: Self = (1 &<< bit) &- 1 + return (self & mask).nonzeroBitCount + } +} + +extension UInt { + @_effects(releasenone) + //@usableFromInline + public func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(UInt.bitWidth == 64 || UInt.bitWidth == 32, + "Unsupported UInt bitWidth") + + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + + if Self.bitWidth == 64 { + let c32 = (self & 0xFFFFFFFF)._nonzeroBitCount + if n >= c32 { + shift &+= 32 + n &-= c32 + } + } + let c16 = ((self &>> shift) & 0xFFFF)._nonzeroBitCount + if n >= c16 { + shift &+= 16 + n &-= c16 + } + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n &-= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n &-= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n &-= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n &-= c1 + } + guard n == 0 && (self &>> shift) & 0x1 == 1 else { return nil } + return shift + } +} + +extension UInt32 { + // Returns the position of the `n`th set bit in `self`, i.e., the bit with + // rank `n`. + @_effects(releasenone) + //@usableFromInline + public func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(n >= 0 && n < Self.bitWidth) + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + let c16 = (self & 0xFFFF)._nonzeroBitCount + if n >= c16 { + shift = 16 + n -= c16 + } + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n -= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n -= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n -= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n -= c1 + } + guard n == 0, (self &>> shift) & 0x1 == 1 else { return nil } + return UInt(truncatingIfNeeded: shift) + } +} + +extension UInt16 { + // Returns the position of the `n`th set bit in `self`, i.e., the bit with + // rank `n`. + @_effects(releasenone) + //@usableFromInline + public func _bit(ranked n: Int) -> UInt? { + // FIXME: Use bit deposit instruction when available (PDEP on Intel). + assert(n >= 0 && n < Self.bitWidth) + var shift: Self = 0 + var n = Self(truncatingIfNeeded: n) + let c8 = ((self &>> shift) & 0xFF)._nonzeroBitCount + if n >= c8 { + shift &+= 8 + n -= c8 + } + let c4 = ((self &>> shift) & 0xF)._nonzeroBitCount + if n >= c4 { + shift &+= 4 + n -= c4 + } + let c2 = ((self &>> shift) & 0x3)._nonzeroBitCount + if n >= c2 { + shift &+= 2 + n -= c2 + } + let c1 = (self &>> shift) & 0x1 + if n >= c1 { + shift &+= 1 + n -= c1 + } + guard n == 0, (self &>> shift) & 0x1 == 1 else { return nil } + return UInt(truncatingIfNeeded: shift) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+first and last set bit.swift b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+first and last set bit.swift new file mode 100644 index 000000000..349a772b2 --- /dev/null +++ b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+first and last set bit.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension UInt { + @inlinable @inline(__always) + internal var _firstSetBit: UInt? { + guard self != 0 else { return nil } + let v = UInt.bitWidth &- 1 &- self.leadingZeroBitCount + return UInt(truncatingIfNeeded: v) + } + + @inlinable @inline(__always) + internal var _lastSetBit: UInt? { + guard self != 0 else { return nil } + return UInt(truncatingIfNeeded: self.trailingZeroBitCount) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension UInt { + @inlinable @inline(__always) + public var _firstSetBit: UInt? { + guard self != 0 else { return nil } + let v = UInt.bitWidth &- 1 &- self.leadingZeroBitCount + return UInt(truncatingIfNeeded: v) + } + + @inlinable @inline(__always) + public var _lastSetBit: UInt? { + guard self != 0 else { return nil } + return UInt(truncatingIfNeeded: self.trailingZeroBitCount) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+reversed.swift b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+reversed.swift new file mode 100644 index 000000000..ff90b6192 --- /dev/null +++ b/Sources/_CollectionsUtilities/IntegerTricks/autogenerated/UInt+reversed.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension UInt { + @inlinable + internal var _reversed: UInt { + // https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + var shift: UInt = UInt(UInt.bitWidth) + var mask: UInt = ~0; + var result = self + while true { + shift &>>= 1 + guard shift > 0 else { break } + mask ^= mask &<< shift + result = ((result &>> shift) & mask) | ((result &<< shift) & ~mask) + } + return result + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension UInt { + @inlinable + public var _reversed: UInt { + // https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + var shift: UInt = UInt(UInt.bitWidth) + var mask: UInt = ~0; + var result = self + while true { + shift &>>= 1 + guard shift > 0 else { break } + mask ^= mask &<< shift + result = ((result &>> shift) & mask) | ((result &<< shift) & ~mask) + } + return result + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift b/Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift.gyb similarity index 62% rename from Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift rename to Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift.gyb index c82c82384..1faa1452a 100644 --- a/Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift +++ b/Sources/_CollectionsUtilities/RandomAccessCollection+Offsets.swift.gyb @@ -2,26 +2,35 @@ // // 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 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension RandomAccessCollection { @_alwaysEmitIntoClient @inline(__always) - public func _index(at offset: Int) -> Index { + ${modifier} func _index(at offset: Int) -> Index { index(startIndex, offsetBy: offset) } @_alwaysEmitIntoClient @inline(__always) - public func _offset(of index: Index) -> Int { + ${modifier} func _offset(of index: Index) -> Int { distance(from: startIndex, to: index) } @_alwaysEmitIntoClient @inline(__always) - public subscript(_offset offset: Int) -> Element { + ${modifier} subscript(_offset offset: Int) -> Element { self[_index(at: offset)] } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift.gyb similarity index 62% rename from Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift rename to Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift.gyb index 712c524ca..c4f52e63b 100644 --- a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+Index.swift.gyb @@ -2,33 +2,41 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension _UnsafeBitSet { - @frozen - public struct Index: Comparable, Hashable { + ${"@frozen" if modifier == "public" else "@frozen @usableFromInline"} + ${modifier} struct Index: Comparable, Hashable { @usableFromInline internal typealias _Word = _UnsafeBitSet._Word - public var value: UInt + ${"@usableFromInline" if modifier != "public" else ""} + ${modifier} var value: UInt @inlinable - public init(_ value: UInt) { + ${modifier} init(_ value: UInt) { self.value = value } @inlinable - public init(_ value: Int) { + ${modifier} init(_ value: Int) { self.value = UInt(value) } @inlinable - public init(word: Int, bit: UInt) { + ${modifier} init(word: Int, bit: UInt) { assert(word >= 0 && word <= Int.max / _Word.capacity) assert(bit < _Word.capacity) self.value = UInt(word &* _Word.capacity) &+ bit @@ -38,24 +46,24 @@ extension _UnsafeBitSet { extension _UnsafeBitSet.Index { @inlinable - public var word: Int { + ${modifier} var word: Int { // Note: We perform on UInts to get faster unsigned math (shifts). Int(truncatingIfNeeded: value / UInt(bitPattern: _Word.capacity)) } @inlinable - public var bit: UInt { + ${modifier} var bit: UInt { // Note: We perform on UInts to get faster unsigned math (masking). value % UInt(bitPattern: _Word.capacity) } @inlinable - public var split: (word: Int, bit: UInt) { + ${modifier} var split: (word: Int, bit: UInt) { (word, bit) } @inlinable - public var endSplit: (word: Int, bit: UInt) { + ${modifier} var endSplit: (word: Int, bit: UInt) { let w = word let b = bit if w > 0, b == 0 { return (w &- 1, UInt(_Word.capacity)) } @@ -63,17 +71,17 @@ extension _UnsafeBitSet.Index { } @inlinable - public static func ==(left: Self, right: Self) -> Bool { + ${modifier} static func ==(left: Self, right: Self) -> Bool { left.value == right.value } @inlinable - public static func <(left: Self, right: Self) -> Bool { + ${modifier} static func <(left: Self, right: Self) -> Bool { left.value < right.value } @inlinable - public func hash(into hasher: inout Hasher) { + ${modifier} func hash(into hasher: inout Hasher) { hasher.combine(value) } @@ -87,3 +95,5 @@ extension _UnsafeBitSet.Index { Self(value - 1) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift.gyb similarity index 69% rename from Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift rename to Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift.gyb index 3d465f6c8..8b9b9547c 100644 --- a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet+_Word.swift.gyb @@ -2,34 +2,43 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension _UnsafeBitSet { - @frozen - public struct _Word { - public var value: UInt + ${"@frozen" if modifier == "public" else "@frozen @usableFromInline"} + ${modifier} struct _Word { + ${"@usableFromInline" if modifier != "public" else ""} + ${modifier} var value: UInt @inlinable @inline(__always) - public init(_ value: UInt) { + ${modifier} init(_ value: UInt) { self.value = value } @inlinable @inline(__always) - public init(upTo bit: UInt) { + ${modifier} init(upTo bit: UInt) { assert(bit <= _Word.capacity) self.init((1 << bit) &- 1) } @inlinable @inline(__always) - public init(from start: UInt, to end: UInt) { + ${modifier} init(from start: UInt, to end: UInt) { assert(start <= end && end <= _Word.capacity) self = Self(upTo: end).symmetricDifference(Self(upTo: start)) } @@ -37,7 +46,8 @@ extension _UnsafeBitSet { } extension _UnsafeBitSet._Word: CustomStringConvertible { - public var description: String { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} var description: String { String(value, radix: 16) } } @@ -79,7 +89,7 @@ extension _UnsafeBitSet._Word { extension _UnsafeBitSet._Word { @inlinable @inline(__always) - public static func wordCount(forBitCount count: UInt) -> Int { + ${modifier} static func wordCount(forBitCount count: UInt) -> Int { // Note: We perform on UInts to get faster unsigned math (shifts). let width = UInt(bitPattern: Self.capacity) return Int(bitPattern: (count + width - 1) / width) @@ -89,51 +99,51 @@ extension _UnsafeBitSet._Word { extension _UnsafeBitSet._Word { @inlinable @inline(__always) - public static var capacity: Int { + ${modifier} static var capacity: Int { return UInt.bitWidth } @inlinable @inline(__always) - public var count: Int { + ${modifier} var count: Int { value.nonzeroBitCount } @inlinable @inline(__always) - public var isEmpty: Bool { + ${modifier} var isEmpty: Bool { value == 0 } @inlinable @inline(__always) - public var isFull: Bool { + ${modifier} var isFull: Bool { value == UInt.max } @inlinable @inline(__always) - public func contains(_ bit: UInt) -> Bool { + ${modifier} func contains(_ bit: UInt) -> Bool { assert(bit >= 0 && bit < UInt.bitWidth) return value & (1 &<< bit) != 0 } @inlinable @inline(__always) - public var firstMember: UInt? { + ${modifier} var firstMember: UInt? { value._lastSetBit } @inlinable @inline(__always) - public var lastMember: UInt? { + ${modifier} var lastMember: UInt? { value._firstSetBit } @inlinable @inline(__always) @discardableResult - public mutating func insert(_ bit: UInt) -> Bool { + ${modifier} mutating func insert(_ bit: UInt) -> Bool { assert(bit < UInt.bitWidth) let mask: UInt = 1 &<< bit let inserted = value & mask == 0 @@ -144,7 +154,7 @@ extension _UnsafeBitSet._Word { @inlinable @inline(__always) @discardableResult - public mutating func remove(_ bit: UInt) -> Bool { + ${modifier} mutating func remove(_ bit: UInt) -> Bool { assert(bit < UInt.bitWidth) let mask: UInt = 1 &<< bit let removed = (value & mask) != 0 @@ -154,7 +164,7 @@ extension _UnsafeBitSet._Word { @inlinable @inline(__always) - public mutating func update(_ bit: UInt, to value: Bool) { + ${modifier} mutating func update(_ bit: UInt, to value: Bool) { assert(bit < UInt.bitWidth) let mask: UInt = 1 &<< bit if value { @@ -203,12 +213,12 @@ extension _UnsafeBitSet._Word { extension _UnsafeBitSet._Word { @inlinable @inline(__always) - public static var empty: Self { + ${modifier} static var empty: Self { Self(0) } @inline(__always) - public static var allBits: Self { + ${modifier} static var allBits: Self { Self(UInt.max) } } @@ -219,14 +229,14 @@ extension _UnsafeBitSet._Word { // iterator, not the original word. extension _UnsafeBitSet._Word: Sequence, IteratorProtocol { @inlinable @inline(__always) - public var underestimatedCount: Int { + ${modifier} var underestimatedCount: Int { count } /// Return the index of the lowest set bit in this word, /// and also destructively clear it. @inlinable - public mutating func next() -> UInt? { + ${modifier} mutating func next() -> UInt? { guard value != 0 else { return nil } let bit = UInt(truncatingIfNeeded: value.trailingZeroBitCount) value &= value &- 1 // Clear lowest nonzero bit. @@ -236,66 +246,66 @@ extension _UnsafeBitSet._Word: Sequence, IteratorProtocol { extension _UnsafeBitSet._Word: Equatable { @inlinable - public static func ==(left: Self, right: Self) -> Bool { + ${modifier} static func ==(left: Self, right: Self) -> Bool { left.value == right.value } } extension _UnsafeBitSet._Word: Hashable { @inlinable - public func hash(into hasher: inout Hasher) { + ${modifier} func hash(into hasher: inout Hasher) { hasher.combine(value) } } extension _UnsafeBitSet._Word { - @inline(__always) - public func complement() -> Self { + @inlinable @inline(__always) + ${modifier} func complement() -> Self { Self(~self.value) } - @inline(__always) - public mutating func formComplement() { + @inlinable @inline(__always) + ${modifier} mutating func formComplement() { self.value = ~self.value } - @inline(__always) - public func union(_ other: Self) -> Self { + @inlinable @inline(__always) + ${modifier} func union(_ other: Self) -> Self { Self(self.value | other.value) } - @inline(__always) - public mutating func formUnion(_ other: Self) { + @inlinable @inline(__always) + ${modifier} mutating func formUnion(_ other: Self) { self.value |= other.value } - @inline(__always) - public func intersection(_ other: Self) -> Self { + @inlinable @inline(__always) + ${modifier} func intersection(_ other: Self) -> Self { Self(self.value & other.value) } - @inline(__always) - public mutating func formIntersection(_ other: Self) { + @inlinable @inline(__always) + ${modifier} mutating func formIntersection(_ other: Self) { self.value &= other.value } - @inline(__always) - public func symmetricDifference(_ other: Self) -> Self { + @inlinable @inline(__always) + ${modifier} func symmetricDifference(_ other: Self) -> Self { Self(self.value ^ other.value) } - @inline(__always) - public mutating func formSymmetricDifference(_ other: Self) { + @inlinable @inline(__always) + ${modifier} mutating func formSymmetricDifference(_ other: Self) { self.value ^= other.value } - @inline(__always) - public func subtracting(_ other: Self) -> Self { + @inlinable @inline(__always) + ${modifier} func subtracting(_ other: Self) -> Self { Self(self.value & ~other.value) } - @inline(__always) - public mutating func subtract(_ other: Self) { + @inlinable @inline(__always) + ${modifier} mutating func subtract(_ other: Self) { self.value &= ~other.value } } @@ -303,15 +313,17 @@ extension _UnsafeBitSet._Word { extension _UnsafeBitSet._Word { @inlinable @inline(__always) - public func shiftedDown(by shift: UInt) -> Self { + ${modifier} func shiftedDown(by shift: UInt) -> Self { assert(shift >= 0 && shift < Self.capacity) return Self(self.value &>> shift) } @inlinable @inline(__always) - public func shiftedUp(by shift: UInt) -> Self { + ${modifier} func shiftedUp(by shift: UInt) -> Self { assert(shift >= 0 && shift < Self.capacity) return Self(self.value &<< shift) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift.gyb similarity index 75% rename from Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift rename to Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift.gyb index 049f24ede..1c9d12f7c 100644 --- a/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/_UnsafeBitSet.swift.gyb @@ -2,19 +2,27 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} /// An unsafe-unowned bitset view over `UInt` storage, providing bit set /// primitives. -@frozen -public struct _UnsafeBitSet { +${"@frozen" if modifier == "public" else "@frozen @usableFromInline"} +${modifier} struct _UnsafeBitSet { /// An unsafe-unowned storage view. - public let _words: UnsafeBufferPointer<_Word> + ${"@usableFromInline" if modifier != "public" else ""} + ${modifier} let _words: UnsafeBufferPointer<_Word> #if DEBUG /// True when this handle does not support table mutations. @@ -25,7 +33,7 @@ public struct _UnsafeBitSet { @inlinable @inline(__always) - public func ensureMutable() { + ${modifier} func ensureMutable() { #if DEBUG assert(_mutable) #endif @@ -33,14 +41,14 @@ public struct _UnsafeBitSet { @inlinable @inline(__always) - public var _mutableWords: UnsafeMutableBufferPointer<_Word> { + ${modifier} var _mutableWords: UnsafeMutableBufferPointer<_Word> { ensureMutable() return UnsafeMutableBufferPointer(mutating: _words) } @inlinable @inline(__always) - public init( + ${modifier} init( words: UnsafeBufferPointer<_Word>, mutable: Bool ) { @@ -53,7 +61,7 @@ public struct _UnsafeBitSet { @inlinable @inline(__always) - public init( + ${modifier} init( words: UnsafeMutableBufferPointer<_Word>, mutable: Bool ) { @@ -64,7 +72,7 @@ public struct _UnsafeBitSet { extension _UnsafeBitSet { @inlinable @inline(__always) - public var wordCount: Int { + ${modifier} var wordCount: Int { _words.count } } @@ -72,7 +80,7 @@ extension _UnsafeBitSet { extension _UnsafeBitSet { @inlinable @inline(__always) - public static func withTemporaryBitSet( + ${modifier} static func withTemporaryBitSet( capacity: Int, run body: (inout _UnsafeBitSet) throws -> R ) rethrows -> R { @@ -82,7 +90,7 @@ extension _UnsafeBitSet { @inlinable @inline(__always) - public static func withTemporaryBitSet( + ${modifier} static func withTemporaryBitSet( wordCount: Int, run body: (inout Self) throws -> R ) rethrows -> R { @@ -94,7 +102,8 @@ extension _UnsafeBitSet { } @inline(never) - public static func _withTemporaryBitSet( + @usableFromInline + internal static func _withTemporaryBitSet( wordCount: Int, run body: (inout Self) throws -> Void ) rethrows { @@ -138,33 +147,34 @@ extension _UnsafeBitSet { extension _UnsafeBitSet { @_effects(readnone) - @inline(__always) - public static func wordCount(forCapacity capacity: UInt) -> Int { + @inlinable @inline(__always) + ${modifier} static func wordCount(forCapacity capacity: UInt) -> Int { _Word.wordCount(forBitCount: capacity) } - public var capacity: UInt { - @inline(__always) - get { - UInt(wordCount &* _Word.capacity) - } + @inlinable @inline(__always) + ${modifier} var capacity: UInt { + UInt(wordCount &* _Word.capacity) } - @inline(__always) + @inlinable @inline(__always) internal func isWithinBounds(_ element: UInt) -> Bool { element < capacity } + @_effects(releasenone) @inline(__always) - public func contains(_ element: UInt) -> Bool { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func contains(_ element: UInt) -> Bool { let (word, bit) = Index(element).split guard word < wordCount else { return false } return _words[word].contains(bit) } @_effects(releasenone) + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } @discardableResult - public mutating func insert(_ element: UInt) -> Bool { + ${modifier} mutating func insert(_ element: UInt) -> Bool { ensureMutable() assert(isWithinBounds(element)) let index = Index(element) @@ -172,8 +182,9 @@ extension _UnsafeBitSet { } @_effects(releasenone) + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } @discardableResult - public mutating func remove(_ element: UInt) -> Bool { + ${modifier} mutating func remove(_ element: UInt) -> Bool { ensureMutable() let index = Index(element) if index.word >= _words.count { return false } @@ -181,7 +192,8 @@ extension _UnsafeBitSet { } @_effects(releasenone) - public mutating func update(_ member: UInt, to newValue: Bool) -> Bool { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} mutating func update(_ member: UInt, to newValue: Bool) -> Bool { ensureMutable() let (w, b) = Index(member).split _mutableWords[w].update(b, to: newValue) @@ -189,7 +201,8 @@ extension _UnsafeBitSet { } @_effects(releasenone) - public mutating func insertAll(upTo max: UInt) { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} mutating func insertAll(upTo max: UInt) { assert(max <= capacity) guard max > 0 else { return } let (w, b) = Index(max).split @@ -202,46 +215,50 @@ extension _UnsafeBitSet { } @_alwaysEmitIntoClient + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } @inline(__always) @discardableResult - public mutating func insert(_ element: Int) -> Bool { + ${modifier} mutating func insert(_ element: Int) -> Bool { precondition(element >= 0) return insert(UInt(bitPattern: element)) } @_alwaysEmitIntoClient + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } @inline(__always) @discardableResult - public mutating func remove(_ element: Int) -> Bool { + ${modifier} mutating func remove(_ element: Int) -> Bool { guard element >= 0 else { return false } return remove(UInt(bitPattern: element)) } @_alwaysEmitIntoClient + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } @inline(__always) - public mutating func insertAll(upTo max: Int) { + ${modifier} mutating func insertAll(upTo max: Int) { precondition(max >= 0) return insertAll(upTo: UInt(bitPattern: max)) } } extension _UnsafeBitSet: Sequence { - public typealias Element = UInt + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} typealias Element = UInt @inlinable @inline(__always) - public var underestimatedCount: Int { + ${modifier} var underestimatedCount: Int { count // FIXME: really? } @inlinable @inline(__always) - public func makeIterator() -> Iterator { + ${modifier} func makeIterator() -> Iterator { return Iterator(self) } - @frozen - public struct Iterator: IteratorProtocol { + ${"@frozen" if modifier == "public" else "@usableFromInline @frozen"} + ${modifier} struct Iterator: IteratorProtocol { @usableFromInline internal let _bitset: _UnsafeBitSet @@ -259,7 +276,8 @@ extension _UnsafeBitSet: Sequence { } @_effects(releasenone) - public mutating func next() -> UInt? { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} mutating func next() -> UInt? { if let bit = _word.next() { return Index(word: _index, bit: bit).value } @@ -278,35 +296,36 @@ extension _UnsafeBitSet: Sequence { extension _UnsafeBitSet: BidirectionalCollection { @inlinable @inline(__always) - public var count: Int { + ${modifier} var count: Int { _words.reduce(0) { $0 + $1.count } } @inlinable @inline(__always) - public var isEmpty: Bool { + ${modifier} var isEmpty: Bool { _words.firstIndex(where: { !$0.isEmpty }) == nil } @inlinable - public var startIndex: Index { + ${modifier} var startIndex: Index { let word = _words.firstIndex { !$0.isEmpty } guard let word = word else { return endIndex } return Index(word: word, bit: _words[word].firstMember!) } @inlinable - public var endIndex: Index { + ${modifier} var endIndex: Index { Index(word: wordCount, bit: 0) } @inlinable - public subscript(position: Index) -> UInt { + ${modifier} subscript(position: Index) -> UInt { position.value } @_effects(releasenone) - public func index(after index: Index) -> Index { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func index(after index: Index) -> Index { precondition(index < endIndex, "Index out of bounds") var word = index.word var w = _words[word] @@ -322,7 +341,8 @@ extension _UnsafeBitSet: BidirectionalCollection { } @_effects(releasenone) - public func index(before index: Index) -> Index { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func index(before index: Index) -> Index { precondition(index <= endIndex, "Index out of bounds") var word = index.word var w: _Word @@ -341,7 +361,8 @@ extension _UnsafeBitSet: BidirectionalCollection { } @_effects(releasenone) - public func distance(from start: Index, to end: Index) -> Int { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func distance(from start: Index, to end: Index) -> Int { precondition(start <= endIndex && end <= endIndex, "Index out of bounds") let isNegative = end < start let (start, end) = (Swift.min(start, end), Swift.max(start, end)) @@ -372,7 +393,8 @@ extension _UnsafeBitSet: BidirectionalCollection { } @_effects(releasenone) - public func index(_ i: Index, offsetBy distance: Int) -> Index { + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func index(_ i: Index, offsetBy distance: Int) -> Index { precondition(i <= endIndex, "Index out of bounds") precondition(i == endIndex || contains(i.value), "Invalid index") guard distance != 0 else { return i } @@ -412,7 +434,8 @@ extension _UnsafeBitSet: BidirectionalCollection { } @_effects(releasenone) - public func index( + ${"@usableFromInline" if modifier != "public" else "//@usableFromInline" } + ${modifier} func index( _ i: Index, offsetBy distance: Int, limitedBy limit: Index ) -> Index? { precondition(i <= endIndex && limit <= endIndex, "Index out of bounds") @@ -464,3 +487,5 @@ extension _UnsafeBitSet: BidirectionalCollection { return nil } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+Index.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+Index.swift new file mode 100644 index 000000000..529fe7e5f --- /dev/null +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+Index.swift @@ -0,0 +1,184 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension _UnsafeBitSet { + @frozen @usableFromInline + internal struct Index: Comparable, Hashable { + @usableFromInline + internal typealias _Word = _UnsafeBitSet._Word + + @usableFromInline + internal var value: UInt + + @inlinable + internal init(_ value: UInt) { + self.value = value + } + + @inlinable + internal init(_ value: Int) { + self.value = UInt(value) + } + + @inlinable + internal init(word: Int, bit: UInt) { + assert(word >= 0 && word <= Int.max / _Word.capacity) + assert(bit < _Word.capacity) + self.value = UInt(word &* _Word.capacity) &+ bit + } + } +} + +extension _UnsafeBitSet.Index { + @inlinable + internal var word: Int { + // Note: We perform on UInts to get faster unsigned math (shifts). + Int(truncatingIfNeeded: value / UInt(bitPattern: _Word.capacity)) + } + + @inlinable + internal var bit: UInt { + // Note: We perform on UInts to get faster unsigned math (masking). + value % UInt(bitPattern: _Word.capacity) + } + + @inlinable + internal var split: (word: Int, bit: UInt) { + (word, bit) + } + + @inlinable + internal var endSplit: (word: Int, bit: UInt) { + let w = word + let b = bit + if w > 0, b == 0 { return (w &- 1, UInt(_Word.capacity)) } + return (w, b) + } + + @inlinable + internal static func ==(left: Self, right: Self) -> Bool { + left.value == right.value + } + + @inlinable + internal static func <(left: Self, right: Self) -> Bool { + left.value < right.value + } + + @inlinable + internal func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + @inlinable + internal func _successor() -> Self { + Self(value + 1) + } + + @inlinable + internal func _predecessor() -> Self { + Self(value - 1) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension _UnsafeBitSet { + @frozen + public struct Index: Comparable, Hashable { + @usableFromInline + internal typealias _Word = _UnsafeBitSet._Word + + + public var value: UInt + + @inlinable + public init(_ value: UInt) { + self.value = value + } + + @inlinable + public init(_ value: Int) { + self.value = UInt(value) + } + + @inlinable + public init(word: Int, bit: UInt) { + assert(word >= 0 && word <= Int.max / _Word.capacity) + assert(bit < _Word.capacity) + self.value = UInt(word &* _Word.capacity) &+ bit + } + } +} + +extension _UnsafeBitSet.Index { + @inlinable + public var word: Int { + // Note: We perform on UInts to get faster unsigned math (shifts). + Int(truncatingIfNeeded: value / UInt(bitPattern: _Word.capacity)) + } + + @inlinable + public var bit: UInt { + // Note: We perform on UInts to get faster unsigned math (masking). + value % UInt(bitPattern: _Word.capacity) + } + + @inlinable + public var split: (word: Int, bit: UInt) { + (word, bit) + } + + @inlinable + public var endSplit: (word: Int, bit: UInt) { + let w = word + let b = bit + if w > 0, b == 0 { return (w &- 1, UInt(_Word.capacity)) } + return (w, b) + } + + @inlinable + public static func ==(left: Self, right: Self) -> Bool { + left.value == right.value + } + + @inlinable + public static func <(left: Self, right: Self) -> Bool { + left.value < right.value + } + + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + @inlinable + internal func _successor() -> Self { + Self(value + 1) + } + + @inlinable + internal func _predecessor() -> Self { + Self(value - 1) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+_Word.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+_Word.swift new file mode 100644 index 000000000..bba3891dd --- /dev/null +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet+_Word.swift @@ -0,0 +1,643 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension _UnsafeBitSet { + @frozen @usableFromInline + internal struct _Word { + @usableFromInline + internal var value: UInt + + @inlinable + @inline(__always) + internal init(_ value: UInt) { + self.value = value + } + + @inlinable + @inline(__always) + internal init(upTo bit: UInt) { + assert(bit <= _Word.capacity) + self.init((1 << bit) &- 1) + } + + @inlinable + @inline(__always) + internal init(from start: UInt, to end: UInt) { + assert(start <= end && end <= _Word.capacity) + self = Self(upTo: end).symmetricDifference(Self(upTo: start)) + } + } +} + +extension _UnsafeBitSet._Word: CustomStringConvertible { + @usableFromInline + internal var description: String { + String(value, radix: 16) + } +} + +extension _UnsafeBitSet._Word { + /// Returns the `n`th member in `self`. + /// + /// - Parameter n: The offset of the element to retrieve. This value is + /// decremented by the number of items found in this `self` towards the + /// value we're looking for. (If the function returns non-nil, then `n` + /// is set to `0` on return.) + /// - Returns: If this word contains enough members to satisfy the request, + /// then this function returns the member found. Otherwise it returns nil. + @inline(never) + internal func nthElement(_ n: inout UInt) -> UInt? { + let c = UInt(bitPattern: count) + guard n < c else { + n &-= c + return nil + } + let m = Int(bitPattern: n) + n = 0 + return value._bit(ranked: m)! + } + + @inline(never) + internal func nthElementFromEnd(_ n: inout UInt) -> UInt? { + let c = UInt(bitPattern: count) + guard n < c else { + n &-= c + return nil + } + let m = Int(bitPattern: c &- 1 &- n) + n = 0 + return value._bit(ranked: m)! + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal static func wordCount(forBitCount count: UInt) -> Int { + // Note: We perform on UInts to get faster unsigned math (shifts). + let width = UInt(bitPattern: Self.capacity) + return Int(bitPattern: (count + width - 1) / width) + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal static var capacity: Int { + return UInt.bitWidth + } + + @inlinable + @inline(__always) + internal var count: Int { + value.nonzeroBitCount + } + + @inlinable + @inline(__always) + internal var isEmpty: Bool { + value == 0 + } + + @inlinable + @inline(__always) + internal var isFull: Bool { + value == UInt.max + } + + @inlinable + @inline(__always) + internal func contains(_ bit: UInt) -> Bool { + assert(bit >= 0 && bit < UInt.bitWidth) + return value & (1 &<< bit) != 0 + } + + @inlinable + @inline(__always) + internal var firstMember: UInt? { + value._lastSetBit + } + + @inlinable + @inline(__always) + internal var lastMember: UInt? { + value._firstSetBit + } + + @inlinable + @inline(__always) + @discardableResult + internal mutating func insert(_ bit: UInt) -> Bool { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + let inserted = value & mask == 0 + value |= mask + return inserted + } + + @inlinable + @inline(__always) + @discardableResult + internal mutating func remove(_ bit: UInt) -> Bool { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + let removed = (value & mask) != 0 + value &= ~mask + return removed + } + + @inlinable + @inline(__always) + internal mutating func update(_ bit: UInt, to value: Bool) { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + if value { + self.value |= mask + } else { + self.value &= ~mask + } + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal mutating func insertAll(upTo bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask: UInt = (1 as UInt &<< bit) &- 1 + value |= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(upTo bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask = UInt.max &<< bit + value &= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(through bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + var mask = UInt.max &<< bit + mask &= mask &- 1 // Clear lowest nonzero bit. + value &= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(from bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask: UInt = (1 as UInt &<< bit) &- 1 + value &= mask + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal static var empty: Self { + Self(0) + } + + @inline(__always) + internal static var allBits: Self { + Self(UInt.max) + } +} + +// _Word implements Sequence by using a copy of itself as its Iterator. +// Iteration with `next()` destroys the word's value; however, this won't cause +// problems in normal use, because `next()` is usually called on a separate +// iterator, not the original word. +extension _UnsafeBitSet._Word: Sequence, IteratorProtocol { + @inlinable @inline(__always) + internal var underestimatedCount: Int { + count + } + + /// Return the index of the lowest set bit in this word, + /// and also destructively clear it. + @inlinable + internal mutating func next() -> UInt? { + guard value != 0 else { return nil } + let bit = UInt(truncatingIfNeeded: value.trailingZeroBitCount) + value &= value &- 1 // Clear lowest nonzero bit. + return bit + } +} + +extension _UnsafeBitSet._Word: Equatable { + @inlinable + internal static func ==(left: Self, right: Self) -> Bool { + left.value == right.value + } +} + +extension _UnsafeBitSet._Word: Hashable { + @inlinable + internal func hash(into hasher: inout Hasher) { + hasher.combine(value) + } +} + +extension _UnsafeBitSet._Word { + @inlinable @inline(__always) + internal func complement() -> Self { + Self(~self.value) + } + + @inlinable @inline(__always) + internal mutating func formComplement() { + self.value = ~self.value + } + + @inlinable @inline(__always) + internal func union(_ other: Self) -> Self { + Self(self.value | other.value) + } + + @inlinable @inline(__always) + internal mutating func formUnion(_ other: Self) { + self.value |= other.value + } + + @inlinable @inline(__always) + internal func intersection(_ other: Self) -> Self { + Self(self.value & other.value) + } + + @inlinable @inline(__always) + internal mutating func formIntersection(_ other: Self) { + self.value &= other.value + } + + @inlinable @inline(__always) + internal func symmetricDifference(_ other: Self) -> Self { + Self(self.value ^ other.value) + } + + @inlinable @inline(__always) + internal mutating func formSymmetricDifference(_ other: Self) { + self.value ^= other.value + } + + @inlinable @inline(__always) + internal func subtracting(_ other: Self) -> Self { + Self(self.value & ~other.value) + } + + @inlinable @inline(__always) + internal mutating func subtract(_ other: Self) { + self.value &= ~other.value + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal func shiftedDown(by shift: UInt) -> Self { + assert(shift >= 0 && shift < Self.capacity) + return Self(self.value &>> shift) + } + + @inlinable + @inline(__always) + internal func shiftedUp(by shift: UInt) -> Self { + assert(shift >= 0 && shift < Self.capacity) + return Self(self.value &<< shift) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension _UnsafeBitSet { + @frozen + public struct _Word { + + public var value: UInt + + @inlinable + @inline(__always) + public init(_ value: UInt) { + self.value = value + } + + @inlinable + @inline(__always) + public init(upTo bit: UInt) { + assert(bit <= _Word.capacity) + self.init((1 << bit) &- 1) + } + + @inlinable + @inline(__always) + public init(from start: UInt, to end: UInt) { + assert(start <= end && end <= _Word.capacity) + self = Self(upTo: end).symmetricDifference(Self(upTo: start)) + } + } +} + +extension _UnsafeBitSet._Word: CustomStringConvertible { + //@usableFromInline + public var description: String { + String(value, radix: 16) + } +} + +extension _UnsafeBitSet._Word { + /// Returns the `n`th member in `self`. + /// + /// - Parameter n: The offset of the element to retrieve. This value is + /// decremented by the number of items found in this `self` towards the + /// value we're looking for. (If the function returns non-nil, then `n` + /// is set to `0` on return.) + /// - Returns: If this word contains enough members to satisfy the request, + /// then this function returns the member found. Otherwise it returns nil. + @inline(never) + internal func nthElement(_ n: inout UInt) -> UInt? { + let c = UInt(bitPattern: count) + guard n < c else { + n &-= c + return nil + } + let m = Int(bitPattern: n) + n = 0 + return value._bit(ranked: m)! + } + + @inline(never) + internal func nthElementFromEnd(_ n: inout UInt) -> UInt? { + let c = UInt(bitPattern: count) + guard n < c else { + n &-= c + return nil + } + let m = Int(bitPattern: c &- 1 &- n) + n = 0 + return value._bit(ranked: m)! + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + public static func wordCount(forBitCount count: UInt) -> Int { + // Note: We perform on UInts to get faster unsigned math (shifts). + let width = UInt(bitPattern: Self.capacity) + return Int(bitPattern: (count + width - 1) / width) + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + public static var capacity: Int { + return UInt.bitWidth + } + + @inlinable + @inline(__always) + public var count: Int { + value.nonzeroBitCount + } + + @inlinable + @inline(__always) + public var isEmpty: Bool { + value == 0 + } + + @inlinable + @inline(__always) + public var isFull: Bool { + value == UInt.max + } + + @inlinable + @inline(__always) + public func contains(_ bit: UInt) -> Bool { + assert(bit >= 0 && bit < UInt.bitWidth) + return value & (1 &<< bit) != 0 + } + + @inlinable + @inline(__always) + public var firstMember: UInt? { + value._lastSetBit + } + + @inlinable + @inline(__always) + public var lastMember: UInt? { + value._firstSetBit + } + + @inlinable + @inline(__always) + @discardableResult + public mutating func insert(_ bit: UInt) -> Bool { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + let inserted = value & mask == 0 + value |= mask + return inserted + } + + @inlinable + @inline(__always) + @discardableResult + public mutating func remove(_ bit: UInt) -> Bool { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + let removed = (value & mask) != 0 + value &= ~mask + return removed + } + + @inlinable + @inline(__always) + public mutating func update(_ bit: UInt, to value: Bool) { + assert(bit < UInt.bitWidth) + let mask: UInt = 1 &<< bit + if value { + self.value |= mask + } else { + self.value &= ~mask + } + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + internal mutating func insertAll(upTo bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask: UInt = (1 as UInt &<< bit) &- 1 + value |= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(upTo bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask = UInt.max &<< bit + value &= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(through bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + var mask = UInt.max &<< bit + mask &= mask &- 1 // Clear lowest nonzero bit. + value &= mask + } + + @inlinable + @inline(__always) + internal mutating func removeAll(from bit: UInt) { + assert(bit >= 0 && bit < Self.capacity) + let mask: UInt = (1 as UInt &<< bit) &- 1 + value &= mask + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + public static var empty: Self { + Self(0) + } + + @inline(__always) + public static var allBits: Self { + Self(UInt.max) + } +} + +// _Word implements Sequence by using a copy of itself as its Iterator. +// Iteration with `next()` destroys the word's value; however, this won't cause +// problems in normal use, because `next()` is usually called on a separate +// iterator, not the original word. +extension _UnsafeBitSet._Word: Sequence, IteratorProtocol { + @inlinable @inline(__always) + public var underestimatedCount: Int { + count + } + + /// Return the index of the lowest set bit in this word, + /// and also destructively clear it. + @inlinable + public mutating func next() -> UInt? { + guard value != 0 else { return nil } + let bit = UInt(truncatingIfNeeded: value.trailingZeroBitCount) + value &= value &- 1 // Clear lowest nonzero bit. + return bit + } +} + +extension _UnsafeBitSet._Word: Equatable { + @inlinable + public static func ==(left: Self, right: Self) -> Bool { + left.value == right.value + } +} + +extension _UnsafeBitSet._Word: Hashable { + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } +} + +extension _UnsafeBitSet._Word { + @inlinable @inline(__always) + public func complement() -> Self { + Self(~self.value) + } + + @inlinable @inline(__always) + public mutating func formComplement() { + self.value = ~self.value + } + + @inlinable @inline(__always) + public func union(_ other: Self) -> Self { + Self(self.value | other.value) + } + + @inlinable @inline(__always) + public mutating func formUnion(_ other: Self) { + self.value |= other.value + } + + @inlinable @inline(__always) + public func intersection(_ other: Self) -> Self { + Self(self.value & other.value) + } + + @inlinable @inline(__always) + public mutating func formIntersection(_ other: Self) { + self.value &= other.value + } + + @inlinable @inline(__always) + public func symmetricDifference(_ other: Self) -> Self { + Self(self.value ^ other.value) + } + + @inlinable @inline(__always) + public mutating func formSymmetricDifference(_ other: Self) { + self.value ^= other.value + } + + @inlinable @inline(__always) + public func subtracting(_ other: Self) -> Self { + Self(self.value & ~other.value) + } + + @inlinable @inline(__always) + public mutating func subtract(_ other: Self) { + self.value &= ~other.value + } +} + +extension _UnsafeBitSet._Word { + @inlinable + @inline(__always) + public func shiftedDown(by shift: UInt) -> Self { + assert(shift >= 0 && shift < Self.capacity) + return Self(self.value &>> shift) + } + + @inlinable + @inline(__always) + public func shiftedUp(by shift: UInt) -> Self { + assert(shift >= 0 && shift < Self.capacity) + return Self(self.value &<< shift) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet.swift b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet.swift new file mode 100644 index 000000000..eaf6dee21 --- /dev/null +++ b/Sources/_CollectionsUtilities/UnsafeBitSet/autogenerated/_UnsafeBitSet.swift @@ -0,0 +1,968 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +/// An unsafe-unowned bitset view over `UInt` storage, providing bit set +/// primitives. +@frozen @usableFromInline +internal struct _UnsafeBitSet { + /// An unsafe-unowned storage view. + @usableFromInline + internal let _words: UnsafeBufferPointer<_Word> + +#if DEBUG + /// True when this handle does not support table mutations. + /// (This is only checked in debug builds.) + @usableFromInline + internal let _mutable: Bool +#endif + + @inlinable + @inline(__always) + internal func ensureMutable() { +#if DEBUG + assert(_mutable) +#endif + } + + @inlinable + @inline(__always) + internal var _mutableWords: UnsafeMutableBufferPointer<_Word> { + ensureMutable() + return UnsafeMutableBufferPointer(mutating: _words) + } + + @inlinable + @inline(__always) + internal init( + words: UnsafeBufferPointer<_Word>, + mutable: Bool + ) { + assert(words.baseAddress != nil) + self._words = words +#if DEBUG + self._mutable = mutable +#endif + } + + @inlinable + @inline(__always) + internal init( + words: UnsafeMutableBufferPointer<_Word>, + mutable: Bool + ) { + self.init(words: UnsafeBufferPointer(words), mutable: mutable) + } +} + +extension _UnsafeBitSet { + @inlinable + @inline(__always) + internal var wordCount: Int { + _words.count + } +} + +extension _UnsafeBitSet { + @inlinable + @inline(__always) + internal static func withTemporaryBitSet( + capacity: Int, + run body: (inout _UnsafeBitSet) throws -> R + ) rethrows -> R { + let wordCount = _UnsafeBitSet.wordCount(forCapacity: UInt(capacity)) + return try withTemporaryBitSet(wordCount: wordCount, run: body) + } + + @inlinable + @inline(__always) + internal static func withTemporaryBitSet( + wordCount: Int, + run body: (inout Self) throws -> R + ) rethrows -> R { + var result: R? + try _withTemporaryBitSet(wordCount: wordCount) { bitset in + result = try body(&bitset) + } + return result! + } + + @inline(never) + @usableFromInline + internal static func _withTemporaryBitSet( + wordCount: Int, + run body: (inout Self) throws -> Void + ) rethrows { + try _withTemporaryUninitializedBitSet(wordCount: wordCount) { handle in + handle._mutableWords.initialize(repeating: .empty) + try body(&handle) + } + } + + internal static func _withTemporaryUninitializedBitSet( + wordCount: Int, + run body: (inout Self) throws -> Void + ) rethrows { + assert(wordCount >= 0) +#if compiler(>=5.6) + return try withUnsafeTemporaryAllocation( + of: _Word.self, capacity: wordCount + ) { words in + var bitset = Self(words: words, mutable: true) + return try body(&bitset) + } +#else + if wordCount <= 2 { + var buffer: (_Word, _Word) = (.empty, .empty) + return try withUnsafeMutablePointer(to: &buffer) { p in + // Homogeneous tuples are layout-compatible with their component type. + let start = UnsafeMutableRawPointer(p) + .assumingMemoryBound(to: _Word.self) + let words = UnsafeMutableBufferPointer(start: start, count: wordCount) + var bitset = Self(words: words, mutable: true) + return try body(&bitset) + } + } + let words = UnsafeMutableBufferPointer<_Word>.allocate(capacity: wordCount) + defer { words.deallocate() } + var bitset = Self(words: words, mutable: true) + return try body(&bitset) +#endif + } +} + +extension _UnsafeBitSet { + @_effects(readnone) + @inlinable @inline(__always) + internal static func wordCount(forCapacity capacity: UInt) -> Int { + _Word.wordCount(forBitCount: capacity) + } + + @inlinable @inline(__always) + internal var capacity: UInt { + UInt(wordCount &* _Word.capacity) + } + + @inlinable @inline(__always) + internal func isWithinBounds(_ element: UInt) -> Bool { + element < capacity + } + + @_effects(releasenone) + @inline(__always) + @usableFromInline + internal func contains(_ element: UInt) -> Bool { + let (word, bit) = Index(element).split + guard word < wordCount else { return false } + return _words[word].contains(bit) + } + + @_effects(releasenone) + @usableFromInline + @discardableResult + internal mutating func insert(_ element: UInt) -> Bool { + ensureMutable() + assert(isWithinBounds(element)) + let index = Index(element) + return _mutableWords[index.word].insert(index.bit) + } + + @_effects(releasenone) + @usableFromInline + @discardableResult + internal mutating func remove(_ element: UInt) -> Bool { + ensureMutable() + let index = Index(element) + if index.word >= _words.count { return false } + return _mutableWords[index.word].remove(index.bit) + } + + @_effects(releasenone) + @usableFromInline + internal mutating func update(_ member: UInt, to newValue: Bool) -> Bool { + ensureMutable() + let (w, b) = Index(member).split + _mutableWords[w].update(b, to: newValue) + return w == _words.count &- 1 + } + + @_effects(releasenone) + @usableFromInline + internal mutating func insertAll(upTo max: UInt) { + assert(max <= capacity) + guard max > 0 else { return } + let (w, b) = Index(max).split + for i in 0 ..< w { + _mutableWords[i] = .allBits + } + if b > 0 { + _mutableWords[w].insertAll(upTo: b) + } + } + + @_alwaysEmitIntoClient + @usableFromInline + @inline(__always) + @discardableResult + internal mutating func insert(_ element: Int) -> Bool { + precondition(element >= 0) + return insert(UInt(bitPattern: element)) + } + + @_alwaysEmitIntoClient + @usableFromInline + @inline(__always) + @discardableResult + internal mutating func remove(_ element: Int) -> Bool { + guard element >= 0 else { return false } + return remove(UInt(bitPattern: element)) + } + + @_alwaysEmitIntoClient + @usableFromInline + @inline(__always) + internal mutating func insertAll(upTo max: Int) { + precondition(max >= 0) + return insertAll(upTo: UInt(bitPattern: max)) + } +} + +extension _UnsafeBitSet: Sequence { + @usableFromInline + internal typealias Element = UInt + + @inlinable + @inline(__always) + internal var underestimatedCount: Int { + count // FIXME: really? + } + + @inlinable + @inline(__always) + internal func makeIterator() -> Iterator { + return Iterator(self) + } + + @usableFromInline @frozen + internal struct Iterator: IteratorProtocol { + @usableFromInline + internal let _bitset: _UnsafeBitSet + + @usableFromInline + internal var _index: Int + + @usableFromInline + internal var _word: _Word + + @inlinable + internal init(_ bitset: _UnsafeBitSet) { + self._bitset = bitset + self._index = 0 + self._word = bitset.wordCount > 0 ? bitset._words[0] : .empty + } + + @_effects(releasenone) + @usableFromInline + internal mutating func next() -> UInt? { + if let bit = _word.next() { + return Index(word: _index, bit: bit).value + } + while (_index + 1) < _bitset.wordCount { + _index += 1 + _word = _bitset._words[_index] + if let bit = _word.next() { + return Index(word: _index, bit: bit).value + } + } + return nil + } + } +} + +extension _UnsafeBitSet: BidirectionalCollection { + @inlinable + @inline(__always) + internal var count: Int { + _words.reduce(0) { $0 + $1.count } + } + + @inlinable + @inline(__always) + internal var isEmpty: Bool { + _words.firstIndex(where: { !$0.isEmpty }) == nil + } + + @inlinable + internal var startIndex: Index { + let word = _words.firstIndex { !$0.isEmpty } + guard let word = word else { return endIndex } + return Index(word: word, bit: _words[word].firstMember!) + } + + @inlinable + internal var endIndex: Index { + Index(word: wordCount, bit: 0) + } + + @inlinable + internal subscript(position: Index) -> UInt { + position.value + } + + @_effects(releasenone) + @usableFromInline + internal func index(after index: Index) -> Index { + precondition(index < endIndex, "Index out of bounds") + var word = index.word + var w = _words[word] + w.removeAll(through: index.bit) + while w.isEmpty { + word += 1 + guard word < wordCount else { + return Index(word: wordCount, bit: 0) + } + w = _words[word] + } + return Index(word: word, bit: w.firstMember!) + } + + @_effects(releasenone) + @usableFromInline + internal func index(before index: Index) -> Index { + precondition(index <= endIndex, "Index out of bounds") + var word = index.word + var w: _Word + if index.bit > 0 { + w = _words[word] + w.removeAll(from: index.bit) + } else { + w = .empty + } + while w.isEmpty { + word -= 1 + precondition(word >= 0, "Can't advance below startIndex") + w = _words[word] + } + return Index(word: word, bit: w.lastMember!) + } + + @_effects(releasenone) + @usableFromInline + internal func distance(from start: Index, to end: Index) -> Int { + precondition(start <= endIndex && end <= endIndex, "Index out of bounds") + let isNegative = end < start + let (start, end) = (Swift.min(start, end), Swift.max(start, end)) + + let (w1, b1) = start.split + let (w2, b2) = end.split + + if w1 == w2 { + guard w1 < wordCount else { return 0 } + let mask = _Word(from: b1, to: b2) + let c = _words[w1].intersection(mask).count + return isNegative ? -c : c + } + + var c = 0 + var w = w1 + guard w < wordCount else { return 0 } + + c &+= _words[w].subtracting(_Word(upTo: b1)).count + w &+= 1 + while w < w2 { + c &+= _words[w].count + w &+= 1 + } + guard w < wordCount else { return isNegative ? -c : c } + c &+= _words[w].intersection(_Word(upTo: b2)).count + return isNegative ? -c : c + } + + @_effects(releasenone) + @usableFromInline + internal func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i <= endIndex, "Index out of bounds") + precondition(i == endIndex || contains(i.value), "Invalid index") + guard distance != 0 else { return i } + var remaining = distance.magnitude + if distance > 0 { + var (w, b) = i.split + precondition(w < wordCount, "Index out of bounds") + if let v = _words[w].subtracting(_Word(upTo: b)).nthElement(&remaining) { + return Index(word: w, bit: v) + } + while true { + w &+= 1 + guard w < wordCount else { break } + if let v = _words[w].nthElement(&remaining) { + return Index(word: w, bit: v) + } + } + precondition(remaining == 0, "Index out of bounds") + return endIndex + } + + // distance < 0 + remaining -= 1 + var (w, b) = i.endSplit + if w < wordCount { + if let v = _words[w].intersection(_Word(upTo: b)).nthElementFromEnd(&remaining) { + return Index(word: w, bit: v) + } + } + while true { + precondition(w > 0, "Index out of bounds") + w &-= 1 + if let v = _words[w].nthElementFromEnd(&remaining) { + return Index(word: w, bit: v) + } + } + } + + @_effects(releasenone) + @usableFromInline + internal func index( + _ i: Index, offsetBy distance: Int, limitedBy limit: Index + ) -> Index? { + precondition(i <= endIndex && limit <= endIndex, "Index out of bounds") + precondition(i == endIndex || contains(i.value), "Invalid index") + guard distance != 0 else { return i } + var remaining = distance.magnitude + if distance > 0 { + guard i <= limit else { + return self.index(i, offsetBy: distance) + } + var (w, b) = i.split + if w < wordCount, + let v = _words[w].subtracting(_Word(upTo: b)).nthElement(&remaining) + { + let r = Index(word: w, bit: v) + return r <= limit ? r : nil + } + let maxWord = Swift.min(wordCount - 1, limit.word) + while w < maxWord { + w &+= 1 + if let v = _words[w].nthElement(&remaining) { + let r = Index(word: w, bit: v) + return r <= limit ? r : nil + } + } + return remaining == 0 && limit == endIndex ? endIndex : nil + } + + // distance < 0 + guard i >= limit else { + return self.index(i, offsetBy: distance) + } + remaining &-= 1 + var (w, b) = i.endSplit + if w < wordCount { + if let v = _words[w].intersection(_Word(upTo: b)).nthElementFromEnd(&remaining) { + let r = Index(word: w, bit: v) + return r >= limit ? r : nil + } + } + let minWord = limit.word + while w > minWord { + w &-= 1 + if let v = _words[w].nthElementFromEnd(&remaining) { + let r = Index(word: w, bit: v) + return r >= limit ? r : nil + } + } + return nil + } +} +#else // !COLLECTIONS_SINGLE_MODULE +/// An unsafe-unowned bitset view over `UInt` storage, providing bit set +/// primitives. +@frozen +public struct _UnsafeBitSet { + /// An unsafe-unowned storage view. + + public let _words: UnsafeBufferPointer<_Word> + +#if DEBUG + /// True when this handle does not support table mutations. + /// (This is only checked in debug builds.) + @usableFromInline + internal let _mutable: Bool +#endif + + @inlinable + @inline(__always) + public func ensureMutable() { +#if DEBUG + assert(_mutable) +#endif + } + + @inlinable + @inline(__always) + public var _mutableWords: UnsafeMutableBufferPointer<_Word> { + ensureMutable() + return UnsafeMutableBufferPointer(mutating: _words) + } + + @inlinable + @inline(__always) + public init( + words: UnsafeBufferPointer<_Word>, + mutable: Bool + ) { + assert(words.baseAddress != nil) + self._words = words +#if DEBUG + self._mutable = mutable +#endif + } + + @inlinable + @inline(__always) + public init( + words: UnsafeMutableBufferPointer<_Word>, + mutable: Bool + ) { + self.init(words: UnsafeBufferPointer(words), mutable: mutable) + } +} + +extension _UnsafeBitSet { + @inlinable + @inline(__always) + public var wordCount: Int { + _words.count + } +} + +extension _UnsafeBitSet { + @inlinable + @inline(__always) + public static func withTemporaryBitSet( + capacity: Int, + run body: (inout _UnsafeBitSet) throws -> R + ) rethrows -> R { + let wordCount = _UnsafeBitSet.wordCount(forCapacity: UInt(capacity)) + return try withTemporaryBitSet(wordCount: wordCount, run: body) + } + + @inlinable + @inline(__always) + public static func withTemporaryBitSet( + wordCount: Int, + run body: (inout Self) throws -> R + ) rethrows -> R { + var result: R? + try _withTemporaryBitSet(wordCount: wordCount) { bitset in + result = try body(&bitset) + } + return result! + } + + @inline(never) + @usableFromInline + internal static func _withTemporaryBitSet( + wordCount: Int, + run body: (inout Self) throws -> Void + ) rethrows { + try _withTemporaryUninitializedBitSet(wordCount: wordCount) { handle in + handle._mutableWords.initialize(repeating: .empty) + try body(&handle) + } + } + + internal static func _withTemporaryUninitializedBitSet( + wordCount: Int, + run body: (inout Self) throws -> Void + ) rethrows { + assert(wordCount >= 0) +#if compiler(>=5.6) + return try withUnsafeTemporaryAllocation( + of: _Word.self, capacity: wordCount + ) { words in + var bitset = Self(words: words, mutable: true) + return try body(&bitset) + } +#else + if wordCount <= 2 { + var buffer: (_Word, _Word) = (.empty, .empty) + return try withUnsafeMutablePointer(to: &buffer) { p in + // Homogeneous tuples are layout-compatible with their component type. + let start = UnsafeMutableRawPointer(p) + .assumingMemoryBound(to: _Word.self) + let words = UnsafeMutableBufferPointer(start: start, count: wordCount) + var bitset = Self(words: words, mutable: true) + return try body(&bitset) + } + } + let words = UnsafeMutableBufferPointer<_Word>.allocate(capacity: wordCount) + defer { words.deallocate() } + var bitset = Self(words: words, mutable: true) + return try body(&bitset) +#endif + } +} + +extension _UnsafeBitSet { + @_effects(readnone) + @inlinable @inline(__always) + public static func wordCount(forCapacity capacity: UInt) -> Int { + _Word.wordCount(forBitCount: capacity) + } + + @inlinable @inline(__always) + public var capacity: UInt { + UInt(wordCount &* _Word.capacity) + } + + @inlinable @inline(__always) + internal func isWithinBounds(_ element: UInt) -> Bool { + element < capacity + } + + @_effects(releasenone) + @inline(__always) + //@usableFromInline + public func contains(_ element: UInt) -> Bool { + let (word, bit) = Index(element).split + guard word < wordCount else { return false } + return _words[word].contains(bit) + } + + @_effects(releasenone) + //@usableFromInline + @discardableResult + public mutating func insert(_ element: UInt) -> Bool { + ensureMutable() + assert(isWithinBounds(element)) + let index = Index(element) + return _mutableWords[index.word].insert(index.bit) + } + + @_effects(releasenone) + //@usableFromInline + @discardableResult + public mutating func remove(_ element: UInt) -> Bool { + ensureMutable() + let index = Index(element) + if index.word >= _words.count { return false } + return _mutableWords[index.word].remove(index.bit) + } + + @_effects(releasenone) + //@usableFromInline + public mutating func update(_ member: UInt, to newValue: Bool) -> Bool { + ensureMutable() + let (w, b) = Index(member).split + _mutableWords[w].update(b, to: newValue) + return w == _words.count &- 1 + } + + @_effects(releasenone) + //@usableFromInline + public mutating func insertAll(upTo max: UInt) { + assert(max <= capacity) + guard max > 0 else { return } + let (w, b) = Index(max).split + for i in 0 ..< w { + _mutableWords[i] = .allBits + } + if b > 0 { + _mutableWords[w].insertAll(upTo: b) + } + } + + @_alwaysEmitIntoClient + //@usableFromInline + @inline(__always) + @discardableResult + public mutating func insert(_ element: Int) -> Bool { + precondition(element >= 0) + return insert(UInt(bitPattern: element)) + } + + @_alwaysEmitIntoClient + //@usableFromInline + @inline(__always) + @discardableResult + public mutating func remove(_ element: Int) -> Bool { + guard element >= 0 else { return false } + return remove(UInt(bitPattern: element)) + } + + @_alwaysEmitIntoClient + //@usableFromInline + @inline(__always) + public mutating func insertAll(upTo max: Int) { + precondition(max >= 0) + return insertAll(upTo: UInt(bitPattern: max)) + } +} + +extension _UnsafeBitSet: Sequence { + //@usableFromInline + public typealias Element = UInt + + @inlinable + @inline(__always) + public var underestimatedCount: Int { + count // FIXME: really? + } + + @inlinable + @inline(__always) + public func makeIterator() -> Iterator { + return Iterator(self) + } + + @frozen + public struct Iterator: IteratorProtocol { + @usableFromInline + internal let _bitset: _UnsafeBitSet + + @usableFromInline + internal var _index: Int + + @usableFromInline + internal var _word: _Word + + @inlinable + internal init(_ bitset: _UnsafeBitSet) { + self._bitset = bitset + self._index = 0 + self._word = bitset.wordCount > 0 ? bitset._words[0] : .empty + } + + @_effects(releasenone) + //@usableFromInline + public mutating func next() -> UInt? { + if let bit = _word.next() { + return Index(word: _index, bit: bit).value + } + while (_index + 1) < _bitset.wordCount { + _index += 1 + _word = _bitset._words[_index] + if let bit = _word.next() { + return Index(word: _index, bit: bit).value + } + } + return nil + } + } +} + +extension _UnsafeBitSet: BidirectionalCollection { + @inlinable + @inline(__always) + public var count: Int { + _words.reduce(0) { $0 + $1.count } + } + + @inlinable + @inline(__always) + public var isEmpty: Bool { + _words.firstIndex(where: { !$0.isEmpty }) == nil + } + + @inlinable + public var startIndex: Index { + let word = _words.firstIndex { !$0.isEmpty } + guard let word = word else { return endIndex } + return Index(word: word, bit: _words[word].firstMember!) + } + + @inlinable + public var endIndex: Index { + Index(word: wordCount, bit: 0) + } + + @inlinable + public subscript(position: Index) -> UInt { + position.value + } + + @_effects(releasenone) + //@usableFromInline + public func index(after index: Index) -> Index { + precondition(index < endIndex, "Index out of bounds") + var word = index.word + var w = _words[word] + w.removeAll(through: index.bit) + while w.isEmpty { + word += 1 + guard word < wordCount else { + return Index(word: wordCount, bit: 0) + } + w = _words[word] + } + return Index(word: word, bit: w.firstMember!) + } + + @_effects(releasenone) + //@usableFromInline + public func index(before index: Index) -> Index { + precondition(index <= endIndex, "Index out of bounds") + var word = index.word + var w: _Word + if index.bit > 0 { + w = _words[word] + w.removeAll(from: index.bit) + } else { + w = .empty + } + while w.isEmpty { + word -= 1 + precondition(word >= 0, "Can't advance below startIndex") + w = _words[word] + } + return Index(word: word, bit: w.lastMember!) + } + + @_effects(releasenone) + //@usableFromInline + public func distance(from start: Index, to end: Index) -> Int { + precondition(start <= endIndex && end <= endIndex, "Index out of bounds") + let isNegative = end < start + let (start, end) = (Swift.min(start, end), Swift.max(start, end)) + + let (w1, b1) = start.split + let (w2, b2) = end.split + + if w1 == w2 { + guard w1 < wordCount else { return 0 } + let mask = _Word(from: b1, to: b2) + let c = _words[w1].intersection(mask).count + return isNegative ? -c : c + } + + var c = 0 + var w = w1 + guard w < wordCount else { return 0 } + + c &+= _words[w].subtracting(_Word(upTo: b1)).count + w &+= 1 + while w < w2 { + c &+= _words[w].count + w &+= 1 + } + guard w < wordCount else { return isNegative ? -c : c } + c &+= _words[w].intersection(_Word(upTo: b2)).count + return isNegative ? -c : c + } + + @_effects(releasenone) + //@usableFromInline + public func index(_ i: Index, offsetBy distance: Int) -> Index { + precondition(i <= endIndex, "Index out of bounds") + precondition(i == endIndex || contains(i.value), "Invalid index") + guard distance != 0 else { return i } + var remaining = distance.magnitude + if distance > 0 { + var (w, b) = i.split + precondition(w < wordCount, "Index out of bounds") + if let v = _words[w].subtracting(_Word(upTo: b)).nthElement(&remaining) { + return Index(word: w, bit: v) + } + while true { + w &+= 1 + guard w < wordCount else { break } + if let v = _words[w].nthElement(&remaining) { + return Index(word: w, bit: v) + } + } + precondition(remaining == 0, "Index out of bounds") + return endIndex + } + + // distance < 0 + remaining -= 1 + var (w, b) = i.endSplit + if w < wordCount { + if let v = _words[w].intersection(_Word(upTo: b)).nthElementFromEnd(&remaining) { + return Index(word: w, bit: v) + } + } + while true { + precondition(w > 0, "Index out of bounds") + w &-= 1 + if let v = _words[w].nthElementFromEnd(&remaining) { + return Index(word: w, bit: v) + } + } + } + + @_effects(releasenone) + //@usableFromInline + public func index( + _ i: Index, offsetBy distance: Int, limitedBy limit: Index + ) -> Index? { + precondition(i <= endIndex && limit <= endIndex, "Index out of bounds") + precondition(i == endIndex || contains(i.value), "Invalid index") + guard distance != 0 else { return i } + var remaining = distance.magnitude + if distance > 0 { + guard i <= limit else { + return self.index(i, offsetBy: distance) + } + var (w, b) = i.split + if w < wordCount, + let v = _words[w].subtracting(_Word(upTo: b)).nthElement(&remaining) + { + let r = Index(word: w, bit: v) + return r <= limit ? r : nil + } + let maxWord = Swift.min(wordCount - 1, limit.word) + while w < maxWord { + w &+= 1 + if let v = _words[w].nthElement(&remaining) { + let r = Index(word: w, bit: v) + return r <= limit ? r : nil + } + } + return remaining == 0 && limit == endIndex ? endIndex : nil + } + + // distance < 0 + guard i >= limit else { + return self.index(i, offsetBy: distance) + } + remaining &-= 1 + var (w, b) = i.endSplit + if w < wordCount { + if let v = _words[w].intersection(_Word(upTo: b)).nthElementFromEnd(&remaining) { + let r = Index(word: w, bit: v) + return r >= limit ? r : nil + } + } + let minWord = limit.word + while w > minWord { + w &-= 1 + if let v = _words[w].nthElementFromEnd(&remaining) { + let r = Index(word: w, bit: v) + return r >= limit ? r : nil + } + } + return nil + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift b/Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb similarity index 63% rename from Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift rename to Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb index f65d2aba0..9c1b6f18f 100644 --- a/Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift +++ b/Sources/_CollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb @@ -2,18 +2,27 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension UnsafeBufferPointer { @inlinable @inline(__always) - public func _ptr(at index: Int) -> UnsafePointer { + ${modifier} func _ptr(at index: Int) -> UnsafePointer { assert(index >= 0 && index < count) return baseAddress.unsafelyUnwrapped + index } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift b/Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb similarity index 94% rename from Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift rename to Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb index 8a85e59bc..0f85ac4c0 100644 --- a/Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift +++ b/Sources/_CollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb @@ -2,13 +2,20 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2023 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 // //===----------------------------------------------------------------------===// +%{ + from gyb_utils import * +}% +${autogenerated_warning()} + +% for modifier in visibility_levels: +${visibility_boilerplate(modifier)} extension UnsafeMutableBufferPointer { @inlinable public func initialize(fromContentsOf source: Self) -> Index { @@ -135,3 +142,5 @@ extension Slice { target.moveInitializeAll(fromContentsOf: source) } } +% end +${visibility_boilerplate("end")} diff --git a/Sources/_CollectionsUtilities/autogenerated/Debugging.swift b/Sources/_CollectionsUtilities/autogenerated/Debugging.swift new file mode 100644 index 000000000..8ed18c5ba --- /dev/null +++ b/Sources/_CollectionsUtilities/autogenerated/Debugging.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +/// True if consistency checking is enabled in the implementation of the +/// Swift Collections package, false otherwise. +/// +/// Documented performance promises are null and void when this property +/// returns true -- for example, operations that are documented to take +/// O(1) time might take O(*n*) time, or worse. +@inlinable @inline(__always) +internal var _isCollectionsInternalCheckingEnabled: Bool { +#if COLLECTIONS_INTERNAL_CHECKS + return true +#else + return false +#endif +} +#else // !COLLECTIONS_SINGLE_MODULE +/// True if consistency checking is enabled in the implementation of the +/// Swift Collections package, false otherwise. +/// +/// Documented performance promises are null and void when this property +/// returns true -- for example, operations that are documented to take +/// O(1) time might take O(*n*) time, or worse. +@inlinable @inline(__always) +public var _isCollectionsInternalCheckingEnabled: Bool { +#if COLLECTIONS_INTERNAL_CHECKS + return true +#else + return false +#endif +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/autogenerated/Descriptions.swift b/Sources/_CollectionsUtilities/autogenerated/Descriptions.swift new file mode 100644 index 000000000..93bdbe939 --- /dev/null +++ b/Sources/_CollectionsUtilities/autogenerated/Descriptions.swift @@ -0,0 +1,194 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE + +@usableFromInline +internal func _addressString(for pointer: UnsafeRawPointer) -> String { + let address = UInt(bitPattern: pointer) + return "0x\(String(address, radix: 16))" +} + +@usableFromInline +internal func _addressString(for object: AnyObject) -> String { + _addressString(for: Unmanaged.passUnretained(object).toOpaque()) +} + +@usableFromInline +internal func _addressString(for object: Unmanaged) -> String { + _addressString(for: object.toOpaque()) +} + +@inlinable +internal func _arrayDescription( + for elements: C, + debug: Bool = false, + typeName: String? = nil +) -> String { + var result = "" + if let typeName = typeName { + result += "\(typeName)(" + } + result += "[" + var first = true + for item in elements { + if first { + first = false + } else { + result += ", " + } + if debug { + debugPrint(item, terminator: "", to: &result) + } else { + print(item, terminator: "", to: &result) + } + } + result += "]" + if typeName != nil { result += ")" } + return result +} + +@inlinable +internal func _dictionaryDescription( + for elements: C, + debug: Bool = false, + typeName: String? = nil +) -> String where C.Element == (key: Key, value: Value) { + var result = "" + if let typeName = typeName { + result += "\(typeName)(" + } + + if elements.isEmpty { + result += "[:]" + } else { + result += "[" + var first = true + for (key, value) in elements { + if first { + first = false + } else { + result += ", " + } + if debug { + debugPrint(key, terminator: "", to: &result) + result += ": " + debugPrint(value, terminator: "", to: &result) + } else { + result += "\(key): \(value)" + } + } + result += "]" + } + + if typeName != nil { + result += ")" + } + return result +} +#else // !COLLECTIONS_SINGLE_MODULE + +//@usableFromInline +public func _addressString(for pointer: UnsafeRawPointer) -> String { + let address = UInt(bitPattern: pointer) + return "0x\(String(address, radix: 16))" +} + +//@usableFromInline +public func _addressString(for object: AnyObject) -> String { + _addressString(for: Unmanaged.passUnretained(object).toOpaque()) +} + +//@usableFromInline +public func _addressString(for object: Unmanaged) -> String { + _addressString(for: object.toOpaque()) +} + +@inlinable +public func _arrayDescription( + for elements: C, + debug: Bool = false, + typeName: String? = nil +) -> String { + var result = "" + if let typeName = typeName { + result += "\(typeName)(" + } + result += "[" + var first = true + for item in elements { + if first { + first = false + } else { + result += ", " + } + if debug { + debugPrint(item, terminator: "", to: &result) + } else { + print(item, terminator: "", to: &result) + } + } + result += "]" + if typeName != nil { result += ")" } + return result +} + +@inlinable +public func _dictionaryDescription( + for elements: C, + debug: Bool = false, + typeName: String? = nil +) -> String where C.Element == (key: Key, value: Value) { + var result = "" + if let typeName = typeName { + result += "\(typeName)(" + } + + if elements.isEmpty { + result += "[:]" + } else { + result += "[" + var first = true + for (key, value) in elements { + if first { + first = false + } else { + result += ", " + } + if debug { + debugPrint(key, terminator: "", to: &result) + result += ": " + debugPrint(value, terminator: "", to: &result) + } else { + result += "\(key): \(value)" + } + } + result += "]" + } + + if typeName != nil { + result += ")" + } + return result +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/autogenerated/RandomAccessCollection+Offsets.swift b/Sources/_CollectionsUtilities/autogenerated/RandomAccessCollection+Offsets.swift new file mode 100644 index 000000000..248a43cf9 --- /dev/null +++ b/Sources/_CollectionsUtilities/autogenerated/RandomAccessCollection+Offsets.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension RandomAccessCollection { + @_alwaysEmitIntoClient @inline(__always) + internal func _index(at offset: Int) -> Index { + index(startIndex, offsetBy: offset) + } + + @_alwaysEmitIntoClient @inline(__always) + internal func _offset(of index: Index) -> Int { + distance(from: startIndex, to: index) + } + + @_alwaysEmitIntoClient @inline(__always) + internal subscript(_offset offset: Int) -> Element { + self[_index(at: offset)] + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension RandomAccessCollection { + @_alwaysEmitIntoClient @inline(__always) + public func _index(at offset: Int) -> Index { + index(startIndex, offsetBy: offset) + } + + @_alwaysEmitIntoClient @inline(__always) + public func _offset(of index: Index) -> Int { + distance(from: startIndex, to: index) + } + + @_alwaysEmitIntoClient @inline(__always) + public subscript(_offset offset: Int) -> Element { + self[_index(at: offset)] + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift b/Sources/_CollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift new file mode 100644 index 000000000..075f1e809 --- /dev/null +++ b/Sources/_CollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension UnsafeBufferPointer { + @inlinable + @inline(__always) + internal func _ptr(at index: Int) -> UnsafePointer { + assert(index >= 0 && index < count) + return baseAddress.unsafelyUnwrapped + index + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension UnsafeBufferPointer { + @inlinable + @inline(__always) + public func _ptr(at index: Int) -> UnsafePointer { + assert(index >= 0 && index < count) + return baseAddress.unsafelyUnwrapped + index + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/_CollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift b/Sources/_CollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift new file mode 100644 index 000000000..fa6ec9614 --- /dev/null +++ b/Sources/_CollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift @@ -0,0 +1,278 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2022 - 2023 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 +// +//===----------------------------------------------------------------------===// + + +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# + + + +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE +extension UnsafeMutableBufferPointer { + @inlinable + public func initialize(fromContentsOf source: Self) -> Index { + guard source.count > 0 else { return 0 } + precondition( + source.count <= self.count, + "buffer cannot contain every element from source.") + baseAddress.unsafelyUnwrapped.initialize( + from: source.baseAddress.unsafelyUnwrapped, + count: source.count) + return source.count + } + + @inlinable + public func initialize(fromContentsOf source: Slice) -> Index { + let sourceCount = source.count + guard sourceCount > 0 else { return 0 } + precondition( + sourceCount <= self.count, + "buffer cannot contain every element from source.") + baseAddress.unsafelyUnwrapped.initialize( + from: source.base.baseAddress.unsafelyUnwrapped + source.startIndex, + count: sourceCount) + return sourceCount + } +} + +extension Slice { + @inlinable @inline(__always) + public func initialize( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index + where Base == UnsafeMutableBufferPointer + { + let target = UnsafeMutableBufferPointer(rebasing: self) + let i = target.initialize(fromContentsOf: source) + return self.startIndex + i + } + + @inlinable @inline(__always) + public func initialize( + fromContentsOf source: Slice> + ) -> Index + where Base == UnsafeMutableBufferPointer + { + let target = UnsafeMutableBufferPointer(rebasing: self) + let i = target.initialize(fromContentsOf: source) + return self.startIndex + i + } +} + +extension UnsafeMutableBufferPointer { + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: C + ) where C.Element == Element { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: Self) { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: Slice) { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func moveInitializeAll(fromContentsOf source: Self) { + let i = self.moveInitialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func moveInitializeAll(fromContentsOf source: Slice) { + let i = self.moveInitialize(fromContentsOf: source) + assert(i == self.endIndex) + } +} + +extension Slice { + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: C + ) where Base == UnsafeMutableBufferPointer { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: UnsafeMutableBufferPointer + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.initializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: Slice> + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.initializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func moveInitializeAll( + fromContentsOf source: UnsafeMutableBufferPointer + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.moveInitializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func moveInitializeAll( + fromContentsOf source: Slice> + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.moveInitializeAll(fromContentsOf: source) + } +} +#else // !COLLECTIONS_SINGLE_MODULE +extension UnsafeMutableBufferPointer { + @inlinable + public func initialize(fromContentsOf source: Self) -> Index { + guard source.count > 0 else { return 0 } + precondition( + source.count <= self.count, + "buffer cannot contain every element from source.") + baseAddress.unsafelyUnwrapped.initialize( + from: source.baseAddress.unsafelyUnwrapped, + count: source.count) + return source.count + } + + @inlinable + public func initialize(fromContentsOf source: Slice) -> Index { + let sourceCount = source.count + guard sourceCount > 0 else { return 0 } + precondition( + sourceCount <= self.count, + "buffer cannot contain every element from source.") + baseAddress.unsafelyUnwrapped.initialize( + from: source.base.baseAddress.unsafelyUnwrapped + source.startIndex, + count: sourceCount) + return sourceCount + } +} + +extension Slice { + @inlinable @inline(__always) + public func initialize( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index + where Base == UnsafeMutableBufferPointer + { + let target = UnsafeMutableBufferPointer(rebasing: self) + let i = target.initialize(fromContentsOf: source) + return self.startIndex + i + } + + @inlinable @inline(__always) + public func initialize( + fromContentsOf source: Slice> + ) -> Index + where Base == UnsafeMutableBufferPointer + { + let target = UnsafeMutableBufferPointer(rebasing: self) + let i = target.initialize(fromContentsOf: source) + return self.startIndex + i + } +} + +extension UnsafeMutableBufferPointer { + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: C + ) where C.Element == Element { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: Self) { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: Slice) { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func moveInitializeAll(fromContentsOf source: Self) { + let i = self.moveInitialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func moveInitializeAll(fromContentsOf source: Slice) { + let i = self.moveInitialize(fromContentsOf: source) + assert(i == self.endIndex) + } +} + +extension Slice { + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: C + ) where Base == UnsafeMutableBufferPointer { + let i = self.initialize(fromContentsOf: source) + assert(i == self.endIndex) + } + + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: UnsafeMutableBufferPointer + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.initializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func initializeAll( + fromContentsOf source: Slice> + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.initializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func moveInitializeAll( + fromContentsOf source: UnsafeMutableBufferPointer + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.moveInitializeAll(fromContentsOf: source) + } + + @inlinable @inline(__always) + public func moveInitializeAll( + fromContentsOf source: Slice> + ) where Base == UnsafeMutableBufferPointer { + let target = UnsafeMutableBufferPointer(rebasing: self) + target.moveInitializeAll(fromContentsOf: source) + } +} +#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Tests/BitCollectionsTests/BitArrayTests.swift b/Tests/BitCollectionsTests/BitArrayTests.swift index 303ec8b46..2368689fd 100644 --- a/Tests/BitCollectionsTests/BitArrayTests.swift +++ b/Tests/BitCollectionsTests/BitArrayTests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else import _CollectionsTestSupport @_spi(Testing) import BitCollections +#endif extension BitArray { static func _fromSequence( @@ -144,7 +148,115 @@ final class BitArrayTests: CollectionTestCase { expectEqualElements(actual, expected) } } - + + func test_conversion_to_BinaryInteger_truncating() { + let cases: [(bits: BitArray, signed: Int8, unsigned: UInt8)] = [ + ("", 0, 0), + ("0", 0, 0), + ("1", -1, 1), + ("00", 0, 0), + ("01", 1, 1), + ("10", -2, 2), + ("11", -1, 3), + ("001", 1, 1), + ("010", 2, 2), + ("011", 3, 3), + ("100", -4, 4), + ("101", -3, 5), + ("110", -2, 6), + ("111", -1, 7), + // 8 bits + ("00000000", 0, 0), + ("10000000", -128, 128), + ("10000001", -127, 129), + ("11111111", -1, 255), + // 9 bits + ("000000000", 0, 0), + ("000000100", 4, 4), + ("010000000", -128, 128), + ("010000001", -127, 129), + ("011111111", -1, 255), + ("100000000", 0, 0), + ("100000001", 1, 1), + ("101111111", 127, 127), + ("110000000", -128, 128), + ("110000001", -127, 129), + ("111111110", -2, 254), + ("111111111", -1, 255), + // 32 bits + ("00000000000000000000000000000000", 0, 0), + ("00000000000000000000000001111111", 127, 127), + ("00000000000000000000000010000000", -128, 128), + ("00000000000000000000000011111111", -1, 255), + ("00000000000000000000000100000000", 0, 0), + ("11111111111111111111111110000000", -128, 128), + ("11111111111111111111111111111110", -2, 254), + ("11111111111111111111111111111111", -1, 255), + ] + + withEvery("pair", in: cases) { (bits, signed, unsigned) in + let actual1 = Int8(truncatingIfNeeded: bits) + expectEqual(actual1, signed) + + let actual2 = UInt8(truncatingIfNeeded: bits) + expectEqual(actual2, unsigned) + } + } + + func test_conversion_to_BinaryInteger_exact() { + let cases: [(bits: BitArray, signed: Int8?, unsigned: UInt8?)] = [ + ("", 0, 0), + ("0", 0, 0), + ("1", -1, 1), + ("00", 0, 0), + ("01", 1, 1), + ("10", -2, 2), + ("11", -1, 3), + ("001", 1, 1), + ("010", 2, 2), + ("011", 3, 3), + ("100", -4, 4), + ("101", -3, 5), + ("110", -2, 6), + ("111", -1, 7), + // 8 bits + ("00000000", 0, 0), + ("10000000", -128, 128), + ("10000001", -127, 129), + ("11111111", -1, 255), + // 9 bits + ("000000000", 0, 0), + ("000000100", 4, 4), + ("010000000", nil, 128), + ("010000001", nil, 129), + ("011111111", nil, 255), + ("100000000", nil, nil), + ("100000001", nil, nil), + ("101111111", nil, nil), + ("110000000", -128, nil), + ("110000001", -127, nil), + ("111111110", -2, nil), + ("111111111", -1, nil), + // 32 bits + ("00000000000000000000000000000000", 0, 0), + ("00000000000000000000000001111111", 127, 127), + ("00000000000000000000000010000000", nil, 128), + ("00000000000000000000000011111111", nil, 255), + ("00000000000000000000000100000000", nil, nil), + ("11111111111111111111111110000000", -128, nil), + ("11111111111111111111111111111110", -2, nil), + ("11111111111111111111111111111111", -1, nil), + ] + + withEvery("pair", in: cases) { (bits, signed, unsigned) in + let actual1 = Int8(exactly: bits) + expectEqual(actual1, signed) + + let actual2 = UInt8(exactly: bits) + expectEqual(actual2, unsigned) + } + } + func test_init_BitSet() { expectEqualElements(BitArray(BitSet([])), []) expectEqualElements(BitArray(BitSet([0])), [true]) @@ -212,7 +324,76 @@ final class BitArrayTests: CollectionTestCase { true, true, false, false, true, true, true, ]) } - + + func test_ExpressibleByStringLiteral() { + let a: BitArray = "" + expectEqualElements(a, []) + + let b: BitArray = "1" + expectEqualElements(b, [true]) + + let c: BitArray = "001" + expectEqualElements(c, [true, false, false]) + + let d: BitArray = "10001" + expectEqualElements(d, [true, false, false, false, true]) + + let e: BitArray = """ + 111001110100010110100000000100110010010101001000000100110010001 + """ + expectEqualElements(e, [ + true, false, false, false, true, false, false, + true, true, false, false, true, false, false, + false, false, false, false, true, false, false, + true, false, true, false, true, false, false, + true, false, false, true, true, false, false, + true, false, false, false, false, false, false, + false, false, true, false, true, true, false, + true, false, false, false, true, false, true, + true, true, false, false, true, true, true, + ]) + } + + func test_literals() { + let cases: [(a: BitArray, b: BitArray)] = [ + ("", []), + ("0", [false]), + ("1", [true]), + ("1010", [false, true, false, true]), + ("0101", [true, false, true, false]), + ("111000", [false, false, false, true, true, true]), + ("000111", [true, true, true, false, false, false]), + ] + withEvery("i", in: cases.indices) { i in + let (a, b) = cases[i] + expectEqual(a, b) + } + } + + func test_LosslessStringConvertible() { + let cases: [(a: String, b: BitArray?)] = [ + ("", []), + ("0", [false]), + ("1", [true]), + ("1010", [false, true, false, true]), + ("0101", [true, false, true, false]), + ("111000", [false, false, false, true, true, true]), + ("000111", [true, true, true, false, false, false]), + ("_", nil), + ("00010101X", nil), + ("①⓪⓪①", nil), + ("2341", nil), + ("00 10 01", nil), + (" 01", nil), + ("01 ", nil), + ] + withEvery("i", in: cases.indices) { i in + let (a, b) = cases[i] + let bits = BitArray(a) + expectEqual(bits, b) + } + } + func test_Hashable() { // This is a silly test, but it does exercise hashing a bit. let classes: [[BitArray]] = [ @@ -628,7 +809,19 @@ final class BitArrayTests: CollectionTestCase { bits.reserveCapacity(2000) expectGreaterThanOrEqual(bits._capacity, 2000) } - + + func test_init_minimumCapacity() { + let b1 = BitArray(minimumCapacity: 0) + expectEqual(b1._capacity, 0) + + let cases = [0, 1, 100, 1000, 2000] + withEvery("capacity", in: cases) { capacity in + let bits = BitArray(minimumCapacity: capacity) + expectTrue(bits.isEmpty) + expectGreaterThanOrEqual(bits._capacity, capacity) + } + } + func test_bitwiseOr() { withSome("count", in: 0 ..< 512, maxSamples: 100) { count in withEvery("i", in: 0 ..< 10) { i in @@ -719,35 +912,35 @@ final class BitArrayTests: CollectionTestCase { func test_random() { var rng = AllOnesRandomNumberGenerator() for c in [0, 10, 64, 65, 77, 1200] { - let array = BitArray.random(count: c, using: &rng) + let array = BitArray.randomBits(count: c, using: &rng) expectEqual(array.count, c) expectEqualElements(array, repeatElement(true, count: c)) } - let a = Set((0..<10).map { _ in BitArray.random(count: 1000) }) + let a = Set((0..<10).map { _ in BitArray.randomBits(count: 1000) }) expectEqual(a.count, 10) } func test_description() { let a: BitArray = [] - expectEqual("\(a)", "0") + expectEqual("\(a)", "") let b: BitArray = [true, false, true, true, true] - expectEqual("\(b)", "10111") + expectEqual("\(b)", "11101") let c: BitArray = [false, false, false, false, true, true, true, false] - expectEqual("\(c)", "00001110") + expectEqual("\(c)", "01110000") } func test_debugDescription() { let a: BitArray = [] - expectEqual("\(String(reflecting: a))", "BitArray(0)") + expectEqual("\(String(reflecting: a))", "BitArray()") let b: BitArray = [true, false, true, true, true] - expectEqual("\(String(reflecting: b))", "BitArray(10111)") + expectEqual("\(String(reflecting: b))", "BitArray(11101)") let c: BitArray = [false, false, false, false, true, true, true, false] - expectEqual("\(String(reflecting: c))", "BitArray(00001110)") + expectEqual("\(String(reflecting: c))", "BitArray(01110000)") } func test_mirror() { diff --git a/Tests/BitCollectionsTests/BitSet.Counted Tests.swift b/Tests/BitCollectionsTests/BitSet.Counted Tests.swift index bb7d86d78..f07d8000d 100644 --- a/Tests/BitCollectionsTests/BitSet.Counted Tests.swift +++ b/Tests/BitCollectionsTests/BitSet.Counted Tests.swift @@ -10,9 +10,13 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import BitCollections import OrderedCollections +#endif extension BitSet.Counted: SetAPIExtras { public mutating func update(_ member: Int, at index: Index) -> Int { diff --git a/Tests/BitCollectionsTests/BitSetTests.swift b/Tests/BitCollectionsTests/BitSetTests.swift index 5a9becf2c..0851a368b 100644 --- a/Tests/BitCollectionsTests/BitSetTests.swift +++ b/Tests/BitCollectionsTests/BitSetTests.swift @@ -10,9 +10,13 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import BitCollections import OrderedCollections +#endif extension BitSet: SetAPIExtras { public mutating func update(_ member: Int, at index: Index) -> Int { diff --git a/Tests/CollectionsTestSupportTests/CombinatoricsChecks.swift b/Tests/CollectionsTestSupportTests/CombinatoricsChecks.swift index 3309fa0aa..dca5da2b0 100644 --- a/Tests/CollectionsTestSupportTests/CombinatoricsChecks.swift +++ b/Tests/CollectionsTestSupportTests/CombinatoricsChecks.swift @@ -10,7 +10,9 @@ //===----------------------------------------------------------------------===// import XCTest +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsTestSupport +#endif class CombinatoricsTests: CollectionTestCase { func testEverySubset_smoke() { diff --git a/Tests/CollectionsTestSupportTests/IndexRangeCollectionTests.swift b/Tests/CollectionsTestSupportTests/IndexRangeCollectionTests.swift index 339f6db85..f3bac6e00 100644 --- a/Tests/CollectionsTestSupportTests/IndexRangeCollectionTests.swift +++ b/Tests/CollectionsTestSupportTests/IndexRangeCollectionTests.swift @@ -10,7 +10,9 @@ //===----------------------------------------------------------------------===// import XCTest +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsTestSupport +#endif final class IndexRangeCollectionTests: CollectionTestCase { func testCollection() { diff --git a/Tests/CollectionsTestSupportTests/MinimalTypeConformances.swift b/Tests/CollectionsTestSupportTests/MinimalTypeConformances.swift index 394e38174..d74b1198a 100644 --- a/Tests/CollectionsTestSupportTests/MinimalTypeConformances.swift +++ b/Tests/CollectionsTestSupportTests/MinimalTypeConformances.swift @@ -10,7 +10,9 @@ //===----------------------------------------------------------------------===// import XCTest +#if !COLLECTIONS_SINGLE_MODULE import _CollectionsTestSupport +#endif final class MinimalTypeTests: CollectionTestCase { func testMinimalSequence() { diff --git a/Tests/CollectionsTestSupportTests/UtilitiesTests.swift b/Tests/CollectionsTestSupportTests/UtilitiesTests.swift index 86ae1e9ba..b0038980e 100644 --- a/Tests/CollectionsTestSupportTests/UtilitiesTests.swift +++ b/Tests/CollectionsTestSupportTests/UtilitiesTests.swift @@ -9,10 +9,13 @@ // //===----------------------------------------------------------------------===// -#if DEBUG import XCTest + +#if !COLLECTIONS_SINGLE_MODULE && DEBUG @testable import _CollectionsTestSupport +#endif +#if COLLECTIONS_SINGLE_MODULE || DEBUG final class UtilitiesTests: CollectionTestCase { func testIntegerSquareRoot() { withSome("i", in: 0 ..< Int.max, maxSamples: 100_000) { i in @@ -22,5 +25,4 @@ final class UtilitiesTests: CollectionTestCase { } } } - #endif diff --git a/Tests/DequeTests/DequeInternals.swift b/Tests/DequeTests/DequeInternals.swift index 1f4e165e8..cc533e0d7 100644 --- a/Tests/DequeTests/DequeInternals.swift +++ b/Tests/DequeTests/DequeInternals.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else import _CollectionsTestSupport @_spi(Testing) import DequeModule +#endif internal struct DequeLayout: CustomStringConvertible { let capacity: Int diff --git a/Tests/DequeTests/DequeTests.swift b/Tests/DequeTests/DequeTests.swift index 22227a6f3..7c191e4d7 100644 --- a/Tests/DequeTests/DequeTests.swift +++ b/Tests/DequeTests/DequeTests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else import _CollectionsTestSupport @_spi(Testing) import DequeModule +#endif final class DequeTests: CollectionTestCase { func test_testingSPIs() { diff --git a/Tests/DequeTests/MutableCollectionTests.swift b/Tests/DequeTests/MutableCollectionTests.swift index 1d5ea85dc..330d7c2a7 100644 --- a/Tests/DequeTests/MutableCollectionTests.swift +++ b/Tests/DequeTests/MutableCollectionTests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport @_spi(Testing) import DequeModule +#endif final class MutableCollectiontests: CollectionTestCase { // Note: Most of the test below are exhaustively testing the behavior diff --git a/Tests/DequeTests/RangeReplaceableCollectionTests.swift b/Tests/DequeTests/RangeReplaceableCollectionTests.swift index e62710194..6731d75e1 100644 --- a/Tests/DequeTests/RangeReplaceableCollectionTests.swift +++ b/Tests/DequeTests/RangeReplaceableCollectionTests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else import _CollectionsTestSupport @_spi(Testing) import DequeModule +#endif /// Exhaustive tests for `Deque`'s implementations for `RangeReplaceableCollection` /// requirements. diff --git a/Tests/HashTreeCollectionsTests/TreeDictionary Smoke Tests.swift b/Tests/HashTreeCollectionsTests/TreeDictionary Smoke Tests.swift index ed6e32a48..f9981d815 100644 --- a/Tests/HashTreeCollectionsTests/TreeDictionary Smoke Tests.swift +++ b/Tests/HashTreeCollectionsTests/TreeDictionary Smoke Tests.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif extension TreeDictionary { fileprivate func contains(_ key: Key) -> Bool { diff --git a/Tests/HashTreeCollectionsTests/TreeDictionary Tests.swift b/Tests/HashTreeCollectionsTests/TreeDictionary Tests.swift index 8674350f6..c38fcfc77 100644 --- a/Tests/HashTreeCollectionsTests/TreeDictionary Tests.swift +++ b/Tests/HashTreeCollectionsTests/TreeDictionary Tests.swift @@ -2,15 +2,19 @@ // // This source file is part of the Swift Collections open source project // -// Copyright (c) 2021 - 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2023 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 COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif extension TreeDictionary: DictionaryAPIExtras {} @@ -270,7 +274,6 @@ class TreeDictionaryTests: CollectionTestCase { d, expectedContents: ref, by: ==) } -#if swift(>=5.6) @available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) struct FancyDictionaryKey: CodingKeyRepresentable, Hashable, Codable { var value: Int @@ -286,7 +289,6 @@ class TreeDictionaryTests: CollectionTestCase { self.init(value) } } -#endif struct BoringDictionaryKey: Hashable, Codable { var value: Int @@ -312,7 +314,6 @@ class TreeDictionaryTests: CollectionTestCase { ]) expectEqual(try MinimalEncoder.encode(d2), v2) -#if swift(>=5.6) if #available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) { let d3: TreeDictionary = [ FancyDictionaryKey(1): 10, FancyDictionaryKey(2): 20 @@ -322,7 +323,6 @@ class TreeDictionaryTests: CollectionTestCase { ]) expectEqual(try MinimalEncoder.encode(d3), v3) } -#endif let d4: TreeDictionary = [ // Note: we only have a single element to prevent ordering @@ -358,7 +358,6 @@ class TreeDictionaryTests: CollectionTestCase { try MinimalDecoder.decode(v2, as: PD.self), d2) -#if swift(>=5.6) if #available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) { let d3: TreeDictionary = [ FancyDictionaryKey(1): 10, FancyDictionaryKey(2): 20 @@ -370,7 +369,6 @@ class TreeDictionaryTests: CollectionTestCase { try MinimalDecoder.decode(v3, as: PD.self), d3) } -#endif let d4: TreeDictionary = [ // Note: we only have a single element to prevent ordering @@ -392,7 +390,6 @@ class TreeDictionaryTests: CollectionTestCase { expectTrue($0 is DecodingError) } -#if swift(>=5.6) if #available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) { let v6: MinimalEncoder.Value = .dictionary([ "This is not a number": .string("bus"), @@ -415,7 +412,6 @@ class TreeDictionaryTests: CollectionTestCase { try MinimalDecoder.decode(v7, as: PD.self), d7) } -#endif let v8: MinimalEncoder.Value = .array([ .int32(1), .string("bike"), .int32(2), diff --git a/Tests/HashTreeCollectionsTests/TreeDictionary.Keys Tests.swift b/Tests/HashTreeCollectionsTests/TreeDictionary.Keys Tests.swift index c714b187d..9dc06a27e 100644 --- a/Tests/HashTreeCollectionsTests/TreeDictionary.Keys Tests.swift +++ b/Tests/HashTreeCollectionsTests/TreeDictionary.Keys Tests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif class TreeDictionaryKeysTests: CollectionTestCase { func test_BidirectionalCollection_fixtures() { diff --git a/Tests/HashTreeCollectionsTests/TreeDictionary.Values Tests.swift b/Tests/HashTreeCollectionsTests/TreeDictionary.Values Tests.swift index b64f8ef4f..8547ccaa2 100644 --- a/Tests/HashTreeCollectionsTests/TreeDictionary.Values Tests.swift +++ b/Tests/HashTreeCollectionsTests/TreeDictionary.Values Tests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif class TreeDictionaryValuesTests: CollectionTestCase { func test_BidirectionalCollection_fixtures() { diff --git a/Tests/HashTreeCollectionsTests/TreeHashedCollections Fixtures.swift b/Tests/HashTreeCollectionsTests/TreeHashedCollections Fixtures.swift index 5b7923065..58333c847 100644 --- a/Tests/HashTreeCollectionsTests/TreeHashedCollections Fixtures.swift +++ b/Tests/HashTreeCollectionsTests/TreeHashedCollections Fixtures.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif /// A set of items whose subsets will produce a bunch of interesting test /// cases. diff --git a/Tests/HashTreeCollectionsTests/TreeSet Tests.swift b/Tests/HashTreeCollectionsTests/TreeSet Tests.swift index 9ea871778..5ce7cc88f 100644 --- a/Tests/HashTreeCollectionsTests/TreeSet Tests.swift +++ b/Tests/HashTreeCollectionsTests/TreeSet Tests.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif extension TreeSet: SetAPIExtras {} diff --git a/Tests/HashTreeCollectionsTests/Utilities.swift b/Tests/HashTreeCollectionsTests/Utilities.swift index bb13dcc73..9d0b8d715 100644 --- a/Tests/HashTreeCollectionsTests/Utilities.swift +++ b/Tests/HashTreeCollectionsTests/Utilities.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import HashTreeCollections +#endif extension LifetimeTracker { func shareableDictionary( @@ -114,22 +118,6 @@ extension LifetimeTracker { } } -func _expectFailure( - _ diagnostic: String, - _ message: () -> String, - trapping: Bool, - file: StaticString, - line: UInt -) { - expectFailure( - """ - \(diagnostic) - \(message()) - """, - trapping: trapping, - file: file, line: line) -} - func expectEqualSets( _ set: TreeSet, _ ref: [Element], diff --git a/Tests/HeapTests/NodeTests.swift b/Tests/HeapTests/HeapNodeTests.swift similarity index 85% rename from Tests/HeapTests/NodeTests.swift rename to Tests/HeapTests/HeapNodeTests.swift index 753e454ed..29bf0a1bd 100644 --- a/Tests/HeapTests/NodeTests.swift +++ b/Tests/HeapTests/HeapNodeTests.swift @@ -11,16 +11,20 @@ #if DEBUG // These unit tests need access to HeapModule internals import XCTest +#if COLLECTIONS_SINGLE_MODULE +@testable import Collections +#else @testable import HeapModule +#endif -class NodeTests: XCTestCase { +class HeapNodeTests: XCTestCase { func test_levelCalculation() { // Check alternating min and max levels in the heap var isMin = true for exp in 0...12 { // Check [2^exp, 2^(exp + 1)) for offset in Int(pow(2, Double(exp)) - 1).. [Element] { diff --git a/Tests/OrderedCollectionsTests/HashTable/HashTableTests.swift b/Tests/OrderedCollectionsTests/HashTable/HashTableTests.swift index e3289a5bc..0e51b0134 100644 --- a/Tests/OrderedCollectionsTests/HashTable/HashTableTests.swift +++ b/Tests/OrderedCollectionsTests/HashTable/HashTableTests.swift @@ -9,10 +9,16 @@ // //===----------------------------------------------------------------------===// -#if DEBUG // These unit tests need access to OrderedSet internals import XCTest + +#if DEBUG // These unit tests use internal decls + +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) @testable import Collections +#else import _CollectionsTestSupport @_spi(Testing) @testable import OrderedCollections +#endif class HashTableTests: CollectionTestCase { typealias Bucket = _HashTable.Bucket diff --git a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Tests.swift b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Tests.swift index d4d34293d..57c7516ef 100644 --- a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Tests.swift @@ -10,9 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else @_spi(Testing) import OrderedCollections - import _CollectionsTestSupport +#endif extension OrderedDictionary: DictionaryAPIExtras {} diff --git a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Utils.swift b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Utils.swift index a79c993bf..5e1ae1a8f 100644 --- a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Utils.swift +++ b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary Utils.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsTestSupport import OrderedCollections +#endif extension LifetimeTracker { func orderedDictionary( diff --git a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Elements Tests.swift b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Elements Tests.swift index 8ac1c4b14..0ea74f770 100644 --- a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Elements Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Elements Tests.swift @@ -10,9 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else @_spi(Testing) import OrderedCollections - import _CollectionsTestSupport +#endif class OrderedDictionaryElementsTests: CollectionTestCase { func test_elements_getter() { diff --git a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Values Tests.swift b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Values Tests.swift index d45e80596..ea2a739fa 100644 --- a/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Values Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedDictionary/OrderedDictionary+Values Tests.swift @@ -10,9 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else @_spi(Testing) import OrderedCollections - import _CollectionsTestSupport +#endif class OrderedDictionaryValueTests: CollectionTestCase { func test_values_getter() { diff --git a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift index 1f06a148a..9d79968f4 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import OrderedCollections import _CollectionsTestSupport +#endif class MeasuringHashable: Hashable { static var equalityChecks = 0 diff --git a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet.UnorderedView Tests.swift b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet.UnorderedView Tests.swift index 4d1662539..bf8aa88fe 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet.UnorderedView Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet.UnorderedView Tests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else @_spi(Testing) import OrderedCollections import _CollectionsTestSupport +#endif // Note: This cannot really work unless `UnorderedView` becomes a Collection. // extension OrderedSet.UnorderedView: SetAPIChecker {} diff --git a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetInternals.swift b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetInternals.swift index 397db56d3..b0279c40a 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetInternals.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetInternals.swift @@ -9,8 +9,12 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else import _CollectionsTestSupport @_spi(Testing) import OrderedCollections +#endif struct OrderedSetLayout: Hashable, CustomStringConvertible { let scale: Int diff --git a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetTests.swift b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetTests.swift index 361078c15..c745ea79e 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetTests.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSetTests.swift @@ -10,8 +10,12 @@ //===----------------------------------------------------------------------===// import XCTest +#if COLLECTIONS_SINGLE_MODULE +@_spi(Testing) import Collections +#else @_spi(Testing) import OrderedCollections import _CollectionsTestSupport +#endif extension OrderedSet: SetAPIExtras {} diff --git a/Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Offsets.swift b/Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Extras.swift similarity index 94% rename from Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Offsets.swift rename to Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Extras.swift index 79db7eded..8c6243d2f 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Offsets.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/RandomAccessCollection+Extras.swift @@ -9,7 +9,12 @@ // //===----------------------------------------------------------------------===// +#if !COLLECTIONS_SINGLE_MODULE +import _CollectionsUtilities +#endif + extension RandomAccessCollection { + #if SWIFT_PACKAGE @inline(__always) internal func _index(at offset: Int) -> Index { index(startIndex, offsetBy: offset) @@ -20,6 +25,12 @@ extension RandomAccessCollection { distance(from: startIndex, to: index) } + @inline(__always) + internal subscript(_offset offset: Int) -> Element { + self[_index(at: offset)] + } + #endif + @inline(__always) internal func _indexRange(at offsets: Range) -> Range { _index(at: offsets.lowerBound) ..< _index(at: offsets.upperBound) @@ -42,11 +53,6 @@ extension RandomAccessCollection { return _offsetRange(of: range.relative(to: self)) } - @inline(__always) - internal subscript(_offset offset: Int) -> Element { - self[_index(at: offset)] - } - @inline(__always) internal subscript(_offsets range: Range) -> SubSequence { self[_indexRange(at: range)] diff --git a/Tests/README.md b/Tests/README.md new file mode 100644 index 000000000..701eda8a5 --- /dev/null +++ b/Tests/README.md @@ -0,0 +1,3 @@ +# Unit tests + +Beware! The contents of this directory are not source stable. They are provided as is, with no compatibility promises across package releases. Future versions of this package can arbitrarily change these files or remove them, without any advance notice. (This can include patch releases.) diff --git a/Tests/RopeModuleTests/Availability.swift b/Tests/RopeModuleTests/Availability.swift new file mode 100644 index 000000000..a20b578c6 --- /dev/null +++ b/Tests/RopeModuleTests/Availability.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +var isRunningOnSwiftStdlib5_8: Bool { + if #available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) { + return true + } + return false +} + + +#if swift(<5.8) +extension String.Index { + var _description: String { + let raw = unsafeBitCast(self, to: UInt64.self) + let encodedOffset = Int(truncatingIfNeeded: raw &>> 16) + let transcodedOffset = Int(truncatingIfNeeded: (raw &>> 14) & 0x3) + var d = "\(encodedOffset)[unknown]" + if transcodedOffset > 0 { + d += "+\(transcodedOffset)" + } + return d + } +} +#endif diff --git a/Tests/RopeModuleTests/SampleStrings.swift b/Tests/RopeModuleTests/SampleStrings.swift new file mode 100644 index 000000000..1f93619d4 --- /dev/null +++ b/Tests/RopeModuleTests/SampleStrings.swift @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 !COLLECTIONS_SINGLE_MODULE +import _CollectionsTestSupport +#endif + +func randomStride( + from start: Int, + to end: Int, + by maxStep: Int, + seed: Int +) -> UnfoldSequence { + var rng = RepeatableRandomNumberGenerator(seed: seed) + return sequence(state: start, next: { + $0 += Int.random(in: 1 ... maxStep, using: &rng) + guard $0 < end else { return nil } + return $0 + }) +} + +let sampleString = #""" + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + 一种强大但极易学习的编程语言。 + Swift 是一种强大直观的编程语言,适用于 iOS、iPadOS、macOS、Apple tvOS 和 watchOS。\# + 编写 Swift 代码的过程充满了乐趣和互动。Swift 语法简洁,但表现力强,更包含了开发者喜爱的现代功能。\# + Swift 代码从设计上保证安全,并能开发出运行快如闪电的软件。 + + パワフルなプログラミング言語でありながら、簡単に習得することができます。 + Swiftは、iOS、iPadOS、macOS、tvOS、watchOS向けのパワフルで直感的なプログラミング言語です。\# + Swiftのコーディングはインタラクティブで楽しく、構文はシンプルでいて表現力に富んでいます。\# + さらに、デベロッパが求める最新の機能も備えています。安全性を重視しつつ非常に軽快に動作す\# + るソフトウェアを作り出すことができます。それがSwiftです。 + + 손쉽게 학습할 수 있는 강력한 프로그래밍 언어. + Swift는 iOS, iPadOS, macOS, tvOS 및 watchOS를 위한 강력하고 직관적인 프로그래밍 언어입니다. \# + Swift 코드 작성은 대화식으로 재미있고, 구문은 간결하면서도 표현력이 풍부하며, Swift에는 개발자들이 \# + 좋아하는 첨단 기능이 포함되어 있습니다. Swift 코드는 안전하게 설계되었으며 빛의 속도로 빠르게 실행되는 \# + 소프트웨어를 제작할 수 있습니다. + + 🪙 A 🥞 short 🍰 piece 🫘 of 🌰 text 👨‍👨‍👧‍👧 with 👨‍👩‍👦 some 🚶🏽 emoji 🇺🇸🇨🇦 characters 🧈 + some🔩times 🛺 placed 🎣 in 🥌 the 🆘 mid🔀dle 🇦🇶or🏁 around 🏳️‍🌈 a 🍇 w🍑o🥒r🥨d + + ⌘⏎ ⌃⇧⌥⌘W + ¯\_(ツ)_/¯ + ಠ_ಠ + + 🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦\# + 🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦\# + 🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦🇺🇸🇨🇦 + + Unicode is such fun! + U̷n̷i̷c̷o̴d̴e̷ ̶i̸s̷ ̸s̵u̵c̸h̷ ̸f̵u̷n̴!̵ + U̴̡̲͋̾n̵̻̳͌ì̶̠̕c̴̭̈͘ǫ̷̯͋̊d̸͖̩̈̈́ḛ̴́ ̴̟͎͐̈i̴̦̓s̴̜̱͘ ̶̲̮̚s̶̙̞͘u̵͕̯̎̽c̵̛͕̜̓h̶̘̍̽ ̸̜̞̿f̵̤̽ṷ̴͇̎͘ń̷͓̒!̷͍̾̚ + U̷̢̢̧̨̼̬̰̪͓̞̠͔̗̼̙͕͕̭̻̗̮̮̥̣͉̫͉̬̲̺͍̺͊̂ͅn̶̨̢̨̯͓̹̝̲̣̖̞̼̺̬̤̝̊̌́̑̋̋͜͝ͅḭ̸̦̺̺͉̳͎́͑c̵̛̘̥̮̙̥̟̘̝͙̤̮͉͔̭̺̺̅̀̽̒̽̏̊̆͒͌̂͌̌̓̈́̐̔̿̂͑͠͝͝ͅö̶̱̠̱̤̙͚͖̳̜̰̹̖̣̻͎͉̞̫̬̯͕̝͔̝̟̘͔̙̪̭̲́̆̂͑̌͂̉̀̓́̏̎̋͗͛͆̌̽͌̄̎̚͝͝͝͝ͅd̶̨̨̡̡͙̟͉̱̗̝͙͍̮͍̘̮͔͑e̶̢͕̦̜͔̘̘̝͈̪̖̺̥̺̹͉͎͈̫̯̯̻͑͑̿̽͂̀̽͋́̎̈́̈̿͆̿̒̈́̽̔̇͐͛̀̓͆̏̾̀̌̈́̆̽̕ͅ ̷̢̳̫̣̼̙̯̤̬̥̱͓̹͇̽̄̄̋̿̐̇̌̒̾̑̆̈́̏͐̒̈̋̎͐̿̽̆̉͋͊̀̍͘̕̕̕͝͠͠͝ͅͅì̸̢̧̨̨̮͇̤͍̭̜̗̪̪͖̭͇͔̜̗͈̫̩͔̗͔̜̖̲̱͍̗̱̩͍̘̜̙̩͔̏̋̓̊́́̋̐̌͊͘̕͠s̶̨̢̧̥̲̖̝̩͖̱͋́͑͐̇̐̔̀̉͒͒́̐̉̔͘͠͠ ̵̧̛͕̦̭̣̝̩͕̠͎̮͓͉̟̠̘͎͋͗͆̋̌̓̃̏̊̔̾̒̿s̸̟͚̪̘̰̮͉̖̝̅̓͛̏̆ư̵͍̙̠͍̜͖͔̮̠̦̤̣̯̘̲͍͂͌̌̅̍͌̈́̆̋̎͋̓̍͆̃̑͌͘̕͜ͅç̸̟̗͉̟̤̙̹͓̖͇̳̈́̍̏͐̓̓̈̆̉̈͆̍ͅh̵̛̛̹̪͇͓̤̺̟͙̣̰͓̺̩̤̘̫͔̺͙͌́̑̓͗̏͆́͊̈́̋̿͒̐̀́̌͜͜͝ ̴̗͓͚͖̣̥͛́̓͐͂͛̐͑̈́͗̂̈͠f̶̡̩̟̤̭̩̱̥͈̼̥̳͕̣͓̱̰͎̖̦͎̦̻̫͉̝̗̝͚̎͌͑̾̿̊̉͆̉̏̅̔̓̈́̀͐̚͘ͅư̷̦̮͖͙̺̱̼̜̺̤͎̜͐͐̊̊̈͋̔̓̍͊̇̊̈́̈͑̐̎̿̑̋͋̀̅̓͛̚͜n̷̡̨͉̠̖̙͎̳̠̦̼̻̲̳̿̀̓̍͋̎͆̓̇̾̅͊̐͘͘̕!̷̡̨̧̢̡̡̼̹̭̝̝̭̫̫̥̰̤̪̦̤̼̖̖̳̰̲͙͕̖̬̳̪͖̹̮͐͐͊̈́̐͑͛̾̈͊̊͋͑̉͒̈̿̈̃̑͋͐́͊̀͝͠͝͠ + Ų̷̧̧̧̧̡̢̨̨̢̢̢̧̡̨̨̧̢̡̡̢̨̨̮̜͈̳̮͔̺͚̹͉͍̫̪͖͙͙̳͖͖̦̮̜̫̗̣̙̪͇̩̻̬̖̝̻̰͙̖̙̭̤͎̠͇̹̦̤̟̦͎̹̝̗̫͔̳̣̦͍̹͈̺̮͈͈̬̭̘͕̟͉̮̟͖̦̥͇̠̙̳̲̝̦͖̻̪̺̬̫͈͈̘͍͚͖̝̥̙͖̪͔̫̣̘͙͓̱̠̲̯̦͓͖͚͎͉͖̘̺͕͇̱̺̗̙̮̮̹̯̤̮̺͓̘̫͕̞̮͕̠̗͍̦̣̮͙͉͈̭̜̭̘̼̼̖̮̘̝͈̌̑͋͂̈́̐̄̂̆̊̈̅͆͋̔͗̍͒̆̐̒̽͑̋́͛͗̓̃̽͋̒͑̈́̕̕̚̕̕͜͜͝͠͝͠͝ͅͅņ̴̡̢̡̢̢̨̛̛̛̛̛̛̛̻̬̲̰̗̭͕̯͇̩̦̮̫̭̰̪͉̹̭͇̣̦͕̹̗̭̬͓͕͍̯͇͕̩̱̲͍̟̙͓̣̖̱͍̟͚͔̞̪͕̣̺̻̭̖̤̜͈̰̻̘̹̝̝̮̗͔̯̻̻͍͕̬͇͓̲̗̟̭̰̬̳͈̼̤̙̱̻̜͍̪̣̈̉̉͑̇̇͐̆̆̀̋̄̂̿͒͐͒͛̒̍͆̿͐͊̂̿̐̇̋̄̈̂̓̅̇̈́̾̒̔̍̈́͐̋͊̑͐͆̈́̿́̽͛́̊̏̓̾̇̈́̀̃͐̃̈́́͒̏̀͑̑̅̈́̇͂̓̐̒́̾̈́͗̋̅̀́͋̍͑͒̌̔̈́̆̂̉͌̈́̑̾̑̽̓̓̏̄͋̽̒̓͌̊̂̀͑̂͑̌͋̓͐̈̃̾̏̃̏͑̋̒̈̀̔͐̓̋̉̐̐͋͆̂̈́̒͊̏̓̔͌̈̾͗͑͂̾͆͑̂̍̂͗͆̄̊̐̏̈́͂̌͐̃̐͌̊̀͗̐͑̔͋͒̊̋̒̐̄͐̏̓͌̃̌͋̔̎̈̆̍̃̿́̇̉̀́̊̅̌̏̆̆͛̔͋̈̽̂͂̇̓́͛͗̔̍̃̈̽͑͐̿̽̉̓̎̔͗̊͂̽͘̕̚̕͘̕̕͘̚͘̚̕̚̚̚͘̚͜͜͝͝͠͝͝͠͝͝͝͝͝͝͝͝͝͝͠͝ͅͅͅi̴̧̡̢̡̧̢̢̢̧̡̧̢̡̹̤̗̭̭̭̪̳̮͉̦̪͉͈̦̗̣̼̻̜̰̳̯͕̩̘̙̯̼͉̖͕̰͓͚͙̠̫̞̰̰̪͖̹̬̥̣̞̯̳̘̙͖̪̗̼̹̝̣͕̯̺̱͉̻̖͓͙̘̗̖̠̫̥̻̱̖͇̬͇̹͕͍̗̻̝̻͕̫̱̻͈̫͕̜̼͎̮̘̫̮̭͉̜͔͈̻͕͍̠̞͔̪̳͕̰͈͖͇͍͇͎͕̙̟͔͔͇̭̥̠͇̖͎͙̖̫͚͇͕̮̳̯̟̺̺̪̪̙̣̥̰̜̺̥̭̦̤̲͓̮̦̹͙̼͓̼̮̙̮̠͎͍͖̇͐͐͐̿́͊̃̀͋̔͑̓͂͌͗̋̇̎͛͊̋̔̓̇̚͜ͅč̷̨̡̡̨̫̗̠̥̫̩͕͉͉͇̮͉̲͎̭̝̬͚̜̮̼̰̭̞̘̠̘̰̹͈̯̫̟͓͙̻̤̰͈̌͂̓͐̑̌̏̊͂͂̉͌̐̇̎͋̍̉̑̃̇̃̎̓͋͛̑͊̾͊̔̍̄͂͘͘͝͝͠ǫ̴̡̧̨̧̧̢̢̧̧̢̨̢̨̡̧̨̡̡̨̨̨̡̡̨̡̨̨̨̧̧̡̛̛̤͚̖̫̰̣̣͍̱̜͈̻̙̲̙͚͚̖͕̠̼̲͚̯͚̳̻͇̘̲̦͕̦̜̣̙̣̣̜̰̝͕̤̝̫̺̳͙̮̬̪̹̲͍̣̹̙̠̫̘̥̦̘̰̞̙̟̟̤͓̙͖̣͓͔͓̩̩͈̗̤͇̠̞̩̮̪̥̪̱͚͍̝͕͎̞͔̩̖̲͔͈̩̻̩͕̫͓̳̙͓̞̟͙͉͉̬̮̗͓̱̲̮̯͇͕̰̖͚̦͈̞̺̠̳͕̭̭̳͓̻̯̞̳͔͍̟̬̳̩͚͎̲̹͍͇͈̙̳̞̖̗̯͖̱͖̯̤̠̲͍̩͈̭̙͈̲̱̲̼̩̘̘̜͔̲̱̯͔̈́̈́̋̿̋̿͋͋͌̏̽̇͗͊͑̔͆͌̿̋͌̋͗̉̊̿̐̄̈́̈́̈̄̆̅̃́̄̅̍́̽͒̈́̽̄̌̇̓̈͂͆̊͆̿́͗͛̅̋́͆͑͛̆̔̍̀̍̃̎͒̀͋͛̽́̏̄̓̌͘͘͘̚̕̕͘͜͜͜͜͜͜͠͠͠͝͠͝͝ͅͅͅd̷̨̧̧̢̢̧̛̛̛̛̛̛̛͖̭̘̺͕̜̬̤̭̬̠̫̤͍͚̪̬̣͉̳̮̱͖̻̟͓̫̹͚̝̗̳̰̺͍͎͉̟̱̜̫͇̫̯̼̠̞̝̤͖̖̻͍͖̰̻͕̙͙͚͈̱̝͉͙̘̰͚̩̗̟͕̞̞̼̣͖̜̳̥̼͉̘͈̘̩͕̦̺̝̟̼͍̥̲̤̪̗̀͌̊̃̆̑̓̓̌͌̏̎͋̀͌͐̈́̀̂̍̒̅̓̏̓̈́̀̀͊̒̎̓́̒̔̉̀̍̿̒͛̍̍̅̇͑̆͒̓̌̑̏̏̈͊̈́́͌̀̃̆́̔̃̀͛̾̅́̿̀́̿̒͆́̍̂̀̿̆͑̊̉͆́̒̑̅̽̂̄͂̏̿̍̽̃͂̈́̀͌̒͗̅̉̎̓̐̀̌̿̓̈̓͛̽̄̉̑̄̊͂̀̽̔̇̍̀͂̇̈͊͐͗̽͐͊͐̑͘͘͘͘̕̚̕̕̚̚̕͘͘͜͜͜͜͜͝͝͝͝͠͠͝͠͝͠͠͝͝͠ͅͅͅę̴̨̡̨̡̢̛͈̳̞͈͇͕͙̪̩̼̩̗̲̳̹̯̖̙̱͔̺̪͇̜̼̍͌̆̅̽͛̏̑͊͒͊͌́̇̄͊́̏́̄̈͆̿͌̌̀̈̃͊̈̀̽͒͑̊́̍̑̃̒̐́̓̈̃̀͛̽̔̎̀̄̑͌̾͒̊̀̓̆̀̕̕͘͜͜͠͝͠͝ͅͅ ̴̨̡̢̢̢̢̛̛̛̛̛̛̛̛̼̻̬̪͎̖̭̯̤̥̭͚̖̖͚̳͍͎̻̰̯̗̭͔͎͇̖̮̻̯̰̯̦̗͔̺͔̩͈̫̣̪͕̜͇͓͓̅͐́̃̿̀̍͂̽̂͒̇̉̿̑̀̈̒̇͋͗̓͑̒̿͒̃̏̏̔͐̓͐̽͛̆̈͗̿͆́͌́̀̇̈́̓̄̾̇̈́̀͑̽̔͒̌͑̓́̀̈́̀͊̓̏̾̿̓͒̅͋̓̂͆͊̎̾̆̀̾̏̆̈̿̆͗̀̿͊̒̌̓́͆̈́͂̍͆͗͌̇̇͋̅̍̈́̊̽̈́̑́̅͐́̌̉͆͊͑̓̿͆̊͒̑̑̉̑̔̀̀́̐̓̽͂͌̾͑̌͑̓͒̀͗̈́̑̀̋͂͆̓̍̆́͛̈́̈́̀́̀͋͂͛̎̑̌͊̅͑̔̓͛͂̓̇̾́͌̓́̆͋̓̓͑̔̈́̎̍͊̃̋̃͌͛̓́̔͆̈͐͒̂̅̂̓͂̋̅̽̏̉̎̊̈̿̾̊̃͆̆͊͂̎͋̌͊͌̍̄̔͒̄͗̈́͒̇̕̕̕̕̕̚͘̚̕͘̕̚͘͘͘̕͘̚͘̕̚͝͝͝͠͝͠͝͝͝͝͝͠͠͝͝͝͠͠͝͝ͅͅį̴̛͕͍̠̩͎͇̳̪̱̖̝͙͉̩̩̯̜͕͓͕̀͂͛̃͑̉͐̏͑́̒̃̑̐̾̈͐͝ͅs̶̨̢̨̡̧̡̡̧̨̪̱̪̙̤̥̺̰͚̦̞̫̟̭̟̠̗̺̲̺̹͙͇͈̭̱̪͔̦̦̻̭͙̱̱̬̙̺̤̤̙͈̭͖̯͇̞͙͈̟͓̖̠͚̳̤̺̙̤͔̯͍͔͖̱͈͍̞͚̗̭̮̣͕̻̝̮̯͐̑̃̌̐̈́̌̈̔́͋̂͊̿̈́̉̄̆̎̃̏͑̈̑̔̋͐̂̽̈́̔͗̒̌́̓̉̕̕͜͜͠ͅͅ ̴̢̡̧̧̡̢̡̨̢̢̧̡̧̨̡̡̨̧̧̡̨̛̛̛̱͚̭̯̯̘͍͕͓̱̯̩̪̠͓̫͕̖̠̤̱͕̬̞̘̭̗͍͙͚͎̗̫̘̹̫͔̹̱̟̻̬̞͙͇͉͔͙͍̟͙͈̪̞̤̪͉̫̠̤̫̭̦͍̰̪͎̠̲̣̰̠͍̪̦̞̬̘̟̳̣̼̜̻̬̗͎͓͓̳̙̳̩͙̼̬͍̝͓̲̰̤͇͚͖̠̹͖͓̜̳̳̼͈͈̝̘̹̪̱̳̱͎̙̳̩͕̞̻͍͓̗̪̖̣͚̤͇͈̳͓̝̗͔͇̖̲͙̤͉̺̮͔̞̫̱̮̻͇̼̯̹͓̥̪̩̹̳̰͍͓̖̟̮͉͔̰͙̲͓͇͉̞͓̥̖̗̘̜͖̱̯͎̺͓̬͎͕̘̻̻̥̲̖̬̯̰̞̜̫̬̪̲͎̠̳̥̫̜̠͍̼̟͓͈̻͍͈̙̮̠̱̻̫̼̯̜̯͓́͂͊̽͑̾̃̽̈́̒̓̒́̑̽͗̃̏̏̿̅̃͑̒́͌̈́́̒͊͊̆́͒̒̓͌̊͆̿̉̈́̇̑̃̇̋̾̒̽̎̍́̕̚̕̕̕̕͘̚̚͜͜͝͠͝͠͠͝ͅͅͅͅͅͅs̸̨̨̨̢̡̢̨̡̢̢̨̧̡̧̡̧̡̧̧̛̯͕̦̪̹̦͓͓̮̹͈̩͎̗̻͍̪̩̮͖̺͕͉̲̖̹̹̻͈̗͎̮̬͔̦̹͔̞̳̙̤͙͈̗̪̥̦͉̯̮͓̰̙̝͇̦̤̳̣̦͎̬̬͈͖̙̺͉̥̮̖͕̗̗͓̥͔̥̬̘͉̠̝͕̥̦̙͉͎͚͔̖͍͓̖̩̳͚͔̟̰̝̳͖̲̬̗̹͈͙̳̘̠̱͇͎̗̞̳̯̣͖͎͇̮̞̗̻̞̱̪̳͓̣̱͙̩̼͍͖̭͓͇̗̫͔̗̘̤͖͈̦̭̻͓̤͚͍̜̝̯͍͓͖̥̳̮͓̦͕̦͕̱͉̗͙̫̞͔̪͍̭͕̄̊͒͌͛̅̑̅̂͒̂̈́̃̂̀́̈͋̑̋̃̊̇̈̄̽̃̆̑̔͆̂̍͑͛͊̇̒̍̂̏̋̓̂́͐͆̎̿́̑̚̕͘̕͘͘͜͜͜͜͜͜͜͝͝͝͝͝͝ͅͅu̴̢̧̨̡̨̡̨̢̢̧̨̧̡̧̨̢̢̡̢̡̨̨̢̡̡̧̢̨͈͔̟̯͇̻͙̬̟͖̘͎͈̘͙̬̰͉̟̠͉̻̯͖̼̪͎͓͚̟̞̺͖̞͕͚̗͇͔̩̼̪͔̺̯̹͍̮̗͍͚̻̙̹͙͉͈̙͔̜̬̙̺̥̬̜̩̜̟̘̪͍̤̤̪͈͈͖̲̥͇̣͈̥͖̩̞̬̟̺̻̩̝͉̮̜̖͖̺͉̺̱̖̗̰͕͓̼̱̥̠̖̫̱̖̝̤̭̲̭̖̙͎̫̰͈̲͈̣̪̣̳͓̝͚̘̪̞͖̩̮̗̱͈͉̰̻̻̠̞͙̭̰̪͙̝̰̞͖̩͖͇̩̺̗̬̦͙͉̬̜̱̰̱͓̪͙̮̝̼̙̻͔͎̱͖͙͓̣̼̩̰̗͖̱̞̼͇̙̦̹̯̖͇̫͕͍̒̀̂̿͆̊̔̐̿̔̀͆͂̅̽̽̋̊̈́̈́̌͂̿̀̌́̔̉̑́̓̒̃̿͛̓̋̆͆́̈́̆̍̔͊͗̏̆̈́̑͑̓̀̆͘͘̕͘͘̚͜͜͜͜͝͝͠͝ͅͅͅͅͅc̶̡̢̢̢̢̨̡̨̢̧̢̨̢̨̧̢̢̡͍̖͎̪͉̼̮̲̣̪̘̮̯͖͖̼̯͙̻̮͍̲̖̙͕͖̯̠̪̯̲̞̞̠̳͈͚̜̟̙̫͎̫̱̩͈͚͎̮̱̝̼͚̺͚͇̪̱̫͇̱͈̟̲͇͔̝̯͎̗̣̘̘̺͈̼̦̖̺̖͉̬̫̥̲̣̞͔̣̣͚̤͇̻̫͉̥̖̦̫̪̠͈̙̰͈̤̤͎͕͎̙͔̪̭̼̞̙͇͕͎͔̼̘̖̦͚͔͉̫͕͔̜̮̱͉̠͓̪͕̼̳̖͙͍̭̬̞̻̬͔͕̑̐̄̆́͊͂͗̒̐̅̾͗̉̕͜͜͜͝ͅḫ̸̢̧̧̧̧̡̢̢̢̫̝̬̣̺̠̯̮͚̦̩͍̻̯̪̪̝̩̹̠̘̤͓͇̪͍̲̠͍̝͉̭̲̘̼͙͍̜̙̣̫̪̬͓̻̤͚͖͛̈̀̌͜͜͝ͅ ̴̨̢̡̡̨̨̡̢̧̡̨̡̡̛̛̛̛̹̭̗̖̹̰̼̗̳̹̯͔͚̻͚̙̹̰̪̺̩͈̳͉̼̗̝̳͖̞̯̠̭̯͎͓͎̘͉̺͇̬͇̯̜̯͓̳̞͚͍̭̯̦̺̳̘̰̲̲̜͓͔̼̺͍̟̠̙̱̞̲͉̣̮̭̗͈͕͚͚̣͓̻͍̩̣̻̲̳̹̲̫̮͚̲͍͎̰̮̮̯͖̰̥̝̮̞͍͇̹̹̫̞͔̫̭̥͉͉̱̯̻̥͈̑̔͊̆̌̇̇̌͆̍̓͛̅̂̊̂̃̌̋́͑̐̄̃̾̔͗̿͛̊̀̉͐̋͑̇̿̃̏̈́̏̔́̌̿̈́̃̈́́̈́̄̃͌̆̅̓̎̽́̑͊̑̈͛̌́̈́̿̿̐͐͗̾͛̉̐͊̏̉̏̉̏̌͋͊̍͒͐̑́̽̈́͊́̃̂͂̓̂͌̓̉̏͛̍̍̄̐̃͐̐͒̉̏̈̐̒̔̈́͒͆̈́̾̄͛̋̔̿̅́̃͌̎͐̀̿̊̍̿̊͌̚̚̚͘͘͘̚̕̕͜͜͜͜͠͠͠͠͝͝͝͝͝ͅͅf̶̨̢̡̢̢̢̢̡̨̡̢̧̢̨̨̧̢̨̨̨̨̢̧̢̢̦͇̼̹̫͔̬͔͎͇̱͉̜̤̟̩̖̯̞̱̗̺͎͖̙͖̱̻̩̮̯̲̝̥̟̠̰̘̮̖̹̲͖̖̪͖̲̖̫͈̞̫̣̗̖̝̹̟̙͓͙͖̺͉͚̪̣͓̮͉̦̪͕̗͈̩̗̤͈̮̱͈̙͉͙̭̼̺̻͙̟̥̬̤̤̺̼͚̣̗͙͇̬̭͇̖͇̖̣͎̜̭͙̠̤̫̫͙͕̺̱͙̟̤̦͓̦͍̜̖͉̥̤͓̹͉̮͇̙͍̙̠̳̲͉̦̮̣͇̠͕͙̫̗̲̘̞͎͕̗̹̞̟̺̥̤̥̪̝̹͎͚̦͍͓͓̘̟̼͙̯̠̮̖̲̲̪̜̖̝̙͉͚̼̳̘̘̻͕̘͓̜̯͙͕͉̬̲̲̼̥̭̰͕̣̻̬̫̬̼̖͈̺̞̹̘̭͈̼̣̮̘̜̗̦̬͓͇͉͍̮͕̲͙̗͍̝̝͉͔̻͔̭͇̟̞̜̘̗̥̲̉̎̀͛̈͊̋̾͋͑̾̃̽̽̓͆̉̃́̈́́͐͂͜͜͜͜͜͝͝͝ͅͅͅͅͅͅͅͅư̸̡̧̢̨̨̢̨̡̧̛̛̛͕̺̘̗͕̪̟̞̳͖̲̻̻̦̤̤͔̬͍̦͈̬͇̯̭͉̬͚̙͙̼͎̩̠̺͙̫̜͙̜̮̬͎̩̼̣͔̦̘̫̞͖͕̻̩̻̱̫̻͉̬͚̘̫̟̣̱̮̞͍̖̰̫͙̭̳͓̦̯͙̣̫̼͓̱͚̩̺͉̭̮̠̮̲̭̙̳͇͙͉̺̖͙̼͔͇̤͙͈̭̟̹̰͎̝͓̗̘̼̤̞̪̱̜̭̖̣͔̹̹̠̝̖̪͉͕̌̍̋́̈̾̓͆͗̑͆̄̓̏̃͒̄̎̐͌̍̐̓̎͊͂̉̍͆́͆̋̒̆̌͗̓͗͋̃̈́̈́̌̏͒̌̏͗̓̿͌̄̃̎̿͋͌̒̏̀͐͆̋̿̕̕̕͜͝͝͠͝͠͝͠͠ͅͅͅǹ̵̨̨̧̨̧̢̡̧̨̡̨̨̨̢̢̨̡̧̨̡̢̡̢̳̙̻̘͔͈̹͓̺̩̦̦̻̥͓͚̟͚͕͔͉̺͈̝͓̯͔͕͍͕̖̬̟̝̰̩̩̰͍̮̥̦̝̲̜̬̝̩͔̺͍͍̻̺̱͎̫͍̥͉̯̳͉͓̟͈̳̤͚̘̮̱̭̞̻̦̗̹̠͖̫̫̤̹̗̺͔̖̦̰̮̬̱̖̹͉̩̬̭̖̦͚̻̹̖̮͙͎̣͓̳̪̳͍͍̗̺̯͈̙͓̣̗̦͙̺͚̰̪͍̤̻̥̣̬̻̜̘̘͇̟̜̝̼̩͇̣̤̳̹̩̜͖̜̤̩̺̼̻̩̟̝̩̼̩̲͖̟̣̝͇̜͙̗̞̦̘͙̳͚̺̫̰̜̖͉̟̗͖͕̬̦̥̮̜̙̬̺͓̠̯̤̮̼̜͙͉̰̙̗͕͍͚̦͕̮͉͈̙̪͓̳̯̟̱̦̹̭̺̼͉̯̯͖̖̘̮̞̼͎̪͖͙̭͍̣̯͚̾̽͒͒̊̈̃̐͐͒̉̋̎͑͆̈̎͌̃̒͊̔͑̄̿͑̃̓͐͆̿̿̾̃̏̀̚̕͜͜͜͜͝͝ͅͅͅͅͅͅ!̶̡̢̨̨̨̡̡̨̡̨̨̨̢̢̧̢̢̢̢̡̨̛̛̛̛̭̲̺͔̰͓͓͈͍̖̮̭̤̩͚̭̩̼̫͈̹̙̭͚͇̗͖̙̙̼̰͔̭͓͎̯͓̯̜͚̗̝͔͉̼̠̹͚͇̩̬̬͓͚̭͎̠̖͖̬̞͉͎͎͉̘̩̬̺̳̖̻̘͎̹̹͕̟̗͉̰͖̮͔͇̘̞̭̮͈̲̪͉͈̻̹̻̣͖̠̙͎͍͉̤̭̳̞̳̝̠̳̺͈̺͍̮̭̺͚̹̫͓͎̱̹̰͓̺̘͓͇̙͙̱͉̟̙͖̭̙̳̰̮̻̬̖̲̹͖̝̬̳͉̗̫̮̮̹͚̹͕̤͓̺̮̜̜̻̩̦̪͓͎̬͍̗̺̝̗̣̘̖͇̬̻͍̮̜͚̱͍̗͔͎͙̗̳̞̩̩͇͕͈͙̬̻̮͕̤̲͚̘̥̙̻̲̠̦͍̩̺̩̭͓̘͙͎̳̝̞͚̄̍̇̎̊̉̃̄͐͆̒͗̈́͛̊͗̉̑̅̒̈́̀̈̈́̌̊̂̃̍̈́̊͒̂̆̎̈́͌́̆͑͆̈́̐͗͋̾̇̂̍̃͑̏͐̍̐̌̑̃̄̓̓̃̽̑̂̂͌͊͆͋̉̇̓̽͛̂̅̀̀̌̎̌̈́̐̽͊͒̀͛̏̌͐̈́̽͒̉̇͒͑͋͛́̽͋̋̊̋̒̈́͛̉̒͊́̓̋͂̋̓̅̃͗́́̾͋͛̃́́̋̎̌̓̄̽̔̌́̅̎̓̎͆́̋͊͆͒͒̈́̂̆͐͋̐̿͆̿̾̿̅͑́̿̏̌̅̌͊͐̔͌̽͆͒͋̈̇̀̈́́͑͐̍̏̀̓̀̓̐͑̓͒̉̂̇͐͐͌̀̈́̅̓͊̓͌̔͂̀͗̓͑́̈́̀̉̀̓̇͐̒͛̈́̀̉̏̐̃̈͂̋̋̀͛̈̌̎̏̐͛̑͊͗̐͘̚͘͘̚͘̚̕͘͘͘͘͘̚̕͘̕̕͘̚͜͜͜͜͜͜͜͜͜͝͠͠͠͝͠͝͝͠͝͝͝͠͝͝͠͝͠͝͝͝͠͝ͅͅͅͅͅͅ + + T̸h̴e̶ ̵p̷o̷w̶e̵r̷f̸u̷l̷ ̵p̴r̷o̷g̶r̷a̸m̸m̶i̸n̴g̴ ̷l̶a̴n̸g̵u̵a̶g̸e̶ ̸t̶h̴a̵t̵ ̶i̷s̶ ̵a̷l̴s̸o̷ ̵e̵a̷s̷y̴ ̵t̵o̷ ̷l̷e̶a̵r̴n̸.̵ + """# + +let shortSample = #""" + Swift 👨‍👨‍👧‍👧 简洁 c̴̭̈͘ǫ̷̯͋̊d̸͖̩̈̈́ḛ̴́ 🇺🇸🇨🇦🇺🇸 코드 + """# + +let sampleString2 = + #""" + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + The powerful programming language that is also easy to learn. + Swift is a powerful and intuitive programming language for iOS, iPadOS, macOS, \# + tvOS, and watchOS. Writing Swift code is interactive and fun, the syntax is \# + concise yet expressive, and Swift includes modern features developers love. \# + Swift code is safe by design and produces software that runs lightning-fast. + + """# diff --git a/Tests/RopeModuleTests/TestBigString.swift b/Tests/RopeModuleTests/TestBigString.swift new file mode 100644 index 000000000..fd0f6306e --- /dev/null +++ b/Tests/RopeModuleTests/TestBigString.swift @@ -0,0 +1,1035 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 swift(>=5.8) +import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else +import _CollectionsTestSupport +import _RopeModule +#endif + +@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) +class TestBigString: CollectionTestCase { + override var isAvailable: Bool { isRunningOnSwiftStdlib5_8 } + + override class func setUp() { + // Turn off output buffering. + setbuf(stdout, nil) + setbuf(stderr, nil) + super.setUp() + } + + override func setUp() { + print("Global seed: \(RepeatableRandomNumberGenerator.globalSeed)") + super.setUp() + } + + func test_capacity() { + let min = BigString._minimumCapacity + let max = BigString._maximumCapacity + + XCTAssertLessThanOrEqual(min, max) +#if !DEBUG // Debug builds have smaller nodes + // We want big strings to hold at least as many UTF-8 code units as a regular String. + XCTAssertGreaterThanOrEqual(min, 1 << 48) +#endif + } + + func test_empty() { + let s = BigString() + s._invariantCheck() + XCTAssertEqual(s.count, 0) + XCTAssertEqual(s.unicodeScalars.count, 0) + XCTAssertEqual(s.utf16.count, 0) + XCTAssertEqual(s.utf8.count, 0) + XCTAssertTrue(s.isEmpty) + XCTAssertEqual(s.startIndex, s.endIndex) + XCTAssertEqual(s.startIndex.utf8Offset, 0) + + XCTAssertEqual(String(s), "") + XCTAssertEqual(String(s[...]), "") + } + + func test_Equatable() { + let a: BigString = "Cafe\u{301}" + let b: BigString = "Café" + XCTAssertEqual(a, b) + XCTAssertNotEqual(a.unicodeScalars, b.unicodeScalars) + XCTAssertNotEqual(a.utf8, b.utf8) + XCTAssertNotEqual(a.utf16, b.utf16) + } + + func test_descriptions() { + let s: BigString = "Café" + XCTAssertEqual(s.description, "Café") + XCTAssertEqual(s.unicodeScalars.description, "Café") + #if false // Should we? + XCTAssertEqual(s.utf8.description, "<43 61 66 C3 A9>") + XCTAssertEqual(s.utf16.description, "<0043 0061 0066 00E9>") + #endif + } + + func test_string_conversion() { + let big = BigString(sampleString) + + big._invariantCheck() + XCTAssertEqual(big.count, sampleString.count) + XCTAssertEqual(big.unicodeScalars.count, sampleString.unicodeScalars.count) + XCTAssertEqual(big.utf16.count, sampleString.utf16.count) + XCTAssertEqual(big.utf8.count, sampleString.utf8.count) + + let flat = String(big) + XCTAssertEqual(flat, sampleString) + } + + func testUTF8View() { + let str = BigString(shortSample) + checkBidirectionalCollection(str.utf8, expectedContents: shortSample.utf8) + } + + func testUTF16View() { + let str = BigString(shortSample) + checkBidirectionalCollection(str.utf16, expectedContents: shortSample.utf16) + } + + func testUnicodeScalarView() { + let str = BigString(shortSample) + checkBidirectionalCollection(str.unicodeScalars, expectedContents: shortSample.unicodeScalars) + } + + func testCharacterView() { + let str = BigString(shortSample) + checkBidirectionalCollection(str, expectedContents: shortSample) + } + + func testHashable_Characters() { + let classes: [[BigString]] = [ + ["Cafe\u{301}", "Café"], + ["Foo\u{301}\u{327}", "Foo\u{327}\u{301}"], + ["Foo;bar", "Foo\u{37e}bar"], + ] + checkHashable(equivalenceClasses: classes) + } + + func testHashable_Scalars() { + let classes: [BigString] = [ + "Cafe\u{301}", + "Café", + "Foo\u{301}\u{327}", + "Foo\u{327}\u{301}", + "Foo;bar", + "Foo\u{37e}bar", + ] + checkHashable(equivalenceClasses: classes.map { [$0.unicodeScalars] }) + checkHashable(equivalenceClasses: classes.map { [$0.utf8] }) + checkHashable(equivalenceClasses: classes.map { [$0.utf16] }) + } + + @discardableResult + func checkCharacterIndices( + _ flat: String, + _ big: BigString, + file: StaticString = #file, line: UInt = #line + ) -> (flat: [String.Index], big: [BigString.Index]) { + // Check iterators + var it1 = flat.makeIterator() + var it2 = big.makeIterator() + while true { + let a = it1.next() + let b = it2.next() + guard a == b else { + XCTAssertEqual(a, b) + break + } + if a == nil { break } + } + + // Check indices + let indices1 = Array(flat.indices) + [flat.endIndex] + let indices2 = Array( + sequence(first: big.startIndex) { + $0 == big.endIndex ? nil : big.index(after: $0) + } + ) + + XCTAssertEqual(indices2.count, indices1.count, file: file, line: line) + + let c = min(indices1.count, indices2.count) + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + guard i1 < flat.endIndex, i2 < big.endIndex else { continue } + let c1 = flat[i1] + let c2 = big[i2] + XCTAssertEqual(c1, c2, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + let d1 = flat.utf8.distance(from: flat.startIndex, to: i1) + let d2 = big.utf8.distance(from: big.startIndex, to: i2) + XCTAssertEqual(d2, d1, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + return (indices1, indices2) + } + + @discardableResult + func checkScalarIndices( + _ flat: String, + _ big: BigString, + file: StaticString = #file, line: UInt = #line + ) -> (flat: [String.Index], big: [BigString.Index]) { + // Check iterators + var it1 = flat.unicodeScalars.makeIterator() + var it2 = big.unicodeScalars.makeIterator() + while true { + let a = it1.next() + let b = it2.next() + guard a == b else { + XCTAssertEqual(a, b) + break + } + if a == nil { break } + } + + // Check indices + let indices1 = Array(flat.unicodeScalars.indices) + [flat.unicodeScalars.endIndex] + let indices2 = Array( + sequence(first: big.startIndex) { + $0 == big.endIndex ? nil : big.unicodeScalars.index(after: $0) + } + ) + + XCTAssertEqual(indices2.count, indices1.count, file: file, line: line) + + let c = min(indices1.count, indices2.count) + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + guard i1 < flat.endIndex, i2 < big.endIndex else { continue } + let c1 = flat.unicodeScalars[i1] + let c2 = big.unicodeScalars[i2] + XCTAssertEqual(c1, c2, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + let d1 = flat.utf8.distance(from: flat.startIndex, to: i1) + let d2 = big.utf8.distance(from: big.startIndex, to: i2) + XCTAssertEqual(d2, d1, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + return (indices1, indices2) + } + + @discardableResult + func checkUTF8Indices( + _ flat: String, + _ big: BigString, + file: StaticString = #file, line: UInt = #line + ) -> (flat: [String.Index], big: [BigString.Index]) { + // Check iterators + var it1 = flat.utf8.makeIterator() + var it2 = big.utf8.makeIterator() + while true { + let a = it1.next() + let b = it2.next() + guard a == b else { + XCTAssertEqual(a, b) + break + } + if a == nil { break } + } + + // Check indices + let indices1 = Array(flat.utf8.indices) + [flat.utf8.endIndex] + let indices2 = Array( + sequence(first: big.startIndex) { + $0 == big.endIndex ? nil : big.utf8.index(after: $0) + } + ) + + XCTAssertEqual(indices2.count, indices1.count, file: file, line: line) + + let c = min(indices1.count, indices2.count) + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + guard i1 < flat.endIndex, i2 < big.endIndex else { continue } + let c1 = flat.utf8[i1] + let c2 = big.utf8[i2] + XCTAssertEqual(c1, c2, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + let d1 = flat.utf8.distance(from: flat.startIndex, to: i1) + let d2 = big.utf8.distance(from: big.startIndex, to: i2) + XCTAssertEqual(d2, d1, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + return (indices1, indices2) + } + + @discardableResult + func checkUTF16Indices( + _ flat: String, + _ big: BigString, + file: StaticString = #file, line: UInt = #line + ) -> (flat: [String.Index], big: [BigString.Index]) { + // Check iterators + var it1 = flat.utf16.makeIterator() + var it2 = big.utf16.makeIterator() + while true { + let a = it1.next() + let b = it2.next() + guard a == b else { + XCTAssertEqual(a, b) + break + } + if a == nil { break } + } + + // Check indices + let indices1 = Array(flat.utf16.indices) + [flat.utf16.endIndex] + let indices2 = Array( + sequence(first: big.startIndex) { + $0 == big.endIndex ? nil : big.utf16.index(after: $0) + } + ) + + XCTAssertEqual(indices2.count, indices1.count, file: file, line: line) + + let c = min(indices1.count, indices2.count) + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + guard i1 < flat.endIndex, i2 < big.endIndex else { continue } + let c1 = flat.utf16[i1] + let c2 = big.utf16[i2] + XCTAssertEqual(c1, c2, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + + for i in 0 ..< c { + let i1 = indices1[i] + let i2 = indices2[i] + let d1 = flat.utf16.distance(from: flat.startIndex, to: i1) + let d2 = big.utf16.distance(from: big.startIndex, to: i2) + XCTAssertEqual(d2, d1, "i: \(i), i1: \(i1._description), i2: \(i2)", file: file, line: line) + } + return (indices1, indices2) + } + + func test_iterator_character() { + let small: BigString = "Cafe\u{301} 👩‍👩‍👧" + var it = small.makeIterator() + XCTAssertEqual(it.next(), "C") + XCTAssertEqual(it.next(), "a") + XCTAssertEqual(it.next(), "f") + XCTAssertEqual(it.next(), "e\u{301}") + XCTAssertEqual(it.next(), " ") + XCTAssertEqual(it.next(), "👩‍👩‍👧") + XCTAssertNil(it.next()) + + let flat = sampleString + let big = BigString(flat) + + let c1 = Array(flat) + let c2 = Array(big) + XCTAssertEqual(c1, c2) + } + + func test_iterator_scalar() { + let small: BigString = "Cafe\u{301} 👩‍👩‍👧" + var it = small.unicodeScalars.makeIterator() + XCTAssertEqual(it.next(), "C") + XCTAssertEqual(it.next(), "a") + XCTAssertEqual(it.next(), "f") + XCTAssertEqual(it.next(), "e") + XCTAssertEqual(it.next(), "\u{301}") + XCTAssertEqual(it.next(), " ") + XCTAssertEqual(it.next(), "👩") + XCTAssertEqual(it.next(), "\u{200D}") + XCTAssertEqual(it.next(), "👩") + XCTAssertEqual(it.next(), "\u{200D}") + XCTAssertEqual(it.next(), "👧") + XCTAssertNil(it.next()) + + let flat = sampleString + let big = BigString(flat) + + let c1 = Array(flat.unicodeScalars) + let c2 = Array(big.unicodeScalars) + XCTAssertEqual(c1, c2) + } + + func test_iterator_utf8() { + let flat = sampleString + let big = BigString(flat) + + let c1 = Array(flat.utf8) + let c2 = Array(big.utf8) + XCTAssertEqual(c1, c2) + } + + func test_iterator_utf16() { + let flat = sampleString + let big = BigString(flat) + + let c1 = Array(flat.utf16) + let c2 = Array(big.utf16) + XCTAssertEqual(c1, c2) + } + + func test_indices_character() { + let flat = sampleString + let big = BigString(flat) + + let (indices1, indices2) = checkCharacterIndices(flat, big) + + let c = min(indices1.count, indices2.count) + for i in randomStride(from: 0, to: c, by: 5, seed: 0) { + for j in randomStride(from: i, to: c, by: 5, seed: i) { + let i1 = indices1[i] + let j1 = indices1[j] + let a = String(sampleString[i1 ..< j1]) + + let i2 = indices2[i] + let j2 = big.index(i2, offsetBy: j - i) + XCTAssertEqual(big.index(roundingDown: i2), i2) + XCTAssertEqual(big.index(roundingDown: j2), j2) + let b = String(big[i2 ..< j2]) + XCTAssertEqual(b, a) + } + } + } + + func test_indices_scalar() { + let flat = sampleString + let big = BigString(flat) + + let (indices1, indices2) = checkScalarIndices(flat, big) + + let c = min(indices1.count, indices2.count) + for i in randomStride(from: 0, to: c, by: 20, seed: 0) { + for j in randomStride(from: i, to: c, by: 20, seed: i) { + let a = String(sampleString.unicodeScalars[indices1[i] ..< indices1[j]]) + + let i2 = indices2[i] + let j2 = big.unicodeScalars.index(i2, offsetBy: j - i) + XCTAssertEqual(big.unicodeScalars.index(roundingDown: i2), i2) + XCTAssertEqual(big.unicodeScalars.index(roundingDown: j2), j2) + let slice = big.unicodeScalars[i2 ..< j2] + let b = String(slice) + XCTAssertEqual(b, a) + } + } + } + + func test_indices_utf16() { + let flat = sampleString + let big = BigString(flat) + + let (indices1, indices2) = checkUTF16Indices(flat, big) + + let c = min(indices1.count, indices2.count) + for i in randomStride(from: 0, to: c, by: 20, seed: 0) { + for j in randomStride(from: i, to: c, by: 20, seed: i) { + let a = sampleString.utf16[indices1[i] ..< indices1[j]] + + let i2 = indices2[i] + let j2 = big.utf16.index(i2, offsetBy: j - i) + XCTAssertEqual(big.utf16.index(roundingDown: i2), i2) + XCTAssertEqual(big.utf16.index(roundingDown: j2), j2) + let b = big.utf16[i2 ..< j2] + XCTAssertEqual(b.first, a.first) + XCTAssertEqual(b.last, a.last) + } + } + } + + func test_indices_utf8() { + let flat = sampleString + let big = BigString(flat) + + let (indices1, indices2) = checkUTF8Indices(flat, big) + + let c = min(indices1.count, indices2.count) + for i in randomStride(from: 0, to: c, by: 40, seed: 0) { + for j in randomStride(from: i, to: c, by: 40, seed: i) { + let a = sampleString.utf8[indices1[i] ..< indices1[j]] + + let i2 = indices2[i] + let j2 = big.utf8.index(i2, offsetBy: j - i) + XCTAssertEqual(big.utf8.index(roundingDown: i2), i2) + XCTAssertEqual(big.utf8.index(roundingDown: j2), j2) + let b = big.utf8[i2 ..< j2] + XCTAssertEqual(b.first, a.first) + XCTAssertEqual(b.last, a.last) + } + } + } + + func test_append_string() { + let flat = sampleString + let ref = BigString(flat) + for stride in [1, 2, 4, 8, 16, 32, 64, 128, 250, 1000, 10000, 20000] { + var big: BigString = "" + var i = flat.startIndex + while i < flat.endIndex { + let j = flat.unicodeScalars.index(i, offsetBy: stride, limitedBy: flat.endIndex) ?? flat.endIndex + let next = String(flat[i ..< j]) + big.append(contentsOf: next) + //big.invariantCheck() + //XCTAssertEqual(String(big)[...], s[.. [(i: Int, str: String)] { + var pieces: [(i: Int, str: String)] = [] + var c = 0 + var i = str.startIndex + while i < str.endIndex { + let j = str.unicodeScalars.index(i, offsetBy: stride, limitedBy: str.endIndex) ?? str.endIndex + let next = String(str[i ..< j]) + pieces.append((c, next)) + c += 1 + i = j + } + return pieces + } + + func test_insert_string() { + let flat = sampleString + let ref = BigString(flat) + for stride in [1, 2, 4, 8, 16, 32, 64, 128, 250, 1000, 10000, 20000] { + print("Stride: \(stride)") + var pieces = pieces(of: flat, by: stride) + var rng = RepeatableRandomNumberGenerator(seed: 0) + pieces.shuffle(using: &rng) + + var big: BigString = "" + var smol = "" + for i in pieces.indices { + let piece = pieces[i] + let utf8Offset = pieces[.., + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + var next = str.index(after: current) + for i in indices { + if i >= next { + current = next + next = str.index(after: current) + } + let j = str.index(roundingDown: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + XCTAssertEqual(str[i], str[j], "i: \(i)", file: file, line: line) + } + XCTAssertEqual(next, str.endIndex, "end", file: file, line: line) + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.index(roundingDown: str.endIndex), str.endIndex) + + } + + func testCharacterIndexRoundingUp() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + for i in indices { + let j = str.index(roundingUp: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + while i >= current { + current = str.index(after: current) + } + } + XCTAssertEqual(current, str.endIndex, "end", file: file, line: line) + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.index(roundingDown: str.endIndex), str.endIndex) + + } + + func testUnicodeScalarIndexRoundingDown() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + var next = str.unicodeScalars.index(after: current) + for i in indices { + while i >= next { + current = next + next = str.unicodeScalars.index(after: current) + } + let j = str.unicodeScalars.index(roundingDown: i) + XCTAssertEqual(j, current, "\(i)", file: file, line: line) + XCTAssertEqual(str.unicodeScalars[i], str.unicodeScalars[j], "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.unicodeScalars.index(roundingDown: str.endIndex), str.endIndex) + } + + func testUnicodeScalarIndexRoundingUp() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + for i in indices { + while i > current { + current = str.unicodeScalars.index(after: current) + } + let j = str.unicodeScalars.index(roundingUp: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.unicodeScalars.index(roundingDown: str.endIndex), str.endIndex) + } + + func testUTF8IndexRoundingDown() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + var next = str.utf8.index(after: current) + for i in indices { + while i >= next { + current = next + next = str.utf8.index(after: current) + } + let j = str.utf8.index(roundingDown: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + XCTAssertEqual(str.utf8[i], str.utf8[j], "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.utf8.index(roundingDown: str.endIndex), str.endIndex) + } + + func testUTF8IndexRoundingUp() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + for i in indices { + while i > current { + current = str.utf8.index(after: current) + } + let j = str.utf8.index(roundingUp: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.utf8.index(roundingDown: str.endIndex), str.endIndex) + } + + func testUTF16IndexRoundingDown() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + // Note: UTF-16 index rounding is not rounding in the usual sense -- it rounds UTF-8 indices + // down to the nearest scalar boundary, not the nearest UTF-16 index. This is because + // UTF-16 indices addressing trailing surrogates are ordered below UTF-8 continuation bytes, + // but this rounds those down to the scalar. + var next = str.unicodeScalars.index(after: current) // Note: intentionally not utf16 + for i in indices { + while i >= next { + current = next + next = str.unicodeScalars.index(after: current) // Note: intentionally not utf16 + } + let j = str.utf16.index(roundingDown: i) + if UTF16.isTrailSurrogate(str.utf16[i]) { + XCTAssertEqual(j, i, "i: \(i)", file: file, line: line) + } else { + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + } + XCTAssertEqual(str.utf16[i], str.utf16[j], "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.utf16.index(roundingDown: str.endIndex), str.endIndex) + } + + func testUTF1t6IndexRoundingUp() { + let ref = sampleString + let str = BigString(ref) + + func check( + _ indices: some Sequence, + file: StaticString = #file, + line: UInt = #line + ) { + var current = str.startIndex + for i in indices { + while i > current { + current = str.utf8.index(after: current) + } + let j = str.utf8.index(roundingUp: i) + XCTAssertEqual(j, current, "i: \(i)", file: file, line: line) + } + } + + check(str.utf8.indices) + check(str.utf16.indices) + check(str.unicodeScalars.indices) + check(str.indices) + XCTAssertEqual(str.utf8.index(roundingDown: str.endIndex), str.endIndex) + } + + func testSubstringMutationIndexRounding() { + let b1: BigString = "Foobar" + var s1 = b1.suffix(3) + XCTAssertEqual(s1, "bar") + s1.insert("\u{308}", at: s1.startIndex) // Combining diaresis + XCTAssertEqual(s1.base, "Foöbar") + XCTAssertEqual(s1, "öbar") + + let b2: BigString = "Foo👩👧bar" // WOMAN, GIRL + var s2: BigSubstring = b2.prefix(4) + XCTAssertEqual(s2, "Foo👩") + s2.append("\u{200d}") // ZWJ + XCTAssertEqual(s2, "Foo") + XCTAssertEqual(s2.base, "Foo👩‍👧bar") // family with mother and daughter + + let b3: BigString = "Foo🇺🇸🇨🇦🇺🇸🇨🇦bar" // Regional indicators "USCAUSCA" + var s3: BigSubstring = b3.prefix(6) + XCTAssertEqual(s3, "Foo🇺🇸🇨🇦🇺🇸") + s3.insert("\u{1f1ed}", at: s3.index(s3.startIndex, offsetBy: 3)) // Regional indicator "H" + XCTAssertEqual(s3, "Foo🇭🇺🇸🇨🇦🇺") // Regional indicators "HUSCAUSCA" + XCTAssertEqual(s3.base, "Foo🇭🇺🇸🇨🇦🇺🇸🇨\u{1f1e6}bar") + } + + func test_unicodeScalars_mutations() { + let b1: BigString = "Foo👩👧bar" // WOMAN, GIRL + var s1 = b1.prefix(4) + XCTAssertEqual(s1, "Foo👩") + + // Append a ZWJ at the end of the substring via its Unicode scalars view. + func mutate(_ value: inout T, by body: (inout T) -> Void) { + body(&value) + } + mutate(&s1.unicodeScalars) { view in + view.append("\u{200d}") // ZWJ + XCTAssertEqual(view, "Foo👩\u{200d}") + } + + XCTAssertEqual(s1, "Foo") + XCTAssertEqual(s1.base, "Foo👩‍👧bar") + } + + func test_ExpressibleByStringLiteral_and_CustomStringConvertible() { + let a: BigString = "foobar" + XCTAssertEqual(a.description, "foobar") + XCTAssertEqual(a.debugDescription, "\"foobar\"") + + let b: BigSubstring = "foobar" + XCTAssertEqual(b.description, "foobar") + XCTAssertEqual(b.debugDescription, "\"foobar\"") + + let c: BigString.UnicodeScalarView = "foobar" + XCTAssertEqual(c.description, "foobar") + XCTAssertEqual(c.debugDescription, "\"foobar\"") + + let d: BigSubstring.UnicodeScalarView = "foobar" + XCTAssertEqual(d.description, "foobar") + XCTAssertEqual(d.debugDescription, "\"foobar\"") + } + + func testCharacterwiseEquality() { + let cafe1: BigString = "Cafe\u{301}" + let cafe2: BigString = "Café" + XCTAssertEqual(cafe1, cafe2) + XCTAssertNotEqual(cafe1.unicodeScalars, cafe2.unicodeScalars) + XCTAssertNotEqual(cafe1.utf8, cafe2.utf8) + XCTAssertNotEqual(cafe1.utf16, cafe2.utf16) + } +} +#endif diff --git a/Tests/RopeModuleTests/TestRope.swift b/Tests/RopeModuleTests/TestRope.swift new file mode 100644 index 000000000..901bfe41d --- /dev/null +++ b/Tests/RopeModuleTests/TestRope.swift @@ -0,0 +1,576 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else +import _RopeModule +import _CollectionsTestSupport +#endif + +struct Chunk: RopeElement, Equatable, CustomStringConvertible { + var length: Int + var value: Int + + struct Summary: RopeSummary, Comparable { + var length: Int + + init(_ length: Int) { + self.length = length + } + + static var zero: Self { Self(0) } + + var isZero: Bool { length == 0 } + + mutating func add(_ other: Self) { + length += other.length + } + + mutating func subtract(_ other: Self) { + length -= other.length + } + + static func ==(left: Self, right: Self) -> Bool { left.length == right.length } + static func <(left: Self, right: Self) -> Bool { left.length < right.length } + + static var maxNodeSize: Int { 6 } + static var nodeSizeBitWidth: Int { 3 } + } + + struct Metric: RopeMetric { + typealias Element = Chunk + + func size(of summary: Chunk.Summary) -> Int { + summary.length + } + + func index(at offset: Int, in element: Chunk) -> Int { + precondition(offset >= 0 && offset <= element.length) + return offset + } + } + + init(length: Int, value: Int) { + self.length = length + self.value = value + } + + var description: String { + "\(value)*\(length)" + } + + var summary: Summary { + Summary(length) + } + + var isEmpty: Bool { length == 0 } + var isUndersized: Bool { isEmpty } + + func invariantCheck() {} + + mutating func rebalance(prevNeighbor left: inout Chunk) -> Bool { + // Fully merge neighbors that have the same value + if left.value == self.value { + self.length += left.length + left.length = 0 + return true + } + if left.isEmpty { return true } + guard self.isEmpty else { return false } + swap(&self, &left) + return true + } + + mutating func rebalance(nextNeighbor right: inout Chunk) -> Bool { + // Fully merge neighbors that have the same value + if self.value == right.value { + self.length += right.length + right.length = 0 + return true + } + if right.isEmpty { return true } + guard self.isEmpty else { return false } + swap(&self, &right) + return true + } + + typealias Index = Int + + mutating func split(at index: Int) -> Chunk { + precondition(index >= 0 && index <= length) + let tail = Chunk(length: length - index, value: value) + self.length = index + return tail + } +} + +class TestRope: XCTestCase { + override func setUp() { + print("Global seed: \(RepeatableRandomNumberGenerator.globalSeed)") + } + + func test_empty() { + let empty = Rope() + empty._invariantCheck() + XCTAssertTrue(empty.isEmpty) + XCTAssertEqual(empty.count, 0) + XCTAssertTrue(empty.summary.isZero) + XCTAssertEqual(empty.startIndex, empty.endIndex) + } + + func test_build() { + let c = 1000 + + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + var builder = Rope.Builder() + for chunk in ref { + builder.insertBeforeTip(chunk) + builder._invariantCheck() + } + let rope = builder.finalize() + + let actualSum = rope.summary + let expectedSum: Chunk.Summary = ref.reduce(into: .zero) { $0.add($1.summary) } + XCTAssertEqual(actualSum, expectedSum) + + XCTAssertTrue(rope.elementsEqual(ref)) + } + + func test_iteration() { + for c in [0, 1, 2, 10, 100, 500, 1000, 10000] { + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + let rope = Rope(ref) + + var it = rope.makeIterator() + var i = 0 + while let next = it.next() { + let expected = ref[i] + XCTAssertEqual(next, expected) + guard next == expected else { break } + i += 1 + } + XCTAssertEqual(i, ref.count) + + let expectedLength = ref.reduce(into: 0) { $0 += $1.length } + let actualLength = rope.reduce(into: 0) { $0 += $1.length } + XCTAssertEqual(actualLength, expectedLength) + } + } + + func test_subscript() { + for c in [0, 1, 2, 10, 100, 500, 1000, 10000] { + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + let rope = Rope(ref) + XCTAssertTrue(rope.elementsEqual(ref)) + + var i = rope.startIndex + var j = 0 + while i != rope.endIndex, j != ref.count { + XCTAssertEqual(rope[i], ref[j]) + i = rope.index(after: i) + j += 1 + } + XCTAssertEqual(i, rope.endIndex) + XCTAssertEqual(j, ref.count) + } + } + + func test_index_before() { + for c in [0, 1, 2, 10, 100, 500, 1000, 10000] { + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + let rope = Rope(ref) + XCTAssertTrue(rope.elementsEqual(ref)) + + var indices: [Rope.Index] = [] + var i = rope.startIndex + while i != rope.endIndex { + indices.append(i) + i = rope.index(after: i) + } + + while let j = indices.popLast() { + i = rope.index(before: i) + XCTAssertEqual(i, j) + } + } + } + + func test_distance() { + let c = 500 + + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + let rope = Rope(ref) + + let indices = Array(rope.indices) + [rope.endIndex] + XCTAssertEqual(indices.count, c + 1) + for i in indices.indices { + for j in indices.indices { + let d = rope.distance(from: indices[i], to: indices[j], in: Chunk.Metric()) + let r = ( + i <= j + ? ref[i..(ref) + + let indices = Array(rope.indices) + [rope.endIndex] + XCTAssertEqual(indices.count, c + 1) + for i in indices.indices { + for j in indices.indices { + let d = ( + i <= j + ? ref[i..() + var ref: [Chunk] = [] + + for i in 0 ..< c { + let chunk = Chunk(length: (i % 4) + 1, value: i) + ref.append(chunk) + rope.append(chunk) + rope._invariantCheck() + } + + let actualSum = rope.summary + let expectedSum: Chunk.Summary = ref.reduce(into: .zero) { $0.add($1.summary) } + XCTAssertEqual(actualSum, expectedSum) + + XCTAssertTrue(rope.elementsEqual(ref)) + } + + func test_prepend_item() { + let c = 1000 + + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + + var rope = Rope() + for chunk in ref.reversed() { + rope.insert(chunk, at: 0, in: Chunk.Metric()) + rope._invariantCheck() + } + + let actualSum = rope.summary + let expectedSum: Chunk.Summary = ref.reduce(into: .zero) { $0.add($1.summary) } + XCTAssertEqual(actualSum, expectedSum) + + XCTAssertTrue(rope.elementsEqual(ref)) + } + + func test_insert_item() { + let c = 1000 + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + + var rng = RepeatableRandomNumberGenerator(seed: 0) + let input = ref.shuffled(using: &rng) + + var rope = Rope() + for i in input.indices { + let chunk = input[i] + let position = input[..(ref) + + var rng = RepeatableRandomNumberGenerator(seed: 0) + let input = ref.shuffled(using: &rng) + + for i in input.indices { + let chunk = input[i] + let offset = input[i...].reduce(into: 0) { + $0 += $1.value < chunk.value ? 1 : 0 + } + let index = rope.index(rope.startIndex, offsetBy: offset) + let removed = rope.remove(at: index) + XCTAssertEqual(removed, chunk) + rope._invariantCheck() + } + XCTAssertTrue(rope.isEmpty) + XCTAssertEqual(rope.summary, .zero) + } + + func test_remove_at_inout_index() { + let c = 1000 + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + + var rope = Rope(ref) + + var rng = RepeatableRandomNumberGenerator(seed: 0) + let input = ref.shuffled(using: &rng) + + for i in input.indices { + let chunk = input[i] + let (offset, position) = input[i...].reduce(into: (0, 0)) { + guard $1.value < chunk.value else { return } + $0.0 += 1 + $0.1 += $1.length + } + var index = rope.index(rope.startIndex, offsetBy: offset) + let removed = rope.remove(at: &index) + XCTAssertEqual(removed, chunk) + XCTAssertEqual(rope.offset(of: index, in: Chunk.Metric()), position, "\(i)") + rope._invariantCheck() + } + XCTAssertTrue(rope.isEmpty) + XCTAssertEqual(rope.summary, .zero) + } + + func test_remove_at_position() { + let c = 1000 + let ref = (0 ..< c).map { + Chunk(length: ($0 % 4) + 1, value: $0) + } + + var rope = Rope(ref) + + var rng = RepeatableRandomNumberGenerator(seed: 0) + let input = ref.shuffled(using: &rng) + + for i in input.indices { + let chunk = input[i] + let position = input[i...].reduce(into: 0) { + $0 += $1.value < chunk.value ? $1.length : 0 + } + let r = rope.remove(at: position, in: Chunk.Metric()) + XCTAssertEqual(r.removed, chunk) + XCTAssertEqual(rope.offset(of: r.next, in: Chunk.Metric()), position, "\(i)") + rope._invariantCheck() + } + XCTAssertTrue(rope.isEmpty) + XCTAssertEqual(rope.summary, .zero) + } + + func test_join() { + let c = 100_000 + var trees = (0 ..< c).map { + let chunk = Chunk(length: ($0 % 4) + 1, value: $0) + return Rope(CollectionOfOne(chunk)) + } + var ranges = (0 ..< c).map { $0 ..< $0 + 1 } + + var rng = RepeatableRandomNumberGenerator(seed: 0) + while trees.count >= 2 { + let i = (0 ..< trees.count - 1).randomElement(using: &rng)! + let expectedRange = ranges[i].lowerBound ..< ranges[i + 1].upperBound + + let a = trees[i] + let b = trees.remove(at: i + 1) + trees[i] = Rope() + + let joined = Rope.join(a, b) + joined._invariantCheck() + let actualValues = joined.map { $0.value } + XCTAssertEqual(actualValues, Array(expectedRange)) + trees[i] = joined + ranges.replaceSubrange(i ... i + 1, with: CollectionOfOne(expectedRange)) + } + XCTAssertEqual(ranges, [0 ..< c]) + } + + func chunkify(_ values: [Int]) -> [Chunk] { + var result: [Chunk] = [] + var last = Int.min + var length = 0 + for i in values { + if length == 0 || i == last { + length += 1 + } else { + result.append(Chunk(length: length, value: last)) + length = 1 + } + last = i + } + if length > 0 { + result.append(Chunk(length: length, value: last)) + } + return result + } + + func checkEqual( + _ x: Rope, + _ y: [Int], + file: StaticString = #file, + line: UInt = #line + ) { + let u = Array(x) + let v = chunkify(y) + XCTAssertEqual(u, v, file: file, line: line) + } + + func checkRemoveSubrange( + _ a: Rope, + _ b: [Int], + range: Range, + file: StaticString = #file, + line: UInt = #line + ) { + var x = a + x.removeSubrange(range, in: Chunk.Metric()) + var y = b + y.removeSubrange(range) + + checkEqual(x, y, file: file, line: line) + } + + func test_removeSubrange_simple() { + var rope = Rope() + for i in 0 ..< 10 { + rope.append(Chunk(length: 10, value: i)) + } + let ref = (0 ..< 10).flatMap { Array(repeating: $0, count: 10) } + + // Basics + checkRemoveSubrange(rope, ref, range: 0 ..< 0) + checkRemoveSubrange(rope, ref, range: 30 ..< 30) + checkRemoveSubrange(rope, ref, range: 0 ..< 100) + + // Whole individual chunks + checkRemoveSubrange(rope, ref, range: 90 ..< 100) + checkRemoveSubrange(rope, ref, range: 0 ..< 10) + checkRemoveSubrange(rope, ref, range: 30 ..< 40) + checkRemoveSubrange(rope, ref, range: 70 ..< 80) + + // Prefixes of single chunks + checkRemoveSubrange(rope, ref, range: 0 ..< 1) + checkRemoveSubrange(rope, ref, range: 30 ..< 35) + checkRemoveSubrange(rope, ref, range: 60 ..< 66) + checkRemoveSubrange(rope, ref, range: 90 ..< 98) + + // Suffixes of single chunks + checkRemoveSubrange(rope, ref, range: 9 ..< 10) + checkRemoveSubrange(rope, ref, range: 35 ..< 40) + checkRemoveSubrange(rope, ref, range: 64 ..< 70) + checkRemoveSubrange(rope, ref, range: 98 ..< 100) + + // Neighboring couple of whole chunks + checkRemoveSubrange(rope, ref, range: 0 ..< 20) + checkRemoveSubrange(rope, ref, range: 80 ..< 100) + checkRemoveSubrange(rope, ref, range: 10 ..< 30) + checkRemoveSubrange(rope, ref, range: 50 ..< 70) // Crosses nodes! + + // Longer whole chunk sequences + checkRemoveSubrange(rope, ref, range: 0 ..< 30) + checkRemoveSubrange(rope, ref, range: 70 ..< 90) + checkRemoveSubrange(rope, ref, range: 0 ..< 60) // entire first node + checkRemoveSubrange(rope, ref, range: 60 ..< 100) // entire second node + checkRemoveSubrange(rope, ref, range: 40 ..< 70) // crosses into second node + checkRemoveSubrange(rope, ref, range: 10 ..< 90) // crosses into second node + + // Arbitrary cuts + checkRemoveSubrange(rope, ref, range: 0 ..< 69) + checkRemoveSubrange(rope, ref, range: 42 ..< 73) + checkRemoveSubrange(rope, ref, range: 21 ..< 89) + checkRemoveSubrange(rope, ref, range: 1 ..< 99) + checkRemoveSubrange(rope, ref, range: 1 ..< 59) + checkRemoveSubrange(rope, ref, range: 61 ..< 99) + + } + + func test_removeSubrange_larger() { + var rope = Rope() + for i in 0 ..< 100 { + rope.append(Chunk(length: 10, value: i)) + } + let ref = (0 ..< 100).flatMap { Array(repeating: $0, count: 10) } + + checkRemoveSubrange(rope, ref, range: 0 ..< 0) + checkRemoveSubrange(rope, ref, range: 0 ..< 1000) + checkRemoveSubrange(rope, ref, range: 0 ..< 100) + checkRemoveSubrange(rope, ref, range: 900 ..< 1000) + checkRemoveSubrange(rope, ref, range: 120 ..< 330) + checkRemoveSubrange(rope, ref, range: 734 ..< 894) + checkRemoveSubrange(rope, ref, range: 183 ..< 892) + + checkRemoveSubrange(rope, ref, range: 181 ..< 479) + checkRemoveSubrange(rope, ref, range: 191 ..< 469) + checkRemoveSubrange(rope, ref, range: 2 ..< 722) + checkRemoveSubrange(rope, ref, range: 358 ..< 718) + checkRemoveSubrange(rope, ref, range: 12 ..< 732) + checkRemoveSubrange(rope, ref, range: 348 ..< 728) + checkRemoveSubrange(rope, ref, range: 63 ..< 783) + checkRemoveSubrange(rope, ref, range: 297 ..< 655) + } + + func test_removeSubrange_random() { + for iteration in 0 ..< 20 { + var rng = RepeatableRandomNumberGenerator(seed: iteration) + let c = 1000 + var rope = Rope() + for i in 0 ..< c { + rope.append(Chunk(length: 2, value: i)) + } + var ref = (0 ..< c).flatMap { Array(repeating: $0, count: 2) } + + while !ref.isEmpty { + print(ref.count) + let i = (0 ..< ref.count).randomElement(using: &rng)! + let j = (i + 1 ... ref.count).randomElement(using: &rng)! + rope.removeSubrange(i ..< j, in: Chunk.Metric()) + ref.removeSubrange(i ..< j) + checkEqual(rope, ref) + } + } + } + +} diff --git a/Sources/_CollectionsTestSupport/AssertionContexts/Assertions.swift b/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift similarity index 99% rename from Sources/_CollectionsTestSupport/AssertionContexts/Assertions.swift rename to Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift index 76db23779..a1ccca5cf 100644 --- a/Sources/_CollectionsTestSupport/AssertionContexts/Assertions.swift +++ b/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift @@ -27,7 +27,7 @@ public func expectFailure( } } -internal func _expectFailure( +public func _expectFailure( _ diagnostic: String, _ message: () -> String, trapping: Bool, diff --git a/Sources/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift b/Tests/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift similarity index 63% rename from Sources/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift rename to Tests/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift index 044a2c08b..d341376c9 100644 --- a/Sources/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift +++ b/Tests/_CollectionsTestSupport/AssertionContexts/CollectionTestCase.swift @@ -17,14 +17,28 @@ open class CollectionTestCase: XCTestCase { public var context: TestContext { _context! } - public override func setUp() { + open var isAvailable: Bool { true } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + open override func invokeTest() { + guard isAvailable else { + print("\(Self.self) unavailable; skipping") + return + } + return super.invokeTest() + } + #endif + + open override func setUp() { super.setUp() _context = TestContext.pushNew() } - public override func tearDown() { - TestContext.pop(context) - _context = nil + open override func tearDown() { + if let context = _context { + TestContext.pop(context) + _context = nil + } super.tearDown() } } diff --git a/Sources/_CollectionsTestSupport/AssertionContexts/Combinatorics.swift b/Tests/_CollectionsTestSupport/AssertionContexts/Combinatorics.swift similarity index 100% rename from Sources/_CollectionsTestSupport/AssertionContexts/Combinatorics.swift rename to Tests/_CollectionsTestSupport/AssertionContexts/Combinatorics.swift diff --git a/Sources/_CollectionsTestSupport/AssertionContexts/TestContext.swift b/Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift similarity index 100% rename from Sources/_CollectionsTestSupport/AssertionContexts/TestContext.swift rename to Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckBidirectionalCollection.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckBidirectionalCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckBidirectionalCollection.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckBidirectionalCollection.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckCollection.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckCollection.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckCollection.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckComparable.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckComparable.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckComparable.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckComparable.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckEquatable.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckEquatable.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckEquatable.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckEquatable.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckHashable.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckHashable.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckHashable.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckHashable.swift diff --git a/Sources/_CollectionsTestSupport/ConformanceCheckers/CheckSequence.swift b/Tests/_CollectionsTestSupport/ConformanceCheckers/CheckSequence.swift similarity index 100% rename from Sources/_CollectionsTestSupport/ConformanceCheckers/CheckSequence.swift rename to Tests/_CollectionsTestSupport/ConformanceCheckers/CheckSequence.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalBidirectionalCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalBidirectionalCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalBidirectionalCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalBidirectionalCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalDecoder.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalDecoder.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalDecoder.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalDecoder.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalEncoder.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalEncoder.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalEncoder.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalEncoder.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalIndex.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalIndex.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalIndex.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalIndex.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalIterator.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalIterator.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalIterator.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalIterator.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalMutableRandomAccessCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalMutableRandomAccessCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalMutableRandomAccessCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalMutableRandomAccessCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalMutableRangeReplaceableRandomAccessCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalMutableRangeReplaceableRandomAccessCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalMutableRangeReplaceableRandomAccessCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalMutableRangeReplaceableRandomAccessCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalRandomAccessCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalRandomAccessCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalRandomAccessCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalRandomAccessCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalRangeReplaceableRandomAccessCollection.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalRangeReplaceableRandomAccessCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalRangeReplaceableRandomAccessCollection.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalRangeReplaceableRandomAccessCollection.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/MinimalSequence.swift b/Tests/_CollectionsTestSupport/MinimalTypes/MinimalSequence.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/MinimalSequence.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/MinimalSequence.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/ResettableValue.swift b/Tests/_CollectionsTestSupport/MinimalTypes/ResettableValue.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/ResettableValue.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/ResettableValue.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/_CollectionState.swift b/Tests/_CollectionsTestSupport/MinimalTypes/_CollectionState.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/_CollectionState.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/_CollectionState.swift diff --git a/Sources/_CollectionsTestSupport/MinimalTypes/_MinimalCollectionCore.swift b/Tests/_CollectionsTestSupport/MinimalTypes/_MinimalCollectionCore.swift similarity index 100% rename from Sources/_CollectionsTestSupport/MinimalTypes/_MinimalCollectionCore.swift rename to Tests/_CollectionsTestSupport/MinimalTypes/_MinimalCollectionCore.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/AllOnesRandomNumberGenerator.swift b/Tests/_CollectionsTestSupport/Utilities/AllOnesRandomNumberGenerator.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/AllOnesRandomNumberGenerator.swift rename to Tests/_CollectionsTestSupport/Utilities/AllOnesRandomNumberGenerator.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/Box.swift b/Tests/_CollectionsTestSupport/Utilities/Box.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/Box.swift rename to Tests/_CollectionsTestSupport/Utilities/Box.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/DictionaryAPIChecker.swift b/Tests/_CollectionsTestSupport/Utilities/DictionaryAPIChecker.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/DictionaryAPIChecker.swift rename to Tests/_CollectionsTestSupport/Utilities/DictionaryAPIChecker.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/HashableBox.swift b/Tests/_CollectionsTestSupport/Utilities/HashableBox.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/HashableBox.swift rename to Tests/_CollectionsTestSupport/Utilities/HashableBox.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/IndexRangeCollection.swift b/Tests/_CollectionsTestSupport/Utilities/IndexRangeCollection.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/IndexRangeCollection.swift rename to Tests/_CollectionsTestSupport/Utilities/IndexRangeCollection.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/Integer Square Root.swift b/Tests/_CollectionsTestSupport/Utilities/Integer Square Root.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/Integer Square Root.swift rename to Tests/_CollectionsTestSupport/Utilities/Integer Square Root.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/LifetimeTracked.swift b/Tests/_CollectionsTestSupport/Utilities/LifetimeTracked.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/LifetimeTracked.swift rename to Tests/_CollectionsTestSupport/Utilities/LifetimeTracked.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/LifetimeTracker.swift b/Tests/_CollectionsTestSupport/Utilities/LifetimeTracker.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/LifetimeTracker.swift rename to Tests/_CollectionsTestSupport/Utilities/LifetimeTracker.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/RandomStableSample.swift b/Tests/_CollectionsTestSupport/Utilities/RandomStableSample.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/RandomStableSample.swift rename to Tests/_CollectionsTestSupport/Utilities/RandomStableSample.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/SeedableRandomNumberGenerator.swift b/Tests/_CollectionsTestSupport/Utilities/RepeatableRandomNumberGenerator.swift similarity index 80% rename from Sources/_CollectionsTestSupport/Utilities/SeedableRandomNumberGenerator.swift rename to Tests/_CollectionsTestSupport/Utilities/RepeatableRandomNumberGenerator.swift index f09b89ebc..3e285d0d6 100644 --- a/Sources/_CollectionsTestSupport/Utilities/SeedableRandomNumberGenerator.swift +++ b/Tests/_CollectionsTestSupport/Utilities/RepeatableRandomNumberGenerator.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-2023 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 @@ -10,6 +10,14 @@ //===----------------------------------------------------------------------===// public struct RepeatableRandomNumberGenerator: RandomNumberGenerator { + public static var globalSeed: Int { +#if COLLECTIONS_RANDOMIZED_TESTING + 42.hashValue +#else + 0 +#endif + } + // This uses the same linear congruential generator as rand48. // FIXME: Replace with something better. internal static let _m: UInt64 = 1 << 48 @@ -26,7 +34,8 @@ public struct RepeatableRandomNumberGenerator: RandomNumberGenerator { // Perturb the seed a little so that the sequence doesn't start with a // zero value in the common case of seed == 0. (Using a zero seed is a // rather silly thing to do, but it's the easy thing.) - _state = seed ^ 0x536f52616e646f6d // "SoRandom" + let global = UInt64(truncatingIfNeeded: Self.globalSeed) + _state = seed ^ global ^ 0x536f52616e646f6d // "SoRandom" } private mutating func _next() -> UInt64 { diff --git a/Sources/_CollectionsTestSupport/Utilities/SetAPIChecker.swift b/Tests/_CollectionsTestSupport/Utilities/SetAPIChecker.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/SetAPIChecker.swift rename to Tests/_CollectionsTestSupport/Utilities/SetAPIChecker.swift diff --git a/Sources/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift b/Tests/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift similarity index 94% rename from Sources/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift rename to Tests/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift index 97cd75a45..55c254225 100644 --- a/Sources/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift +++ b/Tests/_CollectionsTestSupport/Utilities/SortedCollectionAPIChecker.swift @@ -9,7 +9,11 @@ // //===----------------------------------------------------------------------===// +#if COLLECTIONS_SINGLE_MODULE +import Collections +#else import _CollectionsUtilities +#endif /// This protocol simply lists Collection/Sequence extensions that ought to be /// customized for sorted collections. diff --git a/Sources/_CollectionsTestSupport/Utilities/StringConvertibleValue.swift b/Tests/_CollectionsTestSupport/Utilities/StringConvertibleValue.swift similarity index 100% rename from Sources/_CollectionsTestSupport/Utilities/StringConvertibleValue.swift rename to Tests/_CollectionsTestSupport/Utilities/StringConvertibleValue.swift diff --git a/Utils/README.md b/Utils/README.md new file mode 100644 index 000000000..58bce3bdc --- /dev/null +++ b/Utils/README.md @@ -0,0 +1,17 @@ +# Maintenance Scripts + +This directory contains scripts that are used to maintain this package. + +Beware! The contents of this directory are not source stable. They are provided as is, with no compatibility promises across package releases. Future versions of this package can arbitrarily change these files or remove them, without any advance notice. (This can include patch releases.) + +- `generate-docs.sh`: A shell scripts that automates the generation of API documentation. + +- `generate-sources.sh`: A shell script that invokes `gyb` to regenerate the contents of autogenerated/ directories. This needs to be run whenever a file with a `.swift.gyb` extension is updated. + +- `gyb`, `gyb.py`: Generate Your Boilerplate. A rudimentary source code generation utility. + +- `gyb_utils.py`: A Python module containing code generation utility definitions that are shared across multiple `.swift.gyb` files in this repository. + +- `run-full-tests.sh`: A shell script that exercises many common configurations of this package in a semi-automated way. This is used before tagging a release to avoid accidentally shipping a package version that breaks some setups. + +- `shuffle-sources.sh`: A legacy utility that randomly reorders Swift source files in a given directory. This is used to avoid reoccurrances of issue #7. (This is hopefully only relevant with compilers that the package no longer supports.) diff --git a/Utils/generate-sources.sh b/Utils/generate-sources.sh new file mode 100755 index 000000000..00d34fc20 --- /dev/null +++ b/Utils/generate-sources.sh @@ -0,0 +1,62 @@ +#!/bin/sh +#===----------------------------------------------------------------------===// +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2020 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 +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +#===----------------------------------------------------------------------===// + +set -eu + +srcroot="$(dirname "$0")/.." +cd "$srcroot" + +gyb="./Utils/gyb" + +# Disable line directives in gyb output. We commit generated sources +# into the package repository, so we do not want absolute file names +# in them. +lineDirective='' + +# Uncomment the following line to enable #sourceLocation directives. +# This is useful for local development. +#lineDirective='#sourceLocation(file: "%(file)s", line: %(line)d)' + + +# Create a temporary directory; remove it on exit. +tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXX")" +trap "rm -rf \"$tmpdir\"" EXIT + +# Run gyb on each gyb file in the source tree and put results in +# subdirectories named 'autogenerated'. +find ./Sources ./Tests -name "*.gyb" | while read input; do + basename="$(basename "$input")" + targetdir="$(dirname "$input")/autogenerated" + output="$targetdir/"${basename%.gyb} + tmpfile="$tmpdir/${basename%.gyb}" + + # Make sure the output directory exists. + mkdir -p "$targetdir" + + # Run gyb, making sure to only update files when they change. + "$gyb" --line-directive "$lineDirective" -o "$tmpfile" "$input" + if [ -e "$output" ] && cmp -s "$tmpfile" "$output"; then + : Ignore unchanged file + else + echo "Updated $output" + cp "$tmpfile" "$output" + fi + echo "$output" >> "$tmpdir/generated-files.txt" +done + +# Remove autogenerated files without a corresponding gyb. +find . -path '*/autogenerated/*.swift' >> "$tmpdir/generated-files.txt" +sort "$tmpdir/generated-files.txt" | uniq -u | while read obsolete; do + echo "Removing $obsolete" + rm "$obsolete" +done diff --git a/Utils/gyb b/Utils/gyb new file mode 100755 index 000000000..8206bd8a0 --- /dev/null +++ b/Utils/gyb @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 +import gyb +gyb.main() diff --git a/Utils/gyb.py b/Utils/gyb.py new file mode 100755 index 000000000..e3af0b7d8 --- /dev/null +++ b/Utils/gyb.py @@ -0,0 +1,1247 @@ +#!/usr/bin/env python +# GYB: Generate Your Boilerplate (improved names welcome; at least +# this one's short). See -h output for instructions + +import os +import re +import sys +import textwrap +import tokenize +from bisect import bisect +from io import StringIO + + +def get_line_starts(s): + """Return a list containing the start index of each line in s. + + The list also contains a sentinel index for the end of the string, + so there will be one more element in the list than there are lines + in the string + """ + starts = [0] + + for line in s.split('\n'): + starts.append(starts[-1] + len(line) + 1) + + starts[-1] -= 1 + return starts + + +def strip_trailing_nl(s): + """If s ends with a newline, drop it; else return s intact""" + return s[:-1] if s.endswith('\n') else s + + +def split_lines(s): + """Split s into a list of lines, each of which has a trailing newline + + If the lines are later concatenated, the result is s, possibly + with a single appended newline. + """ + return [l + '\n' for l in s.split('\n')] + + +# text on a line up to the first '$$', '${', or '%%' +literalText = r'(?: [^$\n%] | \$(?![${]) | %(?!%) )*' + +# The part of an '%end' line that follows the '%' sign +linesClose = r'[\ \t]* end [\ \t]* (?: \# .* )? $' + +# Note: Where "# Absorb" appears below, the regexp attempts to eat up +# through the end of ${...} and %{...}% constructs. In reality we +# handle this with the Python tokenizer, which avoids mis-detections +# due to nesting, comments and strings. This extra absorption in the +# regexp facilitates testing the regexp on its own, by preventing the +# interior of some of these constructs from being treated as literal +# text. +tokenize_re = re.compile( + r''' +# %-lines and %{...}-blocks + # \n? # absorb one preceding newline + ^ + (?: + (?P + (?P<_indent> [\ \t]* % (?! [{%] ) [\ \t]* ) (?! [\ \t] | ''' + + linesClose + r''' ) .* + ( \n (?P=_indent) (?! ''' + linesClose + r''' ) .* ) * + ) + | (?P [\ \t]* % [ \t]* ''' + linesClose + r''' ) + | [\ \t]* (?P %\{ ) + (?: [^}]| \} (?!%) )* \}% # Absorb + ) + \n? # absorb one trailing newline + +# Substitutions +| (?P \$\{ ) + [^}]* \} # Absorb + +# %% and $$ are literal % and $ respectively +| (?P[$%]) (?P=symbol) + +# Literal text +| (?P ''' + literalText + r''' + (?: + # newline that doesn't precede space+% + (?: \n (?! [\ \t]* %[^%] ) ) + ''' + literalText + r''' + )* + \n? + ) +''', re.VERBOSE | re.MULTILINE) + +gyb_block_close = re.compile(r'\}%[ \t]*\n?') + + +def token_pos_to_index(token_pos, start, line_starts): + """Translate a tokenize (line, column) pair into an absolute + position in source text given the position where we started + tokenizing and a list that maps lines onto their starting + character indexes. + """ + relative_token_line_plus1, token_col = token_pos + + # line number where we started tokenizing + start_line_num = bisect(line_starts, start) - 1 + + # line number of the token in the whole text + abs_token_line = relative_token_line_plus1 - 1 + start_line_num + + # if found in the first line, adjust the end column to account + # for the extra text + if relative_token_line_plus1 == 1: + token_col += start - line_starts[start_line_num] + + # Sometimes tokenizer errors report a line beyond the last one + if abs_token_line >= len(line_starts): + return line_starts[-1] + + return line_starts[abs_token_line] + token_col + + +def tokenize_python_to_unmatched_close_curly(source_text, start, line_starts): + """Apply Python's tokenize to source_text starting at index start + while matching open and close curly braces. When an unmatched + close curly brace is found, return its index. If not found, + return len(source_text). If there's a tokenization error, return + the position of the error. + """ + stream = StringIO(source_text) + stream.seek(start) + nesting = 0 + + try: + for kind, text, token_start, token_end, line_text \ + in tokenize.generate_tokens(stream.readline): + + if text == '{': + nesting += 1 + elif text == '}': + nesting -= 1 + if nesting < 0: + return token_pos_to_index(token_start, start, line_starts) + + except tokenize.TokenError as error: + (message, error_pos) = error.args + return token_pos_to_index(error_pos, start, line_starts) + + return len(source_text) + + +def tokenize_template(template_text): + r"""Given the text of a template, returns an iterator over + (tokenType, token, match) tuples. + + **Note**: this is template syntax tokenization, not Python + tokenization. + + When a non-literal token is matched, a client may call + iter.send(pos) on the iterator to reset the position in + template_text at which scanning will resume. + + This function provides a base level of tokenization which is + then refined by ParseContext.token_generator. + + >>> from pprint import * + >>> pprint(list((kind, text) for kind, text, _ in tokenize_template( + ... '%for x in range(10):\n% print x\n%end\njuicebox'))) + [('gybLines', '%for x in range(10):\n% print x'), + ('gybLinesClose', '%end'), + ('literal', 'juicebox')] + + >>> pprint(list((kind, text) for kind, text, _ in tokenize_template( + ... '''Nothing + ... % if x: + ... % for i in range(3): + ... ${i} + ... % end + ... % else: + ... THIS SHOULD NOT APPEAR IN THE OUTPUT + ... '''))) + [('literal', 'Nothing\n'), + ('gybLines', '% if x:\n% for i in range(3):'), + ('substitutionOpen', '${'), + ('literal', '\n'), + ('gybLinesClose', '% end'), + ('gybLines', '% else:'), + ('literal', 'THIS SHOULD NOT APPEAR IN THE OUTPUT\n')] + + >>> for kind, text, _ in tokenize_template(''' + ... This is $some$ literal stuff containing a ${substitution} + ... followed by a %{...} block: + ... %{ + ... # Python code + ... }% + ... and here $${are} some %-lines: + ... % x = 1 + ... % y = 2 + ... % if z == 3: + ... % print '${hello}' + ... % end + ... % for x in zz: + ... % print x + ... % # different indentation + ... % twice + ... and some lines that literally start with a %% token + ... %% first line + ... %% second line + ... '''): + ... print((kind, text.strip().split('\n',1)[0])) + ('literal', 'This is $some$ literal stuff containing a') + ('substitutionOpen', '${') + ('literal', 'followed by a %{...} block:') + ('gybBlockOpen', '%{') + ('literal', 'and here ${are} some %-lines:') + ('gybLines', '% x = 1') + ('gybLinesClose', '% end') + ('gybLines', '% for x in zz:') + ('gybLines', '% # different indentation') + ('gybLines', '% twice') + ('literal', 'and some lines that literally start with a % token') + """ + pos = 0 + end = len(template_text) + + saved_literal = [] + literal_first_match = None + + while pos < end: + m = tokenize_re.match(template_text, pos, end) + + # pull out the one matched key (ignoring internal patterns starting + # with _) + ((kind, text), ) = ( + (kind, text) for (kind, text) in list(m.groupdict().items()) + if text is not None and kind[0] != '_') + + if kind in ('literal', 'symbol'): + if len(saved_literal) == 0: + literal_first_match = m + # literals and symbols get batched together + saved_literal.append(text) + pos = None + else: + # found a non-literal. First yield any literal we've accumulated + if saved_literal != []: + yield 'literal', ''.join(saved_literal), literal_first_match + saved_literal = [] + + # Then yield the thing we found. If we get a reply, it's + # the place to resume tokenizing + pos = yield kind, text, m + + # If we were not sent a new position by our client, resume + # tokenizing at the end of this match. + if pos is None: + pos = m.end(0) + else: + # Client is not yet ready to process next token + yield + + if saved_literal != []: + yield 'literal', ''.join(saved_literal), literal_first_match + + +def split_gyb_lines(source_lines): + r"""Return a list of lines at which to split the incoming source + + These positions represent the beginnings of python line groups that + will require a matching %end construct if they are to be closed. + + >>> src = split_lines('''\ + ... if x: + ... print x + ... if y: # trailing comment + ... print z + ... if z: # another comment\ + ... ''') + >>> s = split_gyb_lines(src) + >>> len(s) + 2 + >>> src[s[0]] + ' print z\n' + >>> s[1] - len(src) + 0 + + >>> src = split_lines('''\ + ... if x: + ... if y: print 1 + ... if z: + ... print 2 + ... pass\ + ... ''') + >>> s = split_gyb_lines(src) + >>> len(s) + 1 + >>> src[s[0]] + ' if y: print 1\n' + + >>> src = split_lines('''\ + ... if x: + ... if y: + ... print 1 + ... print 2 + ... ''') + >>> s = split_gyb_lines(src) + >>> len(s) + 2 + >>> src[s[0]] + ' if y:\n' + >>> src[s[1]] + ' print 1\n' + """ + last_token_text, last_token_kind = None, None + unmatched_indents = [] + + dedents = 0 + try: + for token_kind, token_text, token_start, \ + (token_end_line, token_end_col), line_text \ + in tokenize.generate_tokens(lambda i=iter(source_lines): + next(i)): + + if token_kind in (tokenize.COMMENT, tokenize.ENDMARKER): + continue + + if token_text == '\n' and last_token_text == ':': + unmatched_indents.append(token_end_line) + + # The tokenizer appends dedents at EOF; don't consider + # those as matching indentations. Instead just save them + # up... + if last_token_kind == tokenize.DEDENT: + dedents += 1 + # And count them later, when we see something real. + if token_kind != tokenize.DEDENT and dedents > 0: + unmatched_indents = unmatched_indents[:-dedents] + dedents = 0 + + last_token_text, last_token_kind = token_text, token_kind + + except tokenize.TokenError: + # Let the later compile() call report the error + return [] + + if last_token_text == ':': + unmatched_indents.append(len(source_lines)) + + return unmatched_indents + + +def code_starts_with_dedent_keyword(source_lines): + r"""Return True iff the incoming Python source_lines begin with "else", + "elif", "except", or "finally". + + Initial comments and whitespace are ignored. + + >>> code_starts_with_dedent_keyword(split_lines('if x in y: pass')) + False + >>> code_starts_with_dedent_keyword(split_lines('except ifSomethingElse:')) + True + >>> code_starts_with_dedent_keyword( + ... split_lines('\n# comment\nelse: # yes')) + True + """ + token_text = None + for token_kind, token_text, _, _, _ \ + in tokenize.generate_tokens(lambda i=iter(source_lines): next(i)): + + if token_kind != tokenize.COMMENT and token_text.strip() != '': + break + + return token_text in ('else', 'elif', 'except', 'finally') + + +class ParseContext(object): + + """State carried through a parse of a template""" + + filename = '' + template = '' + line_starts = [] + code_start_line = -1 + code_text = None + tokens = None # The rest of the tokens + close_lines = False + + def __init__(self, filename, template=None): + self.filename = os.path.abspath(filename) + if sys.platform == 'win32': + self.filename = self.filename.replace('\\', '/') + if template is None: + with open(filename) as f: + self.template = f.read() + else: + self.template = template + self.line_starts = get_line_starts(self.template) + self.tokens = self.token_generator(tokenize_template(self.template)) + self.next_token() + + def pos_to_line(self, pos): + return bisect(self.line_starts, pos) - 1 + + def token_generator(self, base_tokens): + r"""Given an iterator over (kind, text, match) triples (see + tokenize_template above), return a refined iterator over + token_kinds. + + Among other adjustments to the elements found by base_tokens, + this refined iterator tokenizes python code embedded in + template text to help determine its true extent. The + expression "base_tokens.send(pos)" is used to reset the index at + which base_tokens resumes scanning the underlying text. + + >>> ctx = ParseContext('dummy', ''' + ... %for x in y: + ... % print x + ... % end + ... literally + ... ''') + >>> while ctx.token_kind: + ... print((ctx.token_kind, ctx.code_text or ctx.token_text)) + ... ignored = ctx.next_token() + ('literal', '\n') + ('gybLinesOpen', 'for x in y:\n') + ('gybLines', ' print x\n') + ('gybLinesClose', '% end') + ('literal', 'literally\n') + + >>> ctx = ParseContext('dummy', + ... '''Nothing + ... % if x: + ... % for i in range(3): + ... ${i} + ... % end + ... % else: + ... THIS SHOULD NOT APPEAR IN THE OUTPUT + ... ''') + >>> while ctx.token_kind: + ... print((ctx.token_kind, ctx.code_text or ctx.token_text)) + ... ignored = ctx.next_token() + ('literal', 'Nothing\n') + ('gybLinesOpen', 'if x:\n') + ('gybLinesOpen', ' for i in range(3):\n') + ('substitutionOpen', 'i') + ('literal', '\n') + ('gybLinesClose', '% end') + ('gybLinesOpen', 'else:\n') + ('literal', 'THIS SHOULD NOT APPEAR IN THE OUTPUT\n') + + >>> ctx = ParseContext('dummy', + ... '''% for x in [1, 2, 3]: + ... % if x == 1: + ... literal1 + ... % elif x > 1: # add output line here to fix bug + ... % if x == 2: + ... literal2 + ... % end + ... % end + ... % end + ... ''') + >>> while ctx.token_kind: + ... print((ctx.token_kind, ctx.code_text or ctx.token_text)) + ... ignored = ctx.next_token() + ('gybLinesOpen', 'for x in [1, 2, 3]:\n') + ('gybLinesOpen', ' if x == 1:\n') + ('literal', 'literal1\n') + ('gybLinesOpen', 'elif x > 1: # add output line here to fix bug\n') + ('gybLinesOpen', ' if x == 2:\n') + ('literal', 'literal2\n') + ('gybLinesClose', '% end') + ('gybLinesClose', '% end') + ('gybLinesClose', '% end') + """ + for self.token_kind, self.token_text, self.token_match in base_tokens: + kind = self.token_kind + self.code_text = None + + # Do we need to close the current lines? + self.close_lines = kind == 'gybLinesClose' + + # %{...}% and ${...} constructs + if kind.endswith('Open'): + + # Tokenize text that follows as Python up to an unmatched '}' + code_start = self.token_match.end(kind) + self.code_start_line = self.pos_to_line(code_start) + + close_pos = tokenize_python_to_unmatched_close_curly( + self.template, code_start, self.line_starts) + self.code_text = self.template[code_start:close_pos] + yield kind + + if (kind == 'gybBlockOpen'): + # Absorb any '}% \n' + m2 = gyb_block_close.match(self.template, close_pos) + if not m2: + raise ValueError("Invalid block closure") + next_pos = m2.end(0) + else: + assert kind == 'substitutionOpen' + # skip past the closing '}' + next_pos = close_pos + 1 + + # Resume tokenizing after the end of the code. + base_tokens.send(next_pos) + + elif kind == 'gybLines': + + self.code_start_line = self.pos_to_line( + self.token_match.start('gybLines')) + indentation = self.token_match.group('_indent') + + # Strip off the leading indentation and %-sign + source_lines = re.split( + '^' + re.escape(indentation), + self.token_match.group('gybLines') + '\n', + flags=re.MULTILINE)[1:] + + if code_starts_with_dedent_keyword(source_lines): + self.close_lines = True + + last_split = 0 + for line in split_gyb_lines(source_lines): + self.token_kind = 'gybLinesOpen' + self.code_text = ''.join(source_lines[last_split:line]) + yield self.token_kind + last_split = line + self.code_start_line += line - last_split + self.close_lines = False + + self.code_text = ''.join(source_lines[last_split:]) + if self.code_text: + self.token_kind = 'gybLines' + yield self.token_kind + else: + yield self.token_kind + + def next_token(self): + """Move to the next token""" + for kind in self.tokens: + return self.token_kind + + self.token_kind = None + + +_default_line_directive = \ + '// ###sourceLocation(file: "%(file)s", line: %(line)d)' + + +class ExecutionContext(object): + + """State we pass around during execution of a template""" + + def __init__(self, line_directive=_default_line_directive, + **local_bindings): + self.local_bindings = local_bindings + self.line_directive = line_directive + self.local_bindings['__context__'] = self + self.result_text = [] + self.last_file_line = None + + def append_text(self, text, file, line): + # see if we need to inject a line marker + if self.line_directive: + if (file, line) != self.last_file_line: + # We can only insert the line directive at a line break + if len(self.result_text) == 0 \ + or self.result_text[-1].endswith('\n'): + substitutions = {'file': file, 'line': line + 1} + format_str = self.line_directive + '\n' + self.result_text.append(format_str % substitutions) + # But if the new text contains any line breaks, we can create + # one + elif '\n' in text: + i = text.find('\n') + self.result_text.append(text[:i + 1]) + # and try again + self.append_text(text[i + 1:], file, line + 1) + return + + self.result_text.append(text) + self.last_file_line = (file, line + text.count('\n')) + + +class ASTNode(object): + + """Abstract base class for template AST nodes""" + + def __init__(self): + raise NotImplementedError("ASTNode.__init__ is not implemented.") + + def execute(self, context): + raise NotImplementedError("ASTNode.execute is not implemented.") + + def __str__(self, indent=''): + raise NotImplementedError("ASTNode.__str__ is not implemented.") + + def format_children(self, indent): + if not self.children: + return ' []' + + return '\n'.join( + ['', indent + '['] + + [x.__str__(indent + 4 * ' ') for x in self.children] + + [indent + ']']) + + +class Block(ASTNode): + + """A sequence of other AST nodes, to be executed in order""" + + children = [] + + def __init__(self, context): + self.children = [] + + while context.token_kind and not context.close_lines: + if context.token_kind == 'literal': + node = Literal + else: + node = Code + self.children.append(node(context)) + + def execute(self, context): + for x in self.children: + x.execute(context) + + def __str__(self, indent=''): + return indent + 'Block:' + self.format_children(indent) + + +class Literal(ASTNode): + + """An AST node that generates literal text""" + + def __init__(self, context): + self.text = context.token_text + start_position = context.token_match.start(context.token_kind) + self.start_line_number = context.pos_to_line(start_position) + self.filename = context.filename + context.next_token() + + def execute(self, context): + context.append_text(self.text, self.filename, self.start_line_number) + + def __str__(self, indent=''): + return '\n'.join( + [indent + x for x in ['Literal:'] + + strip_trailing_nl(self.text).split('\n')]) + + +class Code(ASTNode): + + """An AST node that is evaluated as Python""" + + code = None + children = () + kind = None + + def __init__(self, context): + + source = '' + source_line_count = 0 + + def accumulate_code(): + s = source + (context.code_start_line - source_line_count) * '\n' \ + + textwrap.dedent(context.code_text) + line_count = context.code_start_line + \ + context.code_text.count('\n') + context.next_token() + return s, line_count + + eval_exec = 'exec' + if context.token_kind.startswith('substitution'): + eval_exec = 'eval' + source, source_line_count = accumulate_code() + source = '(' + source.strip() + ')' + + else: + while context.token_kind == 'gybLinesOpen': + source, source_line_count = accumulate_code() + source += ' __children__[%d].execute(__context__)\n' % len( + self.children) + source_line_count += 1 + + self.children += (Block(context),) + + if context.token_kind == 'gybLinesClose': + context.next_token() + + if context.token_kind == 'gybLines': + source, source_line_count = accumulate_code() + + # Only handle a substitution as part of this code block if + # we don't already have some %-lines. + elif context.token_kind == 'gybBlockOpen': + + # Opening ${...} and %{...}% constructs + source, source_line_count = accumulate_code() + + self.filename = context.filename + self.start_line_number = context.code_start_line + self.code = compile(source, context.filename, eval_exec) + self.source = source + + def execute(self, context): + # Save __children__ from the local bindings + save_children = context.local_bindings.get('__children__') + # Execute the code with our __children__ in scope + context.local_bindings['__children__'] = self.children + context.local_bindings['__file__'] = self.filename + result = eval(self.code, context.local_bindings) + + if context.local_bindings['__children__'] is not self.children: + raise ValueError("The code is not allowed to mutate __children__") + # Restore the bindings + context.local_bindings['__children__'] = save_children + + # If we got a result, the code was an expression, so append + # its value + if result is not None \ + or (isinstance(result, str) and result != ''): + from numbers import Number, Integral + result_string = None + if isinstance(result, Number) and not isinstance(result, Integral): + result_string = repr(result) + else: + result_string = str(result) + context.append_text( + result_string, self.filename, self.start_line_number) + + def __str__(self, indent=''): + source_lines = re.sub(r'^\n', '', strip_trailing_nl( + self.source), flags=re.MULTILINE).split('\n') + if len(source_lines) == 1: + s = indent + 'Code: {' + source_lines[0] + '}' + else: + s = indent + 'Code:\n' + indent + '{\n' + '\n'.join( + indent + 4 * ' ' + l for l in source_lines + ) + '\n' + indent + '}' + return s + self.format_children(indent) + + +def expand(filename, line_directive=_default_line_directive, **local_bindings): + r"""Return the contents of the given template file, executed with the given + local bindings. + + >>> from tempfile import NamedTemporaryFile + >>> # On Windows, the name of a NamedTemporaryFile cannot be used to open + >>> # the file for a second time if delete=True. Therefore, we have to + >>> # manually handle closing and deleting this file to allow us to open + >>> # the file by its name across all platforms. + >>> f = NamedTemporaryFile(delete=False) + >>> f.write( + ... r'''--- + ... % for i in range(int(x)): + ... a pox on ${i} for epoxy + ... % end + ... ${120 + + ... + ... 3} + ... abc + ... ${"w\nx\nX\ny"} + ... z + ... ''') + >>> f.flush() + >>> result = expand( + ... f.name, + ... line_directive='//#sourceLocation(file: "%(file)s", ' + \ + ... 'line: %(line)d)', + ... x=2 + ... ).replace( + ... '"%s"' % f.name.replace('\\', '/'), '"dummy.file"') + >>> print(result, end='') + //#sourceLocation(file: "dummy.file", line: 1) + --- + //#sourceLocation(file: "dummy.file", line: 3) + a pox on 0 for epoxy + //#sourceLocation(file: "dummy.file", line: 3) + a pox on 1 for epoxy + //#sourceLocation(file: "dummy.file", line: 5) + 123 + //#sourceLocation(file: "dummy.file", line: 8) + abc + w + x + X + y + //#sourceLocation(file: "dummy.file", line: 10) + z + >>> f.close() + >>> os.remove(f.name) + """ + with open(filename) as f: + t = parse_template(filename, f.read()) + d = os.getcwd() + os.chdir(os.path.dirname(os.path.abspath(filename))) + try: + return execute_template( + t, line_directive=line_directive, **local_bindings) + finally: + os.chdir(d) + + +def parse_template(filename, text=None): + r"""Return an AST corresponding to the given template file. + + If text is supplied, it is assumed to be the contents of the file, + as a string. + + >>> print(parse_template('dummy.file', text= + ... '''% for x in [1, 2, 3]: + ... % if x == 1: + ... literal1 + ... % elif x > 1: # add output line after this line to fix bug + ... % if x == 2: + ... literal2 + ... % end + ... % end + ... % end + ... ''')) + Block: + [ + Code: + { + for x in [1, 2, 3]: + __children__[0].execute(__context__) + } + [ + Block: + [ + Code: + { + if x == 1: + __children__[0].execute(__context__) + elif x > 1: # add output line after this line to fix bug + __children__[1].execute(__context__) + } + [ + Block: + [ + Literal: + literal1 + ] + Block: + [ + Code: + { + if x == 2: + __children__[0].execute(__context__) + } + [ + Block: + [ + Literal: + literal2 + ] + ] + ] + ] + ] + ] + ] + + >>> print(parse_template( + ... 'dummy.file', + ... text='%for x in range(10):\n% print(x)\n%end\njuicebox')) + Block: + [ + Code: + { + for x in range(10): + __children__[0].execute(__context__) + } + [ + Block: + [ + Code: {print(x)} [] + ] + ] + Literal: + juicebox + ] + + >>> print(parse_template('/dummy.file', text= + ... '''Nothing + ... % if x: + ... % for i in range(3): + ... ${i} + ... % end + ... % else: + ... THIS SHOULD NOT APPEAR IN THE OUTPUT + ... ''')) + Block: + [ + Literal: + Nothing + Code: + { + if x: + __children__[0].execute(__context__) + else: + __children__[1].execute(__context__) + } + [ + Block: + [ + Code: + { + for i in range(3): + __children__[0].execute(__context__) + } + [ + Block: + [ + Code: {(i)} [] + Literal: + + ] + ] + ] + Block: + [ + Literal: + THIS SHOULD NOT APPEAR IN THE OUTPUT + ] + ] + ] + + >>> print(parse_template('dummy.file', text='''% + ... %for x in y: + ... % print(y) + ... ''')) + Block: + [ + Code: + { + for x in y: + __children__[0].execute(__context__) + } + [ + Block: + [ + Code: {print(y)} [] + ] + ] + ] + + >>> print(parse_template('dummy.file', text='''% + ... %if x: + ... % print(y) + ... AAAA + ... %else: + ... BBBB + ... ''')) + Block: + [ + Code: + { + if x: + __children__[0].execute(__context__) + else: + __children__[1].execute(__context__) + } + [ + Block: + [ + Code: {print(y)} [] + Literal: + AAAA + ] + Block: + [ + Literal: + BBBB + ] + ] + ] + + >>> print(parse_template('dummy.file', text='''% + ... %if x: + ... % print(y) + ... AAAA + ... %# This is a comment + ... %else: + ... BBBB + ... ''')) + Block: + [ + Code: + { + if x: + __children__[0].execute(__context__) + # This is a comment + else: + __children__[1].execute(__context__) + } + [ + Block: + [ + Code: {print(y)} [] + Literal: + AAAA + ] + Block: + [ + Literal: + BBBB + ] + ] + ] + + >>> print(parse_template('dummy.file', text='''\ + ... %for x in y: + ... AAAA + ... %if x: + ... BBBB + ... %end + ... CCCC + ... ''')) + Block: + [ + Code: + { + for x in y: + __children__[0].execute(__context__) + } + [ + Block: + [ + Literal: + AAAA + Code: + { + if x: + __children__[0].execute(__context__) + } + [ + Block: + [ + Literal: + BBBB + ] + ] + Literal: + CCCC + ] + ] + ] + """ + return Block(ParseContext(filename, text)) + + +def execute_template( + ast, line_directive=_default_line_directive, **local_bindings): + r"""Return the text generated by executing the given template AST. + + Keyword arguments become local variable bindings in the execution context + + >>> root_directory = os.path.abspath('/') + >>> file_name = (root_directory + 'dummy.file').replace('\\', '/') + >>> ast = parse_template(file_name, text= + ... '''Nothing + ... % if x: + ... % for i in range(3): + ... ${i} + ... % end + ... % else: + ... THIS SHOULD NOT APPEAR IN THE OUTPUT + ... ''') + >>> out = execute_template(ast, + ... line_directive='//#sourceLocation(file: "%(file)s", line: %(line)d)', + ... x=1) + >>> out = out.replace(file_name, "DUMMY-FILE") + >>> print(out, end="") + //#sourceLocation(file: "DUMMY-FILE", line: 1) + Nothing + //#sourceLocation(file: "DUMMY-FILE", line: 4) + 0 + //#sourceLocation(file: "DUMMY-FILE", line: 4) + 1 + //#sourceLocation(file: "DUMMY-FILE", line: 4) + 2 + + >>> ast = parse_template(file_name, text= + ... '''Nothing + ... % a = [] + ... % for x in range(3): + ... % a.append(x) + ... % end + ... ${a} + ... ''') + >>> out = execute_template(ast, + ... line_directive='//#sourceLocation(file: "%(file)s", line: %(line)d)', + ... x=1) + >>> out = out.replace(file_name, "DUMMY-FILE") + >>> print(out, end="") + //#sourceLocation(file: "DUMMY-FILE", line: 1) + Nothing + //#sourceLocation(file: "DUMMY-FILE", line: 6) + [0, 1, 2] + + >>> ast = parse_template(file_name, text= + ... '''Nothing + ... % a = [] + ... % for x in range(3): + ... % a.append(x) + ... % end + ... ${a} + ... ''') + >>> out = execute_template(ast, + ... line_directive='#line %(line)d "%(file)s"', x=1) + >>> out = out.replace(file_name, "DUMMY-FILE") + >>> print(out, end="") + #line 1 "DUMMY-FILE" + Nothing + #line 6 "DUMMY-FILE" + [0, 1, 2] + """ + execution_context = ExecutionContext( + line_directive=line_directive, **local_bindings) + ast.execute(execution_context) + return ''.join(execution_context.result_text) + + +def main(): + import argparse + import sys + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='Generate Your Boilerplate!', epilog=''' + A GYB template consists of the following elements: + + - Literal text which is inserted directly into the output + + - %% or $$ in literal text, which insert literal '%' and '$' + symbols respectively. + + - Substitutions of the form ${}. The Python + expression is converted to a string and the result is inserted + into the output. + + - Python code delimited by %{...}%. Typically used to inject + definitions (functions, classes, variable bindings) into the + evaluation context of the template. Common indentation is + stripped, so you can add as much indentation to the beginning + of this code as you like + + - Lines beginning with optional whitespace followed by a single + '%' and Python code. %-lines allow you to nest other + constructs inside them. To close a level of nesting, use the + "%end" construct. + + - Lines beginning with optional whitespace and followed by a + single '%' and the token "end", which close open constructs in + %-lines. + + Example template: + + - Hello - + %{ + x = 42 + def succ(a): + return a+1 + }% + + I can assure you that ${x} < ${succ(x)} + + % if int(y) > 7: + % for i in range(3): + y is greater than seven! + % end + % else: + y is less than or equal to seven + % end + + - The End. - + + When run with "gyb -Dy=9", the output is + + - Hello - + + I can assure you that 42 < 43 + + y is greater than seven! + y is greater than seven! + y is greater than seven! + + - The End. - +''' + ) + parser.add_argument( + '-D', action='append', dest='defines', metavar='NAME=VALUE', + default=[], + help='''Bindings to be set in the template's execution context''') + + parser.add_argument( + 'file', type=argparse.FileType(), + help='Path to GYB template file (defaults to stdin)', nargs='?', + default=sys.stdin) + parser.add_argument( + '-o', dest='target', type=argparse.FileType('w'), + help='Output file (defaults to stdout)', default=sys.stdout) + parser.add_argument( + '--test', action='store_true', + default=False, help='Run a self-test') + parser.add_argument( + '--verbose-test', action='store_true', + default=False, help='Run a verbose self-test') + parser.add_argument( + '--dump', action='store_true', + default=False, help='Dump the parsed template to stdout') + parser.add_argument( + '--line-directive', + default=_default_line_directive, + help=''' + Line directive format string, which will be + provided 2 substitutions, `%%(line)d` and `%%(file)s`. + + Example: `#sourceLocation(file: "%%(file)s", line: %%(line)d)` + + The default works automatically with the `line-directive` tool, + which see for more information. + ''') + + args = parser.parse_args(sys.argv[1:]) + + if args.test or args.verbose_test: + import doctest + selfmod = sys.modules[__name__] + if doctest.testmod(selfmod, verbose=args.verbose_test or None).failed: + sys.exit(1) + + bindings = dict(x.split('=', 1) for x in args.defines) + ast = parse_template(args.file.name, args.file.read()) + if args.dump: + print(ast) + # Allow the template to open files and import .py files relative to its own + # directory + os.chdir(os.path.dirname(os.path.abspath(args.file.name))) + sys.path = ['.'] + sys.path + + args.target.write(execute_template(ast, args.line_directive, **bindings)) + + +if __name__ == '__main__': + main() diff --git a/Utils/gyb_utils.py b/Utils/gyb_utils.py new file mode 100644 index 000000000..a7eff165b --- /dev/null +++ b/Utils/gyb_utils.py @@ -0,0 +1,34 @@ +#===-----------------------------------------------------------------------===// +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2023 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 +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +#===-----------------------------------------------------------------------===// + +def autogenerated_warning(): + return """ +// ############################################################################# +// # # +// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # +// # # +// ############################################################################# +""" + +visibility_levels = ["internal", "public"] +def visibility_boilerplate(part): + if part == "internal": + return """ +// In single module mode, we need these declarations to be internal, +// but in regular builds we want them to be public. Unfortunately +// the current best way to do this is to duplicate all definitions. +#if COLLECTIONS_SINGLE_MODULE""" + + if part == "public": + return "#else // !COLLECTIONS_SINGLE_MODULE" + if part == "end": + return "#endif // COLLECTIONS_SINGLE_MODULE" diff --git a/Utils/swift-collections.xcworkspace/xcshareddata/xcschemes/_RopeModule.xcscheme b/Utils/swift-collections.xcworkspace/xcshareddata/xcschemes/_RopeModule.xcscheme new file mode 100644 index 000000000..eecd34c58 --- /dev/null +++ b/Utils/swift-collections.xcworkspace/xcshareddata/xcschemes/_RopeModule.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Xcode/Collections.xcconfig b/Xcode/Collections.xcconfig new file mode 100644 index 000000000..13193a678 --- /dev/null +++ b/Xcode/Collections.xcconfig @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +PRODUCT_NAME = Collections +PRODUCT_BUNDLE_IDENTIFIER = org.swift.Collections + +SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator +ARCHS = $(ARCHS_STANDARD) + +MACOSX_DEPLOYMENT_TARGET = 12.0 +IPHONEOS_DEPLOYMENT_TARGET = 15.0 +WATCHOS_DEPLOYMENT_TARGET = 8.0 +TVOS_DEPLOYMENT_TARGET = 15.0 + +MARKETING_VERSION = 1.1 + +CURRENT_PROJECT_VERSION = 1 +VERSIONING_SYSTEM = apple-generic +VERSION_INFO_PREFIX = + +DYLIB_COMPATIBILITY_VERSION = $(CURRENT_PROJECT_VERSION) +DYLIB_CURRENT_VERSION = $(CURRENT_PROJECT_VERSION) + +INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks +SKIP_INSTALL = YES +DYLIB_INSTALL_NAME_BASE = @rpath +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks + +ENABLE_TESTABILITY = NO +ENABLE_TESTABILITY[config=Debug] = YES + +GENERATE_INFOPLIST_FILE = YES +CODE_SIGN_STYLE = Automatic diff --git a/Xcode/Collections.xcodeproj/project.pbxproj b/Xcode/Collections.xcodeproj/project.pbxproj new file mode 100644 index 000000000..adec5ddec --- /dev/null +++ b/Xcode/Collections.xcodeproj/project.pbxproj @@ -0,0 +1,2434 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 7D9B859729E4F74400B291CD /* BitArray+Shifts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859129E4F74400B291CD /* BitArray+Shifts.swift */; }; + 7D9B859829E4F74400B291CD /* BitArray+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859229E4F74400B291CD /* BitArray+Descriptions.swift */; }; + 7D9B859929E4F74400B291CD /* BitArray+LosslessStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859329E4F74400B291CD /* BitArray+LosslessStringConvertible.swift */; }; + 7D9B859A29E4F74400B291CD /* BitArray+RandomBits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859429E4F74400B291CD /* BitArray+RandomBits.swift */; }; + 7D9B859B29E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859529E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift */; }; + 7DE91B2F29CA6721004483EB /* Collections.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DE91B2629CA6721004483EB /* Collections.framework */; }; + 7DE9200D29CA70F3004483EB /* OrderedDictionary+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7529CA70F3004483EB /* OrderedDictionary+Equatable.swift */; }; + 7DE9200E29CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7629CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift */; }; + 7DE9200F29CA70F3004483EB /* OrderedDictionary+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7729CA70F3004483EB /* OrderedDictionary+Hashable.swift */; }; + 7DE9201029CA70F3004483EB /* OrderedDictionary+Deprecations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7829CA70F3004483EB /* OrderedDictionary+Deprecations.swift */; }; + 7DE9201129CA70F3004483EB /* OrderedDictionary+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7929CA70F3004483EB /* OrderedDictionary+Initializers.swift */; }; + 7DE9201229CA70F3004483EB /* OrderedDictionary+Elements.SubSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7A29CA70F3004483EB /* OrderedDictionary+Elements.SubSequence.swift */; }; + 7DE9201329CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7B29CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift */; }; + 7DE9201429CA70F3004483EB /* OrderedDictionary+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7C29CA70F3004483EB /* OrderedDictionary+Sequence.swift */; }; + 7DE9201529CA70F3004483EB /* OrderedDictionary+Partial RangeReplaceableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7D29CA70F3004483EB /* OrderedDictionary+Partial RangeReplaceableCollection.swift */; }; + 7DE9201629CA70F3004483EB /* OrderedDictionary+Elements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7E29CA70F3004483EB /* OrderedDictionary+Elements.swift */; }; + 7DE9201729CA70F3004483EB /* OrderedDictionary+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7F29CA70F3004483EB /* OrderedDictionary+Codable.swift */; }; + 7DE9201829CA70F3004483EB /* OrderedDictionary+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8029CA70F3004483EB /* OrderedDictionary+Invariants.swift */; }; + 7DE9201929CA70F3004483EB /* OrderedDictionary+Values.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8129CA70F3004483EB /* OrderedDictionary+Values.swift */; }; + 7DE9201A29CA70F3004483EB /* OrderedDictionary+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8229CA70F3004483EB /* OrderedDictionary+Sendable.swift */; }; + 7DE9201B29CA70F3004483EB /* OrderedDictionary+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8329CA70F3004483EB /* OrderedDictionary+Descriptions.swift */; }; + 7DE9201C29CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8429CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift */; }; + 7DE9201D29CA70F3004483EB /* OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8529CA70F3004483EB /* OrderedDictionary.swift */; }; + 7DE9201E29CA70F3004483EB /* OrderedSet+Partial SetAlgebra symmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8729CA70F3004483EB /* OrderedSet+Partial SetAlgebra symmetricDifference.swift */; }; + 7DE9201F29CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8829CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift */; }; + 7DE9202029CA70F3004483EB /* OrderedSet+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8929CA70F3004483EB /* OrderedSet+Descriptions.swift */; }; + 7DE9202129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift */; }; + 7DE9202229CA70F3004483EB /* OrderedSet+Diffing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8B29CA70F3004483EB /* OrderedSet+Diffing.swift */; }; + 7DE9202329CA70F3004483EB /* OrderedSet+UnstableInternals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8C29CA70F3004483EB /* OrderedSet+UnstableInternals.swift */; }; + 7DE9202429CA70F3004483EB /* OrderedSet+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8D29CA70F3004483EB /* OrderedSet+Invariants.swift */; }; + 7DE9202529CA70F3004483EB /* OrderedSet+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8E29CA70F3004483EB /* OrderedSet+Hashable.swift */; }; + 7DE9202629CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E8F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift */; }; + 7DE9202729CA70F3004483EB /* OrderedSet+Insertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9029CA70F3004483EB /* OrderedSet+Insertions.swift */; }; + 7DE9202829CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSubset.swift */; }; + 7DE9202929CA70F3004483EB /* OrderedSet+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9229CA70F3004483EB /* OrderedSet+Codable.swift */; }; + 7DE9202A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9329CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift */; }; + 7DE9202B29CA70F3004483EB /* OrderedSet+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9429CA70F3004483EB /* OrderedSet+Equatable.swift */; }; + 7DE9202C29CA70F3004483EB /* OrderedSet+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9529CA70F3004483EB /* OrderedSet+Initializers.swift */; }; + 7DE9202D29CA70F3004483EB /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9629CA70F3004483EB /* OrderedSet.swift */; }; + 7DE9202E29CA70F3004483EB /* OrderedSet+UnorderedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9729CA70F3004483EB /* OrderedSet+UnorderedView.swift */; }; + 7DE9202F29CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9829CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift */; }; + 7DE9203029CA70F3004483EB /* OrderedSet+SubSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9929CA70F3004483EB /* OrderedSet+SubSequence.swift */; }; + 7DE9203129CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9A29CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift */; }; + 7DE9203229CA70F3004483EB /* OrderedSet+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9B29CA70F3004483EB /* OrderedSet+Sendable.swift */; }; + 7DE9203329CA70F3004483EB /* OrderedSet+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9C29CA70F3004483EB /* OrderedSet+Testing.swift */; }; + 7DE9203429CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9D29CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift */; }; + 7DE9203529CA70F3004483EB /* OrderedSet+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9E29CA70F3004483EB /* OrderedSet+CustomReflectable.swift */; }; + 7DE9203629CA70F3004483EB /* OrderedSet+ReserveCapacity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E9F29CA70F3004483EB /* OrderedSet+ReserveCapacity.swift */; }; + 7DE9203729CA70F3004483EB /* OrderedSet+Partial SetAlgebra union.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA029CA70F3004483EB /* OrderedSet+Partial SetAlgebra union.swift */; }; + 7DE9203829CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA129CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift */; }; + 7DE9203929CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA229CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift */; }; + 7DE9203A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra+Basics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA329CA70F3004483EB /* OrderedSet+Partial SetAlgebra+Basics.swift */; }; + 7DE9203B29CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA429CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift */; }; + 7DE9203C29CA70F3004483EB /* OrderedSet+Partial SetAlgebra formSymmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA529CA70F3004483EB /* OrderedSet+Partial SetAlgebra formSymmetricDifference.swift */; }; + 7DE9203D29CA70F3004483EB /* OrderedSet+Partial SetAlgebra intersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA629CA70F3004483EB /* OrderedSet+Partial SetAlgebra intersection.swift */; }; + 7DE9203E29CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA729CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtract.swift */; }; + 7DE9203F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA829CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift */; }; + 7DE9204029CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EA929CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSuperset.swift */; }; + 7DE9204129CA70F3004483EB /* _UnsafeBitset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EAB29CA70F3004483EB /* _UnsafeBitset.swift */; }; + 7DE9204229CA70F3004483EB /* _HashTable+UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EAD29CA70F3004483EB /* _HashTable+UnsafeHandle.swift */; }; + 7DE9204329CA70F3004483EB /* _HashTable+Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EAE29CA70F3004483EB /* _HashTable+Bucket.swift */; }; + 7DE9204429CA70F3004483EB /* _HashTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EAF29CA70F3004483EB /* _HashTable.swift */; }; + 7DE9204529CA70F3004483EB /* _HashTable+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB029CA70F3004483EB /* _HashTable+Constants.swift */; }; + 7DE9204629CA70F3004483EB /* _Hashtable+Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB129CA70F3004483EB /* _Hashtable+Header.swift */; }; + 7DE9204729CA70F3004483EB /* _HashTable+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB229CA70F3004483EB /* _HashTable+Testing.swift */; }; + 7DE9204829CA70F3004483EB /* _HashTable+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB329CA70F3004483EB /* _HashTable+CustomStringConvertible.swift */; }; + 7DE9204929CA70F3004483EB /* _HashTable+BucketIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB429CA70F3004483EB /* _HashTable+BucketIterator.swift */; }; + 7DE9204B29CA70F3004483EB /* _DequeBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */; }; + 7DE9204C29CA70F3004483EB /* Deque+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */; }; + 7DE9204E29CA70F3004483EB /* Deque+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */; }; + 7DE9204F29CA70F3004483EB /* Deque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBB29CA70F3004483EB /* Deque.swift */; }; + 7DE9205029CA70F3004483EB /* _DequeSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */; }; + 7DE9205129CA70F3004483EB /* Deque+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */; }; + 7DE9205229CA70F3004483EB /* _UnsafeWrappedBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */; }; + 7DE9205329CA70F3004483EB /* _DequeBufferHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */; }; + 7DE9205429CA70F3004483EB /* Deque+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */; }; + 7DE9205529CA70F3004483EB /* Deque+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */; }; + 7DE9205629CA70F3004483EB /* Deque+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */; }; + 7DE9205829CA70F3004483EB /* Deque._UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */; }; + 7DE9205929CA70F3004483EB /* Deque+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */; }; + 7DE9205A29CA70F3004483EB /* Deque+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */; }; + 7DE9205B29CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */; }; + 7DE9205C29CA70F3004483EB /* Deque+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */; }; + 7DE9205D29CA70F4004483EB /* Deque._Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */; }; + 7DE9205E29CA70F4004483EB /* BigString+Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECD29CA70F3004483EB /* BigString+Metrics.swift */; }; + 7DE9205F29CA70F4004483EB /* BigString+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECE29CA70F3004483EB /* BigString+Index.swift */; }; + 7DE9206029CA70F4004483EB /* BigString+Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECF29CA70F3004483EB /* BigString+Summary.swift */; }; + 7DE9206129CA70F4004483EB /* BigString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED029CA70F3004483EB /* BigString.swift */; }; + 7DE9206229CA70F4004483EB /* BigString+Iterators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED129CA70F3004483EB /* BigString+Iterators.swift */; }; + 7DE9206329CA70F4004483EB /* BigString+Contents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED229CA70F3004483EB /* BigString+Contents.swift */; }; + 7DE9206429CA70F4004483EB /* BigString+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED329CA70F3004483EB /* BigString+Invariants.swift */; }; + 7DE9206529CA70F4004483EB /* BigString+Ingester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED429CA70F3004483EB /* BigString+Ingester.swift */; }; + 7DE9206629CA70F4004483EB /* BigString+Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED529CA70F3004483EB /* BigString+Debugging.swift */; }; + 7DE9206729CA70F4004483EB /* BigString+Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED629CA70F3004483EB /* BigString+Builder.swift */; }; + 7DE9206829CA70F4004483EB /* BigString+Chunk+Append and Insert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED829CA70F3004483EB /* BigString+Chunk+Append and Insert.swift */; }; + 7DE9206929CA70F4004483EB /* BigString+Chunk+Indexing by UTF16.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ED929CA70F3004483EB /* BigString+Chunk+Indexing by UTF16.swift */; }; + 7DE9206A29CA70F4004483EB /* BigString+Chunk+Counts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDA29CA70F3004483EB /* BigString+Chunk+Counts.swift */; }; + 7DE9206B29CA70F4004483EB /* BigString+Chunk+Indexing by Characters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDB29CA70F3004483EB /* BigString+Chunk+Indexing by Characters.swift */; }; + 7DE9206C29CA70F4004483EB /* BigString+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDC29CA70F3004483EB /* BigString+Chunk.swift */; }; + 7DE9206D29CA70F4004483EB /* BigString+Chunk+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDD29CA70F3004483EB /* BigString+Chunk+Description.swift */; }; + 7DE9206E29CA70F4004483EB /* BigString+Chunk+Splitting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDE29CA70F3004483EB /* BigString+Chunk+Splitting.swift */; }; + 7DE9206F29CA70F4004483EB /* BigString+Chunk+Breaks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EDF29CA70F3004483EB /* BigString+Chunk+Breaks.swift */; }; + 7DE9207029CA70F4004483EB /* BigString+Chunk+RopeElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE029CA70F3004483EB /* BigString+Chunk+RopeElement.swift */; }; + 7DE9207129CA70F4004483EB /* BigString+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE229CA70F3004483EB /* BigString+Split.swift */; }; + 7DE9207229CA70F4004483EB /* BigString+Managing Breaks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE329CA70F3004483EB /* BigString+Managing Breaks.swift */; }; + 7DE9207329CA70F4004483EB /* BigString+RemoveSubrange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE429CA70F3004483EB /* BigString+RemoveSubrange.swift */; }; + 7DE9207429CA70F4004483EB /* BigString+ReplaceSubrange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE529CA70F3004483EB /* BigString+ReplaceSubrange.swift */; }; + 7DE9207529CA70F4004483EB /* BigString+Insert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE629CA70F3004483EB /* BigString+Insert.swift */; }; + 7DE9207629CA70F4004483EB /* BigString+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE729CA70F3004483EB /* BigString+Initializers.swift */; }; + 7DE9207729CA70F4004483EB /* Range+BigString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE829CA70F3004483EB /* Range+BigString.swift */; }; + 7DE9207829CA70F4004483EB /* BigString+Append.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EE929CA70F3004483EB /* BigString+Append.swift */; }; + 7DE9207929CA70F4004483EB /* BigString+UnicodeScalarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EEB29CA70F3004483EB /* BigString+UnicodeScalarView.swift */; }; + 7DE9207A29CA70F4004483EB /* BigString+UTF8View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EEC29CA70F3004483EB /* BigString+UTF8View.swift */; }; + 7DE9207B29CA70F4004483EB /* BigSubstring+UnicodeScalarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EED29CA70F3004483EB /* BigSubstring+UnicodeScalarView.swift */; }; + 7DE9207C29CA70F4004483EB /* BigSubstring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EEE29CA70F3004483EB /* BigSubstring.swift */; }; + 7DE9207D29CA70F4004483EB /* BigSubstring+UTF16View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EEF29CA70F3004483EB /* BigSubstring+UTF16View.swift */; }; + 7DE9207E29CA70F4004483EB /* BigString+UTF16View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF029CA70F3004483EB /* BigString+UTF16View.swift */; }; + 7DE9207F29CA70F4004483EB /* BigSubstring+UTF8View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF129CA70F3004483EB /* BigSubstring+UTF8View.swift */; }; + 7DE9208029CA70F4004483EB /* BigString+Hashing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF329CA70F3004483EB /* BigString+Hashing.swift */; }; + 7DE9208129CA70F4004483EB /* BigString+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF429CA70F3004483EB /* BigString+CustomStringConvertible.swift */; }; + 7DE9208229CA70F4004483EB /* BigString+BidirectionalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF529CA70F3004483EB /* BigString+BidirectionalCollection.swift */; }; + 7DE9208329CA70F4004483EB /* BigString+CustomDebugStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF629CA70F3004483EB /* BigString+CustomDebugStringConvertible.swift */; }; + 7DE9208429CA70F4004483EB /* BigString+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF729CA70F3004483EB /* BigString+Equatable.swift */; }; + 7DE9208529CA70F4004483EB /* BigString+TextOutputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF829CA70F3004483EB /* BigString+TextOutputStream.swift */; }; + 7DE9208629CA70F4004483EB /* BigString+LosslessStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EF929CA70F3004483EB /* BigString+LosslessStringConvertible.swift */; }; + 7DE9208729CA70F4004483EB /* BigString+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EFA29CA70F3004483EB /* BigString+Sequence.swift */; }; + 7DE9208829CA70F4004483EB /* BigString+ExpressibleByStringLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EFB29CA70F3004483EB /* BigString+ExpressibleByStringLiteral.swift */; }; + 7DE9208929CA70F4004483EB /* BigString+RangeReplaceableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EFC29CA70F3004483EB /* BigString+RangeReplaceableCollection.swift */; }; + 7DE9208A29CA70F4004483EB /* BigString+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EFD29CA70F3004483EB /* BigString+Comparable.swift */; }; + 7DE9208B29CA70F4004483EB /* RopeMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0029CA70F3004483EB /* RopeMetric.swift */; }; + 7DE9208C29CA70F4004483EB /* _RopeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0129CA70F3004483EB /* _RopeItem.swift */; }; + 7DE9208D29CA70F4004483EB /* Rope+Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0229CA70F3004483EB /* Rope+Debugging.swift */; }; + 7DE9208E29CA70F4004483EB /* _RopeVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0329CA70F3004483EB /* _RopeVersion.swift */; }; + 7DE9208F29CA70F4004483EB /* RopeElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0429CA70F3004483EB /* RopeElement.swift */; }; + 7DE9209029CA70F4004483EB /* Rope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0529CA70F3004483EB /* Rope.swift */; }; + 7DE9209129CA70F4004483EB /* Rope+_UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0629CA70F3004483EB /* Rope+_UnsafeHandle.swift */; }; + 7DE9209229CA70F4004483EB /* Rope+_Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0729CA70F3004483EB /* Rope+_Storage.swift */; }; + 7DE9209329CA70F4004483EB /* Rope+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0829CA70F3004483EB /* Rope+Invariants.swift */; }; + 7DE9209429CA70F4004483EB /* Rope+_UnmanagedLeaf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0929CA70F3004483EB /* Rope+_UnmanagedLeaf.swift */; }; + 7DE9209529CA70F4004483EB /* RopeSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0A29CA70F3004483EB /* RopeSummary.swift */; }; + 7DE9209629CA70F4004483EB /* Rope+Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0B29CA70F3004483EB /* Rope+Builder.swift */; }; + 7DE9209729CA70F4004483EB /* _RopePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0C29CA70F3004483EB /* _RopePath.swift */; }; + 7DE9209829CA70F4004483EB /* Rope+_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0D29CA70F3004483EB /* Rope+_Node.swift */; }; + 7DE9209929CA70F4004483EB /* Rope+Extract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F0F29CA70F3004483EB /* Rope+Extract.swift */; }; + 7DE9209A29CA70F4004483EB /* Rope+Append.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1029CA70F3004483EB /* Rope+Append.swift */; }; + 7DE9209B29CA70F4004483EB /* Rope+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1129CA70F3004483EB /* Rope+Split.swift */; }; + 7DE9209C29CA70F4004483EB /* Rope+Find.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1229CA70F3004483EB /* Rope+Find.swift */; }; + 7DE9209D29CA70F4004483EB /* Rope+Insert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1329CA70F3004483EB /* Rope+Insert.swift */; }; + 7DE9209E29CA70F4004483EB /* Rope+ForEachWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1429CA70F3004483EB /* Rope+ForEachWhile.swift */; }; + 7DE9209F29CA70F4004483EB /* Rope+MutatingForEach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1529CA70F3004483EB /* Rope+MutatingForEach.swift */; }; + 7DE920A029CA70F4004483EB /* Rope+Join.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1629CA70F3004483EB /* Rope+Join.swift */; }; + 7DE920A129CA70F4004483EB /* Rope+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1729CA70F3004483EB /* Rope+Remove.swift */; }; + 7DE920A229CA70F4004483EB /* Rope+RemoveSubrange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1829CA70F3004483EB /* Rope+RemoveSubrange.swift */; }; + 7DE920A329CA70F4004483EB /* Rope+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1A29CA70F3004483EB /* Rope+Index.swift */; }; + 7DE920A429CA70F4004483EB /* Rope+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1B29CA70F3004483EB /* Rope+Sequence.swift */; }; + 7DE920A529CA70F4004483EB /* Rope+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1C29CA70F3004483EB /* Rope+Collection.swift */; }; + 7DE920A629CA70F4004483EB /* String Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1E29CA70F3004483EB /* String Utilities.swift */; }; + 7DE920A729CA70F4004483EB /* _CharacterRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F1F29CA70F3004483EB /* _CharacterRecognizer.swift */; }; + 7DE920A829CA70F4004483EB /* String.Index+ABI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F2029CA70F3004483EB /* String.Index+ABI.swift */; }; + 7DE920A929CA70F4004483EB /* Optional Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F2129CA70F3004483EB /* Optional Utilities.swift */; }; + 7DE920B529CA70F4004483EB /* _SortedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3029CA70F3004483EB /* _SortedCollection.swift */; }; + 7DE920BC29CA70F4004483EB /* _UniqueCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3829CA70F3004483EB /* _UniqueCollection.swift */; }; + 7DE920BD29CA70F4004483EB /* BitSet+SetAlgebra symmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3B29CA70F3004483EB /* BitSet+SetAlgebra symmetricDifference.swift */; }; + 7DE920BE29CA70F4004483EB /* BitSet+SetAlgebra formIntersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3C29CA70F3004483EB /* BitSet+SetAlgebra formIntersection.swift */; }; + 7DE920BF29CA70F4004483EB /* BitSet+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3D29CA70F3004483EB /* BitSet+Codable.swift */; }; + 7DE920C029CA70F4004483EB /* BitSet+SetAlgebra conformance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3E29CA70F3004483EB /* BitSet+SetAlgebra conformance.swift */; }; + 7DE920C129CA70F4004483EB /* BitSet+SetAlgebra subtract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3F29CA70F3004483EB /* BitSet+SetAlgebra subtract.swift */; }; + 7DE920C229CA70F4004483EB /* BitSet+Sorted Collection APIs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4029CA70F3004483EB /* BitSet+Sorted Collection APIs.swift */; }; + 7DE920C329CA70F4004483EB /* BitSet+SetAlgebra formUnion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4129CA70F3004483EB /* BitSet+SetAlgebra formUnion.swift */; }; + 7DE920C429CA70F4004483EB /* BitSet+SetAlgebra isStrictSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4229CA70F3004483EB /* BitSet+SetAlgebra isStrictSuperset.swift */; }; + 7DE920C529CA70F4004483EB /* BitSet+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4329CA70F3004483EB /* BitSet+Extras.swift */; }; + 7DE920C629CA70F4004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4429CA70F3004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift */; }; + 7DE920C729CA70F4004483EB /* BitSet+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4529CA70F3004483EB /* BitSet+ExpressibleByArrayLiteral.swift */; }; + 7DE920C829CA70F4004483EB /* BitSet+SetAlgebra isEqualSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4629CA70F3004483EB /* BitSet+SetAlgebra isEqualSet.swift */; }; + 7DE920C929CA70F4004483EB /* BitSet+BidirectionalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4729CA70F3004483EB /* BitSet+BidirectionalCollection.swift */; }; + 7DE920CA29CA70F4004483EB /* BitSet+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4829CA70F3004483EB /* BitSet+CustomStringConvertible.swift */; }; + 7DE920CB29CA70F4004483EB /* BitSet+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4929CA70F3004483EB /* BitSet+Random.swift */; }; + 7DE920CC29CA70F4004483EB /* BitSet.Counted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4A29CA70F3004483EB /* BitSet.Counted.swift */; }; + 7DE920CD29CA70F4004483EB /* BitSet+CustomDebugStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4B29CA70F3004483EB /* BitSet+CustomDebugStringConvertible.swift */; }; + 7DE920CE29CA70F4004483EB /* BitSet+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4C29CA70F3004483EB /* BitSet+CustomReflectable.swift */; }; + 7DE920CF29CA70F4004483EB /* BitSet+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4D29CA70F3004483EB /* BitSet+Initializers.swift */; }; + 7DE920D029CA70F4004483EB /* BitSet+SetAlgebra isSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4E29CA70F3004483EB /* BitSet+SetAlgebra isSubset.swift */; }; + 7DE920D129CA70F4004483EB /* BitSet.Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F4F29CA70F3004483EB /* BitSet.Index.swift */; }; + 7DE920D229CA70F4004483EB /* BitSet+SetAlgebra basics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5029CA70F3004483EB /* BitSet+SetAlgebra basics.swift */; }; + 7DE920D329CA70F4004483EB /* BitSet+SetAlgebra isDisjoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5129CA70F3004483EB /* BitSet+SetAlgebra isDisjoint.swift */; }; + 7DE920D429CA70F4004483EB /* BitSet+SetAlgebra union.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5229CA70F3004483EB /* BitSet+SetAlgebra union.swift */; }; + 7DE920D529CA70F4004483EB /* BitSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5329CA70F3004483EB /* BitSet.swift */; }; + 7DE920D629CA70F4004483EB /* BitSet+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5429CA70F3004483EB /* BitSet+Equatable.swift */; }; + 7DE920D729CA70F4004483EB /* BitSet+SetAlgebra isSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5529CA70F3004483EB /* BitSet+SetAlgebra isSuperset.swift */; }; + 7DE920D829CA70F4004483EB /* BitSet+SetAlgebra subtracting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5629CA70F3004483EB /* BitSet+SetAlgebra subtracting.swift */; }; + 7DE920D929CA70F4004483EB /* BitSet+SetAlgebra intersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5729CA70F3004483EB /* BitSet+SetAlgebra intersection.swift */; }; + 7DE920DA29CA70F4004483EB /* BitSet._UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5829CA70F3004483EB /* BitSet._UnsafeHandle.swift */; }; + 7DE920DB29CA70F4004483EB /* BitSet+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5929CA70F3004483EB /* BitSet+Hashable.swift */; }; + 7DE920DC29CA70F4004483EB /* BitSet+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5A29CA70F3004483EB /* BitSet+Invariants.swift */; }; + 7DE920DD29CA70F4004483EB /* BitSet+SetAlgebra isStrictSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5B29CA70F3004483EB /* BitSet+SetAlgebra isStrictSubset.swift */; }; + 7DE920DF29CA70F4004483EB /* Slice+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5E29CA70F3004483EB /* Slice+Utilities.swift */; }; + 7DE920E029CA70F4004483EB /* Range+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F5F29CA70F3004483EB /* Range+Utilities.swift */; }; + 7DE920E129CA70F4004483EB /* UInt+Tricks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6029CA70F3004483EB /* UInt+Tricks.swift */; }; + 7DE920E229CA70F4004483EB /* _Word.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6129CA70F3004483EB /* _Word.swift */; }; + 7DE920E629CA70F4004483EB /* BitArray+ChunkedBitsIterators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6629CA70F3004483EB /* BitArray+ChunkedBitsIterators.swift */; }; + 7DE920E729CA70F4004483EB /* BitArray+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6729CA70F3004483EB /* BitArray+CustomReflectable.swift */; }; + 7DE920E829CA70F4004483EB /* BitArray+BitwiseOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6829CA70F3004483EB /* BitArray+BitwiseOperations.swift */; }; + 7DE920E929CA70F4004483EB /* BitArray+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6929CA70F3004483EB /* BitArray+Invariants.swift */; }; + 7DE920EA29CA70F4004483EB /* BitArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6A29CA70F3004483EB /* BitArray.swift */; }; + 7DE920EB29CA70F4004483EB /* BitArray+Fill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6B29CA70F3004483EB /* BitArray+Fill.swift */; }; + 7DE920EC29CA70F4004483EB /* BitArray+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6C29CA70F3004483EB /* BitArray+Collection.swift */; }; + 7DE920ED29CA70F4004483EB /* BitArray+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6D29CA70F3004483EB /* BitArray+Extras.swift */; }; + 7DE920EE29CA70F4004483EB /* BitArray+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6E29CA70F3004483EB /* BitArray+ExpressibleByArrayLiteral.swift */; }; + 7DE920EF29CA70F4004483EB /* BitArray+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F6F29CA70F3004483EB /* BitArray+Codable.swift */; }; + 7DE920F029CA70F4004483EB /* BitArray+RangeReplaceableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7029CA70F3004483EB /* BitArray+RangeReplaceableCollection.swift */; }; + 7DE920F129CA70F4004483EB /* BitArray+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7129CA70F3004483EB /* BitArray+Initializers.swift */; }; + 7DE920F229CA70F4004483EB /* BitArray._UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7229CA70F3004483EB /* BitArray._UnsafeHandle.swift */; }; + 7DE920F329CA70F4004483EB /* BitArray+Copy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7329CA70F3004483EB /* BitArray+Copy.swift */; }; + 7DE920F429CA70F4004483EB /* BitArray+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7429CA70F3004483EB /* BitArray+Hashable.swift */; }; + 7DE920F629CA70F4004483EB /* BitArray+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7629CA70F3004483EB /* BitArray+Equatable.swift */; }; + 7DE920F829CA70F4004483EB /* BitArray+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7829CA70F3004483EB /* BitArray+Testing.swift */; }; + 7DE9212329CA70F4004483EB /* TreeSet+SetAlgebra isEqualSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FAB29CA70F3004483EB /* TreeSet+SetAlgebra isEqualSet.swift */; }; + 7DE9212429CA70F4004483EB /* TreeSet+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FAC29CA70F3004483EB /* TreeSet+Descriptions.swift */; }; + 7DE9212529CA70F4004483EB /* TreeSet+SetAlgebra isStrictSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FAD29CA70F3004483EB /* TreeSet+SetAlgebra isStrictSubset.swift */; }; + 7DE9212629CA70F4004483EB /* TreeSet+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FAE29CA70F3004483EB /* TreeSet+Sendable.swift */; }; + 7DE9212729CA70F4004483EB /* TreeSet+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FAF29CA70F3004483EB /* TreeSet+Equatable.swift */; }; + 7DE9212829CA70F4004483EB /* TreeSet+SetAlgebra intersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB029CA70F3004483EB /* TreeSet+SetAlgebra intersection.swift */; }; + 7DE9212929CA70F4004483EB /* TreeSet+SetAlgebra formUnion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB129CA70F3004483EB /* TreeSet+SetAlgebra formUnion.swift */; }; + 7DE9212A29CA70F4004483EB /* TreeSet+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB229CA70F3004483EB /* TreeSet+Extras.swift */; }; + 7DE9212B29CA70F4004483EB /* TreeSet+SetAlgebra subtracting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB329CA70F3004483EB /* TreeSet+SetAlgebra subtracting.swift */; }; + 7DE9212C29CA70F4004483EB /* TreeSet+SetAlgebra subtract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB429CA70F3004483EB /* TreeSet+SetAlgebra subtract.swift */; }; + 7DE9212D29CA70F4004483EB /* TreeSet+Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB529CA70F3004483EB /* TreeSet+Debugging.swift */; }; + 7DE9212E29CA70F4004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB629CA70F3004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift */; }; + 7DE9212F29CA70F4004483EB /* TreeSet+SetAlgebra formIntersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB729CA70F3004483EB /* TreeSet+SetAlgebra formIntersection.swift */; }; + 7DE9213029CA70F4004483EB /* TreeSet+SetAlgebra isSuperset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB829CA70F3004483EB /* TreeSet+SetAlgebra isSuperset.swift */; }; + 7DE9213129CA70F4004483EB /* TreeSet+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FB929CA70F3004483EB /* TreeSet+CustomReflectable.swift */; }; + 7DE9213229CA70F4004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBA29CA70F3004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift */; }; + 7DE9213329CA70F4004483EB /* TreeSet+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBB29CA70F3004483EB /* TreeSet+Collection.swift */; }; + 7DE9213429CA70F4004483EB /* TreeSet+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBC29CA70F3004483EB /* TreeSet+ExpressibleByArrayLiteral.swift */; }; + 7DE9213529CA70F4004483EB /* TreeSet+SetAlgebra symmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBD29CA70F3004483EB /* TreeSet+SetAlgebra symmetricDifference.swift */; }; + 7DE9213629CA70F4004483EB /* TreeSet+SetAlgebra basics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBE29CA70F3004483EB /* TreeSet+SetAlgebra basics.swift */; }; + 7DE9213729CA70F4004483EB /* TreeSet+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FBF29CA70F3004483EB /* TreeSet+Sequence.swift */; }; + 7DE9213829CA70F4004483EB /* TreeSet+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC029CA70F3004483EB /* TreeSet+Filter.swift */; }; + 7DE9213929CA70F4004483EB /* TreeSet+SetAlgebra Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC129CA70F3004483EB /* TreeSet+SetAlgebra Initializers.swift */; }; + 7DE9213A29CA70F4004483EB /* TreeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC229CA70F3004483EB /* TreeSet.swift */; }; + 7DE9213B29CA70F4004483EB /* TreeSet+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC329CA70F3004483EB /* TreeSet+Hashable.swift */; }; + 7DE9213C29CA70F4004483EB /* TreeSet+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC429CA70F3004483EB /* TreeSet+Codable.swift */; }; + 7DE9213D29CA70F4004483EB /* TreeSet+SetAlgebra isSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC529CA70F3004483EB /* TreeSet+SetAlgebra isSubset.swift */; }; + 7DE9213E29CA70F4004483EB /* TreeSet+SetAlgebra union.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC629CA70F3004483EB /* TreeSet+SetAlgebra union.swift */; }; + 7DE9213F29CA70F4004483EB /* TreeSet+SetAlgebra isDisjoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC729CA70F3004483EB /* TreeSet+SetAlgebra isDisjoint.swift */; }; + 7DE9214029CA70F4004483EB /* _Hash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FC929CA70F3004483EB /* _Hash.swift */; }; + 7DE9214129CA70F4004483EB /* _HashNode+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCA29CA70F3004483EB /* _HashNode+Initializers.swift */; }; + 7DE9214229CA70F4004483EB /* _HashStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCB29CA70F3004483EB /* _HashStack.swift */; }; + 7DE9214329CA70F4004483EB /* _HashNode+Subtree Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCC29CA70F3004483EB /* _HashNode+Subtree Removals.swift */; }; + 7DE9214429CA70F4004483EB /* _HashTreeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCD29CA70F3004483EB /* _HashTreeIterator.swift */; }; + 7DE9214529CA70F4004483EB /* _HashNode+Structural symmetricDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCE29CA70F3004483EB /* _HashNode+Structural symmetricDifference.swift */; }; + 7DE9214629CA70F4004483EB /* _HashSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FCF29CA70F3004483EB /* _HashSlot.swift */; }; + 7DE9214729CA70F4004483EB /* _HashNode+Primitive Replacement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD029CA70F3004483EB /* _HashNode+Primitive Replacement.swift */; }; + 7DE9214829CA70F4004483EB /* _HashNode+Primitive Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD129CA70F3004483EB /* _HashNode+Primitive Removals.swift */; }; + 7DE9214929CA70F4004483EB /* _HashNode+Structural isEqualSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD229CA70F3004483EB /* _HashNode+Structural isEqualSet.swift */; }; + 7DE9214A29CA70F4004483EB /* _HashNode+Subtree Insertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD329CA70F3004483EB /* _HashNode+Subtree Insertions.swift */; }; + 7DE9214B29CA70F4004483EB /* _HashTreeStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD429CA70F3004483EB /* _HashTreeStatistics.swift */; }; + 7DE9214C29CA70F4004483EB /* _HashNode+Primitive Insertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD529CA70F3004483EB /* _HashNode+Primitive Insertions.swift */; }; + 7DE9214D29CA70F4004483EB /* _HashNode+Structural filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD629CA70F3004483EB /* _HashNode+Structural filter.swift */; }; + 7DE9214E29CA70F4004483EB /* _HashNode+Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD729CA70F3004483EB /* _HashNode+Debugging.swift */; }; + 7DE9214F29CA70F4004483EB /* _Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD829CA70F3004483EB /* _Bucket.swift */; }; + 7DE9215029CA70F4004483EB /* _UnmanagedHashNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FD929CA70F3004483EB /* _UnmanagedHashNode.swift */; }; + 7DE9215129CA70F4004483EB /* _HashNode+Subtree Modify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDA29CA70F3004483EB /* _HashNode+Subtree Modify.swift */; }; + 7DE9215229CA70F4004483EB /* _HashNode+Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDB29CA70F3004483EB /* _HashNode+Builder.swift */; }; + 7DE9215329CA70F4004483EB /* _HashNode+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDC29CA70F3004483EB /* _HashNode+Invariants.swift */; }; + 7DE9215429CA70F4004483EB /* _HashNode+UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDD29CA70F3004483EB /* _HashNode+UnsafeHandle.swift */; }; + 7DE9215529CA70F4004483EB /* _RawHashNode+UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDE29CA70F3004483EB /* _RawHashNode+UnsafeHandle.swift */; }; + 7DE9215629CA70F4004483EB /* _HashNode+Structural compactMapValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FDF29CA70F3004483EB /* _HashNode+Structural compactMapValues.swift */; }; + 7DE9215729CA70F4004483EB /* _RawHashNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE029CA70F3004483EB /* _RawHashNode.swift */; }; + 7DE9215829CA70F4004483EB /* _HashNode+Structural union.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE129CA70F3004483EB /* _HashNode+Structural union.swift */; }; + 7DE9215929CA70F4004483EB /* _HashNode+Structural isSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE229CA70F3004483EB /* _HashNode+Structural isSubset.swift */; }; + 7DE9215A29CA70F4004483EB /* _HashNode+Structural subtracting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE329CA70F3004483EB /* _HashNode+Structural subtracting.swift */; }; + 7DE9215B29CA70F4004483EB /* _HashNode+Structural mapValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE429CA70F3004483EB /* _HashNode+Structural mapValues.swift */; }; + 7DE9215C29CA70F4004483EB /* _Bitmap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE529CA70F3004483EB /* _Bitmap.swift */; }; + 7DE9215D29CA70F4004483EB /* _StorageHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE629CA70F3004483EB /* _StorageHeader.swift */; }; + 7DE9215E29CA70F4004483EB /* _HashNode+Lookups.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE729CA70F3004483EB /* _HashNode+Lookups.swift */; }; + 7DE9215F29CA70F4004483EB /* _UnsafePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE829CA70F3004483EB /* _UnsafePath.swift */; }; + 7DE9216029CA70F4004483EB /* _HashNode+Structural isDisjoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FE929CA70F3004483EB /* _HashNode+Structural isDisjoint.swift */; }; + 7DE9216129CA70F4004483EB /* _HashNode+Structural merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FEA29CA70F3004483EB /* _HashNode+Structural merge.swift */; }; + 7DE9216229CA70F4004483EB /* _AncestorHashSlots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FEB29CA70F3004483EB /* _AncestorHashSlots.swift */; }; + 7DE9216329CA70F4004483EB /* _HashLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FEC29CA70F3004483EB /* _HashLevel.swift */; }; + 7DE9216429CA70F4004483EB /* _HashNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FED29CA70F3004483EB /* _HashNode.swift */; }; + 7DE9216529CA70F4004483EB /* _HashNode+Structural intersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FEE29CA70F3004483EB /* _HashNode+Structural intersection.swift */; }; + 7DE9216629CA70F4004483EB /* _HashNode+Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FEF29CA70F3004483EB /* _HashNode+Storage.swift */; }; + 7DE9216729CA70F4004483EB /* TreeDictionary+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF129CA70F3004483EB /* TreeDictionary+Equatable.swift */; }; + 7DE9216829CA70F4004483EB /* TreeDictionary+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF229CA70F3004483EB /* TreeDictionary+Sequence.swift */; }; + 7DE9216929CA70F4004483EB /* TreeDictionary+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF329CA70F3004483EB /* TreeDictionary+Filter.swift */; }; + 7DE9216A29CA70F4004483EB /* TreeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF429CA70F3004483EB /* TreeDictionary.swift */; }; + 7DE9216B29CA70F4004483EB /* TreeDictionary+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF529CA70F3004483EB /* TreeDictionary+Codable.swift */; }; + 7DE9216C29CA70F4004483EB /* TreeDictionary+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF629CA70F3004483EB /* TreeDictionary+Descriptions.swift */; }; + 7DE9216D29CA70F4004483EB /* TreeDictionary+Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF729CA70F3004483EB /* TreeDictionary+Debugging.swift */; }; + 7DE9216E29CA70F4004483EB /* TreeDictionary+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF829CA70F3004483EB /* TreeDictionary+Hashable.swift */; }; + 7DE9216F29CA70F4004483EB /* TreeDictionary+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FF929CA70F3004483EB /* TreeDictionary+CustomReflectable.swift */; }; + 7DE9217029CA70F4004483EB /* TreeDictionary+ExpressibleByDictionaryLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFA29CA70F3004483EB /* TreeDictionary+ExpressibleByDictionaryLiteral.swift */; }; + 7DE9217129CA70F4004483EB /* TreeDictionary+Values.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFB29CA70F3004483EB /* TreeDictionary+Values.swift */; }; + 7DE9217229CA70F4004483EB /* TreeDictionary+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFC29CA70F3004483EB /* TreeDictionary+Sendable.swift */; }; + 7DE9217329CA70F4004483EB /* TreeDictionary+Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFD29CA70F3004483EB /* TreeDictionary+Merge.swift */; }; + 7DE9217429CA70F4004483EB /* TreeDictionary+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFE29CA70F3004483EB /* TreeDictionary+Collection.swift */; }; + 7DE9217529CA70F4004483EB /* TreeDictionary+MapValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91FFF29CA70F3004483EB /* TreeDictionary+MapValues.swift */; }; + 7DE9217629CA70F4004483EB /* TreeDictionary+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200029CA70F3004483EB /* TreeDictionary+Initializers.swift */; }; + 7DE9217729CA70F4004483EB /* TreeDictionary+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200129CA70F3004483EB /* TreeDictionary+Keys.swift */; }; + 7DE9217929CA70F4004483EB /* Heap+UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200429CA70F3004483EB /* Heap+UnsafeHandle.swift */; }; + 7DE9217A29CA70F4004483EB /* Heap+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200529CA70F3004483EB /* Heap+Descriptions.swift */; }; + 7DE9217B29CA70F4004483EB /* Heap+Invariants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200629CA70F3004483EB /* Heap+Invariants.swift */; }; + 7DE9217D29CA70F4004483EB /* Heap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200829CA70F3004483EB /* Heap.swift */; }; + 7DE9217E29CA70F4004483EB /* Heap+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200929CA70F3004483EB /* Heap+ExpressibleByArrayLiteral.swift */; }; + 7DE9217F29CA70F4004483EB /* _HeapNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE9200A29CA70F3004483EB /* _HeapNode.swift */; }; + 7DE921B929CA81DC004483EB /* _SortedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3029CA70F3004483EB /* _SortedCollection.swift */; }; + 7DE921C829CA81DC004483EB /* _UniqueCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F3829CA70F3004483EB /* _UniqueCollection.swift */; }; + 7DE921FA29CA8576004483EB /* UtilitiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921CC29CA8575004483EB /* UtilitiesTests.swift */; }; + 7DE921FB29CA8576004483EB /* MinimalTypeConformances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921CD29CA8575004483EB /* MinimalTypeConformances.swift */; }; + 7DE921FC29CA8576004483EB /* IndexRangeCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921CE29CA8575004483EB /* IndexRangeCollectionTests.swift */; }; + 7DE921FD29CA8576004483EB /* CombinatoricsChecks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921CF29CA8575004483EB /* CombinatoricsChecks.swift */; }; + 7DE921FE29CA8576004483EB /* Hash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D129CA8575004483EB /* Hash.swift */; }; + 7DE921FF29CA8576004483EB /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D229CA8575004483EB /* Utilities.swift */; }; + 7DE9220029CA8576004483EB /* TreeDictionary Smoke Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D329CA8575004483EB /* TreeDictionary Smoke Tests.swift */; }; + 7DE9220129CA8576004483EB /* TreeDictionary.Keys Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D429CA8575004483EB /* TreeDictionary.Keys Tests.swift */; }; + 7DE9220229CA8576004483EB /* TreeDictionary Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D529CA8575004483EB /* TreeDictionary Tests.swift */; }; + 7DE9220329CA8576004483EB /* Colliders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D629CA8575004483EB /* Colliders.swift */; }; + 7DE9220429CA8576004483EB /* TreeHashedCollections Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D729CA8575004483EB /* TreeHashedCollections Fixtures.swift */; }; + 7DE9220529CA8576004483EB /* TreeDictionary.Values Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D829CA8575004483EB /* TreeDictionary.Values Tests.swift */; }; + 7DE9220629CA8576004483EB /* TreeSet Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921D929CA8575004483EB /* TreeSet Tests.swift */; }; + 7DE9220729CA8576004483EB /* BitSet.Counted Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921DB29CA8575004483EB /* BitSet.Counted Tests.swift */; }; + 7DE9220829CA8576004483EB /* BitSetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921DC29CA8575004483EB /* BitSetTests.swift */; }; + 7DE9220929CA8576004483EB /* BitArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921DD29CA8575004483EB /* BitArrayTests.swift */; }; + 7DE9220A29CA8576004483EB /* TestRope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921DF29CA8575004483EB /* TestRope.swift */; }; + 7DE9220C29CA8576004483EB /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E129CA8575004483EB /* Availability.swift */; }; + 7DE9220D29CA8576004483EB /* TestBigString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E229CA8575004483EB /* TestBigString.swift */; }; + 7DE9220E29CA8576004483EB /* SampleStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E329CA8575004483EB /* SampleStrings.swift */; }; + 7DE9220F29CA8576004483EB /* OrderedDictionary Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E629CA8575004483EB /* OrderedDictionary Tests.swift */; }; + 7DE9221029CA8576004483EB /* OrderedDictionary Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E729CA8575004483EB /* OrderedDictionary Utils.swift */; }; + 7DE9221129CA8576004483EB /* OrderedDictionary+Values Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E829CA8575004483EB /* OrderedDictionary+Values Tests.swift */; }; + 7DE9221229CA8576004483EB /* OrderedDictionary+Elements Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921E929CA8575004483EB /* OrderedDictionary+Elements Tests.swift */; }; + 7DE9221329CA8576004483EB /* OrderedSetInternals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921EB29CA8575004483EB /* OrderedSetInternals.swift */; }; + 7DE9221429CA8576004483EB /* OrderedSet Diffing Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921EC29CA8575004483EB /* OrderedSet Diffing Tests.swift */; }; + 7DE9221529CA8576004483EB /* RandomAccessCollection+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921ED29CA8575004483EB /* RandomAccessCollection+Extras.swift */; }; + 7DE9221629CA8576004483EB /* OrderedSet.UnorderedView Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921EE29CA8575004483EB /* OrderedSet.UnorderedView Tests.swift */; }; + 7DE9221729CA8576004483EB /* OrderedSetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921EF29CA8575004483EB /* OrderedSetTests.swift */; }; + 7DE9221829CA8576004483EB /* HashTableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F129CA8575004483EB /* HashTableTests.swift */; }; + 7DE9221929CA8576004483EB /* HeapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F329CA8575004483EB /* HeapTests.swift */; }; + 7DE9221A29CA8576004483EB /* HeapNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F429CA8575004483EB /* HeapNodeTests.swift */; }; + 7DE9221B29CA8576004483EB /* DequeInternals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F629CA8576004483EB /* DequeInternals.swift */; }; + 7DE9221C29CA8576004483EB /* RangeReplaceableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F729CA8576004483EB /* RangeReplaceableCollectionTests.swift */; }; + 7DE9221D29CA8576004483EB /* DequeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F829CA8576004483EB /* DequeTests.swift */; }; + 7DE9221E29CA8576004483EB /* MutableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F929CA8576004483EB /* MutableCollectionTests.swift */; }; + 7DEBDAF229CBEE5300ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */; }; + 7DEBDAF329CBEE5300ADC226 /* Array+WithContiguousStorage Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACE29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift */; }; + 7DEBDAF429CBEE5300ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */; }; + 7DEBDAF529CBEE5300ADC226 /* UnsafeRawPointer extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */; }; + 7DEBDAF829CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */; }; + 7DEBDAF929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */; }; + 7DEBDAFA29CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */; }; + 7DEBDAFB29CBEE5300ADC226 /* Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD729CBEE5300ADC226 /* Descriptions.swift */; }; + 7DEBDAFC29CBEE5300ADC226 /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD829CBEE5300ADC226 /* Debugging.swift */; }; + 7DEBDB0129CBEE5300ADC226 /* _UnsafeBitSet+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDADF29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift */; }; + 7DEBDB0229CBEE5300ADC226 /* _UnsafeBitSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE029CBEE5300ADC226 /* _UnsafeBitSet.swift */; }; + 7DEBDB0329CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE129CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift */; }; + 7DEBDB0829CBEE5300ADC226 /* UInt+reversed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE829CBEE5300ADC226 /* UInt+reversed.swift */; }; + 7DEBDB0929CBEE5300ADC226 /* Integer rank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE929CBEE5300ADC226 /* Integer rank.swift */; }; + 7DEBDB0A29CBEE5300ADC226 /* UInt+first and last set bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEA29CBEE5300ADC226 /* UInt+first and last set bit.swift */; }; + 7DEBDB0B29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEB29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift */; }; + 7DEBDB0E29CBF68900ADC226 /* Integer rank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE929CBEE5300ADC226 /* Integer rank.swift */; }; + 7DEBDB0F29CBF68900ADC226 /* UInt+reversed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE829CBEE5300ADC226 /* UInt+reversed.swift */; }; + 7DEBDB1029CBF68900ADC226 /* UInt+first and last set bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEA29CBEE5300ADC226 /* UInt+first and last set bit.swift */; }; + 7DEBDB1129CBF68900ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEB29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift */; }; + 7DEBDB1229CBF68D00ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */; }; + 7DEBDB1329CBF68D00ADC226 /* Array+WithContiguousStorage Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACE29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift */; }; + 7DEBDB1429CBF68D00ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */; }; + 7DEBDB1529CBF68D00ADC226 /* UnsafeRawPointer extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */; }; + 7DEBDB1629CBF69F00ADC226 /* _UnsafeBitSet+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDADF29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift */; }; + 7DEBDB1729CBF69F00ADC226 /* _UnsafeBitSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE029CBEE5300ADC226 /* _UnsafeBitSet.swift */; }; + 7DEBDB1829CBF69F00ADC226 /* _UnsafeBitSet+_Word.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE129CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift */; }; + 7DEBDB1A29CBF6B200ADC226 /* Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD729CBEE5300ADC226 /* Descriptions.swift */; }; + 7DEBDB1B29CBF6B200ADC226 /* UnsafeBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */; }; + 7DEBDB1C29CBF6B200ADC226 /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD829CBEE5300ADC226 /* Debugging.swift */; }; + 7DEBDB1D29CBF6B200ADC226 /* RandomAccessCollection+Offsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */; }; + 7DEBDB1E29CBF6B200ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */; }; + 7DEBDB6E29CCE44A00ADC226 /* SortedCollectionAPIChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3029CCE43600ADC226 /* SortedCollectionAPIChecker.swift */; }; + 7DEBDB6F29CCE44A00ADC226 /* MinimalMutableRangeReplaceableRandomAccessCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3F29CCE43600ADC226 /* MinimalMutableRangeReplaceableRandomAccessCollection.swift */; }; + 7DEBDB7029CCE44A00ADC226 /* ResettableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4229CCE43600ADC226 /* ResettableValue.swift */; }; + 7DEBDB7129CCE44A00ADC226 /* MinimalEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4429CCE43600ADC226 /* MinimalEncoder.swift */; }; + 7DEBDB7229CCE44A00ADC226 /* MinimalBidirectionalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4029CCE43600ADC226 /* MinimalBidirectionalCollection.swift */; }; + 7DEBDB7329CCE44A00ADC226 /* _MinimalCollectionCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4629CCE43600ADC226 /* _MinimalCollectionCore.swift */; }; + 7DEBDB7429CCE44A00ADC226 /* DictionaryAPIChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2A29CCE43600ADC226 /* DictionaryAPIChecker.swift */; }; + 7DEBDB7529CCE44A00ADC226 /* MinimalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4829CCE43600ADC226 /* MinimalCollection.swift */; }; + 7DEBDB7629CCE44A00ADC226 /* MinimalIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4129CCE43600ADC226 /* MinimalIndex.swift */; }; + 7DEBDB7729CCE44A00ADC226 /* HashableBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3129CCE43600ADC226 /* HashableBox.swift */; }; + 7DEBDB7829CCE44A00ADC226 /* Assertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2329CCE43600ADC226 /* Assertions.swift */; }; + 7DEBDB7929CCE44A00ADC226 /* LifetimeTracked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2729CCE43600ADC226 /* LifetimeTracked.swift */; }; + 7DEBDB7A29CCE44A00ADC226 /* SetAPIChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3229CCE43600ADC226 /* SetAPIChecker.swift */; }; + 7DEBDB7B29CCE44A00ADC226 /* IndexRangeCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2829CCE43600ADC226 /* IndexRangeCollection.swift */; }; + 7DEBDB7C29CCE44A00ADC226 /* MinimalDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4729CCE43600ADC226 /* MinimalDecoder.swift */; }; + 7DEBDB7D29CCE44A00ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4329CCE43600ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift */; }; + 7DEBDB7E29CCE44A00ADC226 /* CheckComparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3629CCE43600ADC226 /* CheckComparable.swift */; }; + 7DEBDB7F29CCE44A00ADC226 /* CheckBidirectionalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3929CCE43600ADC226 /* CheckBidirectionalCollection.swift */; }; + 7DEBDB8029CCE44A00ADC226 /* MinimalRandomAccessCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3C29CCE43600ADC226 /* MinimalRandomAccessCollection.swift */; }; + 7DEBDB8129CCE44A00ADC226 /* CheckCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3429CCE43600ADC226 /* CheckCollection.swift */; }; + 7DEBDB8229CCE44A00ADC226 /* RepeatableRandomNumberGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2F29CCE43600ADC226 /* RepeatableRandomNumberGenerator.swift */; }; + 7DEBDB8329CCE44A00ADC226 /* CheckHashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3529CCE43600ADC226 /* CheckHashable.swift */; }; + 7DEBDB8429CCE44A00ADC226 /* CheckSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3829CCE43600ADC226 /* CheckSequence.swift */; }; + 7DEBDB8529CCE44A00ADC226 /* AllOnesRandomNumberGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2C29CCE43600ADC226 /* AllOnesRandomNumberGenerator.swift */; }; + 7DEBDB8629CCE44A00ADC226 /* LifetimeTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2E29CCE43600ADC226 /* LifetimeTracker.swift */; }; + 7DEBDB8729CCE44A00ADC226 /* MinimalMutableRandomAccessCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3D29CCE43600ADC226 /* MinimalMutableRandomAccessCollection.swift */; }; + 7DEBDB8829CCE44A00ADC226 /* MinimalSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3E29CCE43600ADC226 /* MinimalSequence.swift */; }; + 7DEBDB8929CCE44A00ADC226 /* Combinatorics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2129CCE43600ADC226 /* Combinatorics.swift */; }; + 7DEBDB8A29CCE44A00ADC226 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2D29CCE43600ADC226 /* Box.swift */; }; + 7DEBDB8B29CCE44A00ADC226 /* _CollectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3B29CCE43600ADC226 /* _CollectionState.swift */; }; + 7DEBDB8C29CCE44A00ADC226 /* MinimalIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB4529CCE43600ADC226 /* MinimalIterator.swift */; }; + 7DEBDB8D29CCE44A00ADC226 /* RandomStableSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2629CCE43600ADC226 /* RandomStableSample.swift */; }; + 7DEBDB8E29CCE44A00ADC226 /* Integer Square Root.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2929CCE43600ADC226 /* Integer Square Root.swift */; }; + 7DEBDB8F29CCE44A00ADC226 /* CheckEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB3729CCE43600ADC226 /* CheckEquatable.swift */; }; + 7DEBDB9029CCE44A00ADC226 /* TestContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2229CCE43600ADC226 /* TestContext.swift */; }; + 7DEBDB9129CCE44A00ADC226 /* CollectionTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2429CCE43600ADC226 /* CollectionTestCase.swift */; }; + 7DEBDB9229CCE44A00ADC226 /* StringConvertibleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2B29CCE43600ADC226 /* StringConvertibleValue.swift */; }; + 7DEBDB9D29CCE73D00ADC226 /* Collections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7B29CA70F3004483EB /* Collections.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7DE91B3029CA6721004483EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7DE91B1D29CA6721004483EB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7DE91B2529CA6721004483EB; + remoteInfo = Collections; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 7D5A64D229CCE8CC00CB2595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 7D5A64D329CCEE9A00CB2595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 7D5A64D429CCEF1500CB2595 /* generate-sources.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-sources.sh"; sourceTree = ""; }; + 7D5A64D529CCEF1500CB2595 /* gyb_utils.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = gyb_utils.py; sourceTree = ""; }; + 7D5A64D629CCEF1500CB2595 /* gyb */ = {isa = PBXFileReference; lastKnownFileType = text; path = gyb; sourceTree = ""; }; + 7D5A64D729CCEF1500CB2595 /* gyb.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = gyb.py; sourceTree = ""; }; + 7D5A64D829CCF0FE00CB2595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 7D9B859129E4F74400B291CD /* BitArray+Shifts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Shifts.swift"; sourceTree = ""; }; + 7D9B859229E4F74400B291CD /* BitArray+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Descriptions.swift"; sourceTree = ""; }; + 7D9B859329E4F74400B291CD /* BitArray+LosslessStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+LosslessStringConvertible.swift"; sourceTree = ""; }; + 7D9B859429E4F74400B291CD /* BitArray+RandomBits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+RandomBits.swift"; sourceTree = ""; }; + 7D9B859529E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+ExpressibleByStringLiteral.swift"; sourceTree = ""; }; + 7D9B859C29E4F77D00B291CD /* Collections.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Collections.xctestplan; sourceTree = ""; }; + 7DE91B2629CA6721004483EB /* Collections.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Collections.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7DE91B2E29CA6721004483EB /* CollectionsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CollectionsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7DE91E7229CA70F3004483EB /* OrderedCollections.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = OrderedCollections.docc; sourceTree = ""; }; + 7DE91E7329CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91E7529CA70F3004483EB /* OrderedDictionary+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Equatable.swift"; sourceTree = ""; }; + 7DE91E7629CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+ExpressibleByDictionaryLiteral.swift"; sourceTree = ""; }; + 7DE91E7729CA70F3004483EB /* OrderedDictionary+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Hashable.swift"; sourceTree = ""; }; + 7DE91E7829CA70F3004483EB /* OrderedDictionary+Deprecations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Deprecations.swift"; sourceTree = ""; }; + 7DE91E7929CA70F3004483EB /* OrderedDictionary+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Initializers.swift"; sourceTree = ""; }; + 7DE91E7A29CA70F3004483EB /* OrderedDictionary+Elements.SubSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Elements.SubSequence.swift"; sourceTree = ""; }; + 7DE91E7B29CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91E7C29CA70F3004483EB /* OrderedDictionary+Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Sequence.swift"; sourceTree = ""; }; + 7DE91E7D29CA70F3004483EB /* OrderedDictionary+Partial RangeReplaceableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Partial RangeReplaceableCollection.swift"; sourceTree = ""; }; + 7DE91E7E29CA70F3004483EB /* OrderedDictionary+Elements.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Elements.swift"; sourceTree = ""; }; + 7DE91E7F29CA70F3004483EB /* OrderedDictionary+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Codable.swift"; sourceTree = ""; }; + 7DE91E8029CA70F3004483EB /* OrderedDictionary+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Invariants.swift"; sourceTree = ""; }; + 7DE91E8129CA70F3004483EB /* OrderedDictionary+Values.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Values.swift"; sourceTree = ""; }; + 7DE91E8229CA70F3004483EB /* OrderedDictionary+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Sendable.swift"; sourceTree = ""; }; + 7DE91E8329CA70F3004483EB /* OrderedDictionary+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Descriptions.swift"; sourceTree = ""; }; + 7DE91E8429CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Partial MutableCollection.swift"; sourceTree = ""; }; + 7DE91E8529CA70F3004483EB /* OrderedDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedDictionary.swift; sourceTree = ""; }; + 7DE91E8729CA70F3004483EB /* OrderedSet+Partial SetAlgebra symmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra symmetricDifference.swift"; sourceTree = ""; }; + 7DE91E8829CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+RandomAccessCollection.swift"; sourceTree = ""; }; + 7DE91E8929CA70F3004483EB /* OrderedSet+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Descriptions.swift"; sourceTree = ""; }; + 7DE91E8A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isEqualSet.swift"; sourceTree = ""; }; + 7DE91E8B29CA70F3004483EB /* OrderedSet+Diffing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Diffing.swift"; sourceTree = ""; }; + 7DE91E8C29CA70F3004483EB /* OrderedSet+UnstableInternals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+UnstableInternals.swift"; sourceTree = ""; }; + 7DE91E8D29CA70F3004483EB /* OrderedSet+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Invariants.swift"; sourceTree = ""; }; + 7DE91E8E29CA70F3004483EB /* OrderedSet+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Hashable.swift"; sourceTree = ""; }; + 7DE91E8F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isStrictSubset.swift"; sourceTree = ""; }; + 7DE91E9029CA70F3004483EB /* OrderedSet+Insertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Insertions.swift"; sourceTree = ""; }; + 7DE91E9129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isSubset.swift"; sourceTree = ""; }; + 7DE91E9229CA70F3004483EB /* OrderedSet+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Codable.swift"; sourceTree = ""; }; + 7DE91E9329CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isSuperset.swift"; sourceTree = ""; }; + 7DE91E9429CA70F3004483EB /* OrderedSet+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Equatable.swift"; sourceTree = ""; }; + 7DE91E9529CA70F3004483EB /* OrderedSet+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Initializers.swift"; sourceTree = ""; }; + 7DE91E9629CA70F3004483EB /* OrderedSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedSet.swift; sourceTree = ""; }; + 7DE91E9729CA70F3004483EB /* OrderedSet+UnorderedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+UnorderedView.swift"; sourceTree = ""; }; + 7DE91E9829CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE91E9929CA70F3004483EB /* OrderedSet+SubSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+SubSequence.swift"; sourceTree = ""; }; + 7DE91E9A29CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial MutableCollection.swift"; sourceTree = ""; }; + 7DE91E9B29CA70F3004483EB /* OrderedSet+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Sendable.swift"; sourceTree = ""; }; + 7DE91E9C29CA70F3004483EB /* OrderedSet+Testing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Testing.swift"; sourceTree = ""; }; + 7DE91E9D29CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra formUnion.swift"; sourceTree = ""; }; + 7DE91E9E29CA70F3004483EB /* OrderedSet+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91E9F29CA70F3004483EB /* OrderedSet+ReserveCapacity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+ReserveCapacity.swift"; sourceTree = ""; }; + 7DE91EA029CA70F3004483EB /* OrderedSet+Partial SetAlgebra union.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra union.swift"; sourceTree = ""; }; + 7DE91EA129CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra formIntersection.swift"; sourceTree = ""; }; + 7DE91EA229CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial RangeReplaceableCollection.swift"; sourceTree = ""; }; + 7DE91EA329CA70F3004483EB /* OrderedSet+Partial SetAlgebra+Basics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra+Basics.swift"; sourceTree = ""; }; + 7DE91EA429CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra subtracting.swift"; sourceTree = ""; }; + 7DE91EA529CA70F3004483EB /* OrderedSet+Partial SetAlgebra formSymmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra formSymmetricDifference.swift"; sourceTree = ""; }; + 7DE91EA629CA70F3004483EB /* OrderedSet+Partial SetAlgebra intersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra intersection.swift"; sourceTree = ""; }; + 7DE91EA729CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtract.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra subtract.swift"; sourceTree = ""; }; + 7DE91EA829CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isDisjoint.swift"; sourceTree = ""; }; + 7DE91EA929CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet+Partial SetAlgebra isStrictSuperset.swift"; sourceTree = ""; }; + 7DE91EAB29CA70F3004483EB /* _UnsafeBitset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafeBitset.swift; sourceTree = ""; }; + 7DE91EAD29CA70F3004483EB /* _HashTable+UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+UnsafeHandle.swift"; sourceTree = ""; }; + 7DE91EAE29CA70F3004483EB /* _HashTable+Bucket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+Bucket.swift"; sourceTree = ""; }; + 7DE91EAF29CA70F3004483EB /* _HashTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashTable.swift; sourceTree = ""; }; + 7DE91EB029CA70F3004483EB /* _HashTable+Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+Constants.swift"; sourceTree = ""; }; + 7DE91EB129CA70F3004483EB /* _Hashtable+Header.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_Hashtable+Header.swift"; sourceTree = ""; }; + 7DE91EB229CA70F3004483EB /* _HashTable+Testing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+Testing.swift"; sourceTree = ""; }; + 7DE91EB329CA70F3004483EB /* _HashTable+CustomStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+CustomStringConvertible.swift"; sourceTree = ""; }; + 7DE91EB429CA70F3004483EB /* _HashTable+BucketIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+BucketIterator.swift"; sourceTree = ""; }; + 7DE91EB529CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeBuffer.swift; sourceTree = ""; }; + 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Collection.swift"; sourceTree = ""; }; + 7DE91EB929CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91EBB29CA70F3004483EB /* Deque.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque.swift; sourceTree = ""; }; + 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeSlot.swift; sourceTree = ""; }; + 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Hashable.swift"; sourceTree = ""; }; + 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafeWrappedBuffer.swift; sourceTree = ""; }; + 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeBufferHeader.swift; sourceTree = ""; }; + 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Sendable.swift"; sourceTree = ""; }; + 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Codable.swift"; sourceTree = ""; }; + 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Testing.swift"; sourceTree = ""; }; + 7DE91EC329CA70F3004483EB /* DequeModule.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = DequeModule.docc; sourceTree = ""; }; + 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque._UnsafeHandle.swift; sourceTree = ""; }; + 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Descriptions.swift"; sourceTree = ""; }; + 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Extras.swift"; sourceTree = ""; }; + 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Equatable.swift"; sourceTree = ""; }; + 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque._Storage.swift; sourceTree = ""; }; + 7DE91ECD29CA70F3004483EB /* BigString+Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Metrics.swift"; sourceTree = ""; }; + 7DE91ECE29CA70F3004483EB /* BigString+Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Index.swift"; sourceTree = ""; }; + 7DE91ECF29CA70F3004483EB /* BigString+Summary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Summary.swift"; sourceTree = ""; }; + 7DE91ED029CA70F3004483EB /* BigString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigString.swift; sourceTree = ""; }; + 7DE91ED129CA70F3004483EB /* BigString+Iterators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Iterators.swift"; sourceTree = ""; }; + 7DE91ED229CA70F3004483EB /* BigString+Contents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Contents.swift"; sourceTree = ""; }; + 7DE91ED329CA70F3004483EB /* BigString+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Invariants.swift"; sourceTree = ""; }; + 7DE91ED429CA70F3004483EB /* BigString+Ingester.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Ingester.swift"; sourceTree = ""; }; + 7DE91ED529CA70F3004483EB /* BigString+Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Debugging.swift"; sourceTree = ""; }; + 7DE91ED629CA70F3004483EB /* BigString+Builder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Builder.swift"; sourceTree = ""; }; + 7DE91ED829CA70F3004483EB /* BigString+Chunk+Append and Insert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Append and Insert.swift"; sourceTree = ""; }; + 7DE91ED929CA70F3004483EB /* BigString+Chunk+Indexing by UTF16.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Indexing by UTF16.swift"; sourceTree = ""; }; + 7DE91EDA29CA70F3004483EB /* BigString+Chunk+Counts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Counts.swift"; sourceTree = ""; }; + 7DE91EDB29CA70F3004483EB /* BigString+Chunk+Indexing by Characters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Indexing by Characters.swift"; sourceTree = ""; }; + 7DE91EDC29CA70F3004483EB /* BigString+Chunk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk.swift"; sourceTree = ""; }; + 7DE91EDD29CA70F3004483EB /* BigString+Chunk+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Description.swift"; sourceTree = ""; }; + 7DE91EDE29CA70F3004483EB /* BigString+Chunk+Splitting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Splitting.swift"; sourceTree = ""; }; + 7DE91EDF29CA70F3004483EB /* BigString+Chunk+Breaks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+Breaks.swift"; sourceTree = ""; }; + 7DE91EE029CA70F3004483EB /* BigString+Chunk+RopeElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Chunk+RopeElement.swift"; sourceTree = ""; }; + 7DE91EE229CA70F3004483EB /* BigString+Split.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Split.swift"; sourceTree = ""; }; + 7DE91EE329CA70F3004483EB /* BigString+Managing Breaks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Managing Breaks.swift"; sourceTree = ""; }; + 7DE91EE429CA70F3004483EB /* BigString+RemoveSubrange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+RemoveSubrange.swift"; sourceTree = ""; }; + 7DE91EE529CA70F3004483EB /* BigString+ReplaceSubrange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+ReplaceSubrange.swift"; sourceTree = ""; }; + 7DE91EE629CA70F3004483EB /* BigString+Insert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Insert.swift"; sourceTree = ""; }; + 7DE91EE729CA70F3004483EB /* BigString+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Initializers.swift"; sourceTree = ""; }; + 7DE91EE829CA70F3004483EB /* Range+BigString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Range+BigString.swift"; sourceTree = ""; }; + 7DE91EE929CA70F3004483EB /* BigString+Append.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Append.swift"; sourceTree = ""; }; + 7DE91EEB29CA70F3004483EB /* BigString+UnicodeScalarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+UnicodeScalarView.swift"; sourceTree = ""; }; + 7DE91EEC29CA70F3004483EB /* BigString+UTF8View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+UTF8View.swift"; sourceTree = ""; }; + 7DE91EED29CA70F3004483EB /* BigSubstring+UnicodeScalarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigSubstring+UnicodeScalarView.swift"; sourceTree = ""; }; + 7DE91EEE29CA70F3004483EB /* BigSubstring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigSubstring.swift; sourceTree = ""; }; + 7DE91EEF29CA70F3004483EB /* BigSubstring+UTF16View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigSubstring+UTF16View.swift"; sourceTree = ""; }; + 7DE91EF029CA70F3004483EB /* BigString+UTF16View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+UTF16View.swift"; sourceTree = ""; }; + 7DE91EF129CA70F3004483EB /* BigSubstring+UTF8View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigSubstring+UTF8View.swift"; sourceTree = ""; }; + 7DE91EF329CA70F3004483EB /* BigString+Hashing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Hashing.swift"; sourceTree = ""; }; + 7DE91EF429CA70F3004483EB /* BigString+CustomStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+CustomStringConvertible.swift"; sourceTree = ""; }; + 7DE91EF529CA70F3004483EB /* BigString+BidirectionalCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+BidirectionalCollection.swift"; sourceTree = ""; }; + 7DE91EF629CA70F3004483EB /* BigString+CustomDebugStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+CustomDebugStringConvertible.swift"; sourceTree = ""; }; + 7DE91EF729CA70F3004483EB /* BigString+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Equatable.swift"; sourceTree = ""; }; + 7DE91EF829CA70F3004483EB /* BigString+TextOutputStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+TextOutputStream.swift"; sourceTree = ""; }; + 7DE91EF929CA70F3004483EB /* BigString+LosslessStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+LosslessStringConvertible.swift"; sourceTree = ""; }; + 7DE91EFA29CA70F3004483EB /* BigString+Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Sequence.swift"; sourceTree = ""; }; + 7DE91EFB29CA70F3004483EB /* BigString+ExpressibleByStringLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+ExpressibleByStringLiteral.swift"; sourceTree = ""; }; + 7DE91EFC29CA70F3004483EB /* BigString+RangeReplaceableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+RangeReplaceableCollection.swift"; sourceTree = ""; }; + 7DE91EFD29CA70F3004483EB /* BigString+Comparable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Comparable.swift"; sourceTree = ""; }; + 7DE91F0029CA70F3004483EB /* RopeMetric.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RopeMetric.swift; sourceTree = ""; }; + 7DE91F0129CA70F3004483EB /* _RopeItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RopeItem.swift; sourceTree = ""; }; + 7DE91F0229CA70F3004483EB /* Rope+Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Debugging.swift"; sourceTree = ""; }; + 7DE91F0329CA70F3004483EB /* _RopeVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RopeVersion.swift; sourceTree = ""; }; + 7DE91F0429CA70F3004483EB /* RopeElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RopeElement.swift; sourceTree = ""; }; + 7DE91F0529CA70F3004483EB /* Rope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rope.swift; sourceTree = ""; }; + 7DE91F0629CA70F3004483EB /* Rope+_UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+_UnsafeHandle.swift"; sourceTree = ""; }; + 7DE91F0729CA70F3004483EB /* Rope+_Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+_Storage.swift"; sourceTree = ""; }; + 7DE91F0829CA70F3004483EB /* Rope+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Invariants.swift"; sourceTree = ""; }; + 7DE91F0929CA70F3004483EB /* Rope+_UnmanagedLeaf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+_UnmanagedLeaf.swift"; sourceTree = ""; }; + 7DE91F0A29CA70F3004483EB /* RopeSummary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RopeSummary.swift; sourceTree = ""; }; + 7DE91F0B29CA70F3004483EB /* Rope+Builder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Builder.swift"; sourceTree = ""; }; + 7DE91F0C29CA70F3004483EB /* _RopePath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RopePath.swift; sourceTree = ""; }; + 7DE91F0D29CA70F3004483EB /* Rope+_Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+_Node.swift"; sourceTree = ""; }; + 7DE91F0F29CA70F3004483EB /* Rope+Extract.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Extract.swift"; sourceTree = ""; }; + 7DE91F1029CA70F3004483EB /* Rope+Append.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Append.swift"; sourceTree = ""; }; + 7DE91F1129CA70F3004483EB /* Rope+Split.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Split.swift"; sourceTree = ""; }; + 7DE91F1229CA70F3004483EB /* Rope+Find.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Find.swift"; sourceTree = ""; }; + 7DE91F1329CA70F3004483EB /* Rope+Insert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Insert.swift"; sourceTree = ""; }; + 7DE91F1429CA70F3004483EB /* Rope+ForEachWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+ForEachWhile.swift"; sourceTree = ""; }; + 7DE91F1529CA70F3004483EB /* Rope+MutatingForEach.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+MutatingForEach.swift"; sourceTree = ""; }; + 7DE91F1629CA70F3004483EB /* Rope+Join.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Join.swift"; sourceTree = ""; }; + 7DE91F1729CA70F3004483EB /* Rope+Remove.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Remove.swift"; sourceTree = ""; }; + 7DE91F1829CA70F3004483EB /* Rope+RemoveSubrange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+RemoveSubrange.swift"; sourceTree = ""; }; + 7DE91F1A29CA70F3004483EB /* Rope+Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Index.swift"; sourceTree = ""; }; + 7DE91F1B29CA70F3004483EB /* Rope+Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Sequence.swift"; sourceTree = ""; }; + 7DE91F1C29CA70F3004483EB /* Rope+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rope+Collection.swift"; sourceTree = ""; }; + 7DE91F1E29CA70F3004483EB /* String Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String Utilities.swift"; sourceTree = ""; }; + 7DE91F1F29CA70F3004483EB /* _CharacterRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _CharacterRecognizer.swift; sourceTree = ""; }; + 7DE91F2029CA70F3004483EB /* String.Index+ABI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String.Index+ABI.swift"; sourceTree = ""; }; + 7DE91F2129CA70F3004483EB /* Optional Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Optional Utilities.swift"; sourceTree = ""; }; + 7DE91F2E29CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91F3029CA70F3004483EB /* _SortedCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _SortedCollection.swift; sourceTree = ""; }; + 7DE91F3829CA70F3004483EB /* _UniqueCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UniqueCollection.swift; sourceTree = ""; }; + 7DE91F3B29CA70F3004483EB /* BitSet+SetAlgebra symmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra symmetricDifference.swift"; sourceTree = ""; }; + 7DE91F3C29CA70F3004483EB /* BitSet+SetAlgebra formIntersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra formIntersection.swift"; sourceTree = ""; }; + 7DE91F3D29CA70F3004483EB /* BitSet+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Codable.swift"; sourceTree = ""; }; + 7DE91F3E29CA70F3004483EB /* BitSet+SetAlgebra conformance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra conformance.swift"; sourceTree = ""; }; + 7DE91F3F29CA70F3004483EB /* BitSet+SetAlgebra subtract.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra subtract.swift"; sourceTree = ""; }; + 7DE91F4029CA70F3004483EB /* BitSet+Sorted Collection APIs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Sorted Collection APIs.swift"; sourceTree = ""; }; + 7DE91F4129CA70F3004483EB /* BitSet+SetAlgebra formUnion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra formUnion.swift"; sourceTree = ""; }; + 7DE91F4229CA70F3004483EB /* BitSet+SetAlgebra isStrictSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isStrictSuperset.swift"; sourceTree = ""; }; + 7DE91F4329CA70F3004483EB /* BitSet+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Extras.swift"; sourceTree = ""; }; + 7DE91F4429CA70F3004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra formSymmetricDifference.swift"; sourceTree = ""; }; + 7DE91F4529CA70F3004483EB /* BitSet+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE91F4629CA70F3004483EB /* BitSet+SetAlgebra isEqualSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isEqualSet.swift"; sourceTree = ""; }; + 7DE91F4729CA70F3004483EB /* BitSet+BidirectionalCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+BidirectionalCollection.swift"; sourceTree = ""; }; + 7DE91F4829CA70F3004483EB /* BitSet+CustomStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+CustomStringConvertible.swift"; sourceTree = ""; }; + 7DE91F4929CA70F3004483EB /* BitSet+Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Random.swift"; sourceTree = ""; }; + 7DE91F4A29CA70F3004483EB /* BitSet.Counted.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitSet.Counted.swift; sourceTree = ""; }; + 7DE91F4B29CA70F3004483EB /* BitSet+CustomDebugStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+CustomDebugStringConvertible.swift"; sourceTree = ""; }; + 7DE91F4C29CA70F3004483EB /* BitSet+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91F4D29CA70F3004483EB /* BitSet+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Initializers.swift"; sourceTree = ""; }; + 7DE91F4E29CA70F3004483EB /* BitSet+SetAlgebra isSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isSubset.swift"; sourceTree = ""; }; + 7DE91F4F29CA70F3004483EB /* BitSet.Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitSet.Index.swift; sourceTree = ""; }; + 7DE91F5029CA70F3004483EB /* BitSet+SetAlgebra basics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra basics.swift"; sourceTree = ""; }; + 7DE91F5129CA70F3004483EB /* BitSet+SetAlgebra isDisjoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isDisjoint.swift"; sourceTree = ""; }; + 7DE91F5229CA70F3004483EB /* BitSet+SetAlgebra union.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra union.swift"; sourceTree = ""; }; + 7DE91F5329CA70F3004483EB /* BitSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitSet.swift; sourceTree = ""; }; + 7DE91F5429CA70F3004483EB /* BitSet+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Equatable.swift"; sourceTree = ""; }; + 7DE91F5529CA70F3004483EB /* BitSet+SetAlgebra isSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isSuperset.swift"; sourceTree = ""; }; + 7DE91F5629CA70F3004483EB /* BitSet+SetAlgebra subtracting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra subtracting.swift"; sourceTree = ""; }; + 7DE91F5729CA70F3004483EB /* BitSet+SetAlgebra intersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra intersection.swift"; sourceTree = ""; }; + 7DE91F5829CA70F3004483EB /* BitSet._UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitSet._UnsafeHandle.swift; sourceTree = ""; }; + 7DE91F5929CA70F3004483EB /* BitSet+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Hashable.swift"; sourceTree = ""; }; + 7DE91F5A29CA70F3004483EB /* BitSet+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+Invariants.swift"; sourceTree = ""; }; + 7DE91F5B29CA70F3004483EB /* BitSet+SetAlgebra isStrictSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet+SetAlgebra isStrictSubset.swift"; sourceTree = ""; }; + 7DE91F5C29CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91F5E29CA70F3004483EB /* Slice+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Slice+Utilities.swift"; sourceTree = ""; }; + 7DE91F5F29CA70F3004483EB /* Range+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Range+Utilities.swift"; sourceTree = ""; }; + 7DE91F6029CA70F3004483EB /* UInt+Tricks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt+Tricks.swift"; sourceTree = ""; }; + 7DE91F6129CA70F3004483EB /* _Word.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Word.swift; sourceTree = ""; }; + 7DE91F6229CA70F3004483EB /* BitCollections.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = BitCollections.docc; sourceTree = ""; }; + 7DE91F6629CA70F3004483EB /* BitArray+ChunkedBitsIterators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+ChunkedBitsIterators.swift"; sourceTree = ""; }; + 7DE91F6729CA70F3004483EB /* BitArray+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91F6829CA70F3004483EB /* BitArray+BitwiseOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+BitwiseOperations.swift"; sourceTree = ""; }; + 7DE91F6929CA70F3004483EB /* BitArray+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Invariants.swift"; sourceTree = ""; }; + 7DE91F6A29CA70F3004483EB /* BitArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitArray.swift; sourceTree = ""; }; + 7DE91F6B29CA70F3004483EB /* BitArray+Fill.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Fill.swift"; sourceTree = ""; }; + 7DE91F6C29CA70F3004483EB /* BitArray+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Collection.swift"; sourceTree = ""; }; + 7DE91F6D29CA70F3004483EB /* BitArray+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Extras.swift"; sourceTree = ""; }; + 7DE91F6E29CA70F3004483EB /* BitArray+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE91F6F29CA70F3004483EB /* BitArray+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Codable.swift"; sourceTree = ""; }; + 7DE91F7029CA70F3004483EB /* BitArray+RangeReplaceableCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+RangeReplaceableCollection.swift"; sourceTree = ""; }; + 7DE91F7129CA70F3004483EB /* BitArray+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Initializers.swift"; sourceTree = ""; }; + 7DE91F7229CA70F3004483EB /* BitArray._UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitArray._UnsafeHandle.swift; sourceTree = ""; }; + 7DE91F7329CA70F3004483EB /* BitArray+Copy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Copy.swift"; sourceTree = ""; }; + 7DE91F7429CA70F3004483EB /* BitArray+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Hashable.swift"; sourceTree = ""; }; + 7DE91F7629CA70F3004483EB /* BitArray+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Equatable.swift"; sourceTree = ""; }; + 7DE91F7829CA70F3004483EB /* BitArray+Testing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+Testing.swift"; sourceTree = ""; }; + 7DE91F7A29CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91F7B29CA70F3004483EB /* Collections.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collections.swift; sourceTree = ""; }; + 7DE91F7C29CA70F3004483EB /* Collections.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Collections.docc; sourceTree = ""; }; + 7DE91FA829CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE91FA929CA70F3004483EB /* HashTreeCollections.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = HashTreeCollections.docc; sourceTree = ""; }; + 7DE91FAB29CA70F3004483EB /* TreeSet+SetAlgebra isEqualSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isEqualSet.swift"; sourceTree = ""; }; + 7DE91FAC29CA70F3004483EB /* TreeSet+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Descriptions.swift"; sourceTree = ""; }; + 7DE91FAD29CA70F3004483EB /* TreeSet+SetAlgebra isStrictSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isStrictSubset.swift"; sourceTree = ""; }; + 7DE91FAE29CA70F3004483EB /* TreeSet+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Sendable.swift"; sourceTree = ""; }; + 7DE91FAF29CA70F3004483EB /* TreeSet+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Equatable.swift"; sourceTree = ""; }; + 7DE91FB029CA70F3004483EB /* TreeSet+SetAlgebra intersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra intersection.swift"; sourceTree = ""; }; + 7DE91FB129CA70F3004483EB /* TreeSet+SetAlgebra formUnion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra formUnion.swift"; sourceTree = ""; }; + 7DE91FB229CA70F3004483EB /* TreeSet+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Extras.swift"; sourceTree = ""; }; + 7DE91FB329CA70F3004483EB /* TreeSet+SetAlgebra subtracting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra subtracting.swift"; sourceTree = ""; }; + 7DE91FB429CA70F3004483EB /* TreeSet+SetAlgebra subtract.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra subtract.swift"; sourceTree = ""; }; + 7DE91FB529CA70F3004483EB /* TreeSet+Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Debugging.swift"; sourceTree = ""; }; + 7DE91FB629CA70F3004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isStrictSuperset.swift"; sourceTree = ""; }; + 7DE91FB729CA70F3004483EB /* TreeSet+SetAlgebra formIntersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra formIntersection.swift"; sourceTree = ""; }; + 7DE91FB829CA70F3004483EB /* TreeSet+SetAlgebra isSuperset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isSuperset.swift"; sourceTree = ""; }; + 7DE91FB929CA70F3004483EB /* TreeSet+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91FBA29CA70F3004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra formSymmetricDifference.swift"; sourceTree = ""; }; + 7DE91FBB29CA70F3004483EB /* TreeSet+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Collection.swift"; sourceTree = ""; }; + 7DE91FBC29CA70F3004483EB /* TreeSet+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE91FBD29CA70F3004483EB /* TreeSet+SetAlgebra symmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra symmetricDifference.swift"; sourceTree = ""; }; + 7DE91FBE29CA70F3004483EB /* TreeSet+SetAlgebra basics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra basics.swift"; sourceTree = ""; }; + 7DE91FBF29CA70F3004483EB /* TreeSet+Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Sequence.swift"; sourceTree = ""; }; + 7DE91FC029CA70F3004483EB /* TreeSet+Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Filter.swift"; sourceTree = ""; }; + 7DE91FC129CA70F3004483EB /* TreeSet+SetAlgebra Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra Initializers.swift"; sourceTree = ""; }; + 7DE91FC229CA70F3004483EB /* TreeSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TreeSet.swift; sourceTree = ""; }; + 7DE91FC329CA70F3004483EB /* TreeSet+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Hashable.swift"; sourceTree = ""; }; + 7DE91FC429CA70F3004483EB /* TreeSet+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+Codable.swift"; sourceTree = ""; }; + 7DE91FC529CA70F3004483EB /* TreeSet+SetAlgebra isSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isSubset.swift"; sourceTree = ""; }; + 7DE91FC629CA70F3004483EB /* TreeSet+SetAlgebra union.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra union.swift"; sourceTree = ""; }; + 7DE91FC729CA70F3004483EB /* TreeSet+SetAlgebra isDisjoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet+SetAlgebra isDisjoint.swift"; sourceTree = ""; }; + 7DE91FC929CA70F3004483EB /* _Hash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Hash.swift; sourceTree = ""; }; + 7DE91FCA29CA70F3004483EB /* _HashNode+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Initializers.swift"; sourceTree = ""; }; + 7DE91FCB29CA70F3004483EB /* _HashStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashStack.swift; sourceTree = ""; }; + 7DE91FCC29CA70F3004483EB /* _HashNode+Subtree Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Subtree Removals.swift"; sourceTree = ""; }; + 7DE91FCD29CA70F3004483EB /* _HashTreeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashTreeIterator.swift; sourceTree = ""; }; + 7DE91FCE29CA70F3004483EB /* _HashNode+Structural symmetricDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural symmetricDifference.swift"; sourceTree = ""; }; + 7DE91FCF29CA70F3004483EB /* _HashSlot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashSlot.swift; sourceTree = ""; }; + 7DE91FD029CA70F3004483EB /* _HashNode+Primitive Replacement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Primitive Replacement.swift"; sourceTree = ""; }; + 7DE91FD129CA70F3004483EB /* _HashNode+Primitive Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Primitive Removals.swift"; sourceTree = ""; }; + 7DE91FD229CA70F3004483EB /* _HashNode+Structural isEqualSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural isEqualSet.swift"; sourceTree = ""; }; + 7DE91FD329CA70F3004483EB /* _HashNode+Subtree Insertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Subtree Insertions.swift"; sourceTree = ""; }; + 7DE91FD429CA70F3004483EB /* _HashTreeStatistics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashTreeStatistics.swift; sourceTree = ""; }; + 7DE91FD529CA70F3004483EB /* _HashNode+Primitive Insertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Primitive Insertions.swift"; sourceTree = ""; }; + 7DE91FD629CA70F3004483EB /* _HashNode+Structural filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural filter.swift"; sourceTree = ""; }; + 7DE91FD729CA70F3004483EB /* _HashNode+Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Debugging.swift"; sourceTree = ""; }; + 7DE91FD829CA70F3004483EB /* _Bucket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Bucket.swift; sourceTree = ""; }; + 7DE91FD929CA70F3004483EB /* _UnmanagedHashNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnmanagedHashNode.swift; sourceTree = ""; }; + 7DE91FDA29CA70F3004483EB /* _HashNode+Subtree Modify.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Subtree Modify.swift"; sourceTree = ""; }; + 7DE91FDB29CA70F3004483EB /* _HashNode+Builder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Builder.swift"; sourceTree = ""; }; + 7DE91FDC29CA70F3004483EB /* _HashNode+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Invariants.swift"; sourceTree = ""; }; + 7DE91FDD29CA70F3004483EB /* _HashNode+UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+UnsafeHandle.swift"; sourceTree = ""; }; + 7DE91FDE29CA70F3004483EB /* _RawHashNode+UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_RawHashNode+UnsafeHandle.swift"; sourceTree = ""; }; + 7DE91FDF29CA70F3004483EB /* _HashNode+Structural compactMapValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural compactMapValues.swift"; sourceTree = ""; }; + 7DE91FE029CA70F3004483EB /* _RawHashNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RawHashNode.swift; sourceTree = ""; }; + 7DE91FE129CA70F3004483EB /* _HashNode+Structural union.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural union.swift"; sourceTree = ""; }; + 7DE91FE229CA70F3004483EB /* _HashNode+Structural isSubset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural isSubset.swift"; sourceTree = ""; }; + 7DE91FE329CA70F3004483EB /* _HashNode+Structural subtracting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural subtracting.swift"; sourceTree = ""; }; + 7DE91FE429CA70F3004483EB /* _HashNode+Structural mapValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural mapValues.swift"; sourceTree = ""; }; + 7DE91FE529CA70F3004483EB /* _Bitmap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Bitmap.swift; sourceTree = ""; }; + 7DE91FE629CA70F3004483EB /* _StorageHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _StorageHeader.swift; sourceTree = ""; }; + 7DE91FE729CA70F3004483EB /* _HashNode+Lookups.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Lookups.swift"; sourceTree = ""; }; + 7DE91FE829CA70F3004483EB /* _UnsafePath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafePath.swift; sourceTree = ""; }; + 7DE91FE929CA70F3004483EB /* _HashNode+Structural isDisjoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural isDisjoint.swift"; sourceTree = ""; }; + 7DE91FEA29CA70F3004483EB /* _HashNode+Structural merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural merge.swift"; sourceTree = ""; }; + 7DE91FEB29CA70F3004483EB /* _AncestorHashSlots.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _AncestorHashSlots.swift; sourceTree = ""; }; + 7DE91FEC29CA70F3004483EB /* _HashLevel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashLevel.swift; sourceTree = ""; }; + 7DE91FED29CA70F3004483EB /* _HashNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HashNode.swift; sourceTree = ""; }; + 7DE91FEE29CA70F3004483EB /* _HashNode+Structural intersection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Structural intersection.swift"; sourceTree = ""; }; + 7DE91FEF29CA70F3004483EB /* _HashNode+Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashNode+Storage.swift"; sourceTree = ""; }; + 7DE91FF129CA70F3004483EB /* TreeDictionary+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Equatable.swift"; sourceTree = ""; }; + 7DE91FF229CA70F3004483EB /* TreeDictionary+Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Sequence.swift"; sourceTree = ""; }; + 7DE91FF329CA70F3004483EB /* TreeDictionary+Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Filter.swift"; sourceTree = ""; }; + 7DE91FF429CA70F3004483EB /* TreeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TreeDictionary.swift; sourceTree = ""; }; + 7DE91FF529CA70F3004483EB /* TreeDictionary+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Codable.swift"; sourceTree = ""; }; + 7DE91FF629CA70F3004483EB /* TreeDictionary+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Descriptions.swift"; sourceTree = ""; }; + 7DE91FF729CA70F3004483EB /* TreeDictionary+Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Debugging.swift"; sourceTree = ""; }; + 7DE91FF829CA70F3004483EB /* TreeDictionary+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Hashable.swift"; sourceTree = ""; }; + 7DE91FF929CA70F3004483EB /* TreeDictionary+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+CustomReflectable.swift"; sourceTree = ""; }; + 7DE91FFA29CA70F3004483EB /* TreeDictionary+ExpressibleByDictionaryLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+ExpressibleByDictionaryLiteral.swift"; sourceTree = ""; }; + 7DE91FFB29CA70F3004483EB /* TreeDictionary+Values.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Values.swift"; sourceTree = ""; }; + 7DE91FFC29CA70F3004483EB /* TreeDictionary+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Sendable.swift"; sourceTree = ""; }; + 7DE91FFD29CA70F3004483EB /* TreeDictionary+Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Merge.swift"; sourceTree = ""; }; + 7DE91FFE29CA70F3004483EB /* TreeDictionary+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Collection.swift"; sourceTree = ""; }; + 7DE91FFF29CA70F3004483EB /* TreeDictionary+MapValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+MapValues.swift"; sourceTree = ""; }; + 7DE9200029CA70F3004483EB /* TreeDictionary+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Initializers.swift"; sourceTree = ""; }; + 7DE9200129CA70F3004483EB /* TreeDictionary+Keys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary+Keys.swift"; sourceTree = ""; }; + 7DE9200329CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DE9200429CA70F3004483EB /* Heap+UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Heap+UnsafeHandle.swift"; sourceTree = ""; }; + 7DE9200529CA70F3004483EB /* Heap+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Heap+Descriptions.swift"; sourceTree = ""; }; + 7DE9200629CA70F3004483EB /* Heap+Invariants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Heap+Invariants.swift"; sourceTree = ""; }; + 7DE9200729CA70F3004483EB /* HeapModule.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = HeapModule.docc; sourceTree = ""; }; + 7DE9200829CA70F3004483EB /* Heap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Heap.swift; sourceTree = ""; }; + 7DE9200929CA70F3004483EB /* Heap+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Heap+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; + 7DE9200A29CA70F3004483EB /* _HeapNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HeapNode.swift; sourceTree = ""; }; + 7DE921A529CA782F004483EB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 7DE921A629CA782F004483EB /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CONTRIBUTING.md; path = ../CONTRIBUTING.md; sourceTree = ""; }; + 7DE921A729CA782F004483EB /* CODEOWNERS */ = {isa = PBXFileReference; lastKnownFileType = text; name = CODEOWNERS; path = ../CODEOWNERS; sourceTree = ""; }; + 7DE921A829CA782F004483EB /* CODE_OF_CONDUCT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CODE_OF_CONDUCT.md; path = ../CODE_OF_CONDUCT.md; sourceTree = ""; }; + 7DE921A929CA782F004483EB /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Package.swift; sourceTree = ""; }; + 7DE921AA29CA782F004483EB /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE.txt; path = ../LICENSE.txt; sourceTree = ""; }; + 7DE921AB29CA782F004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = CMakeLists.txt; path = ../CMakeLists.txt; sourceTree = ""; }; + 7DE921AD29CA78B2004483EB /* run-benchmarks.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "run-benchmarks.sh"; sourceTree = ""; }; + 7DE921B229CA78B2004483EB /* shuffle-sources.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "shuffle-sources.sh"; sourceTree = ""; }; + 7DE921B329CA78B2004483EB /* run-full-tests.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "run-full-tests.sh"; sourceTree = ""; }; + 7DE921B629CA78B2004483EB /* generate-docs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-docs.sh"; sourceTree = ""; }; + 7DE921CC29CA8575004483EB /* UtilitiesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilitiesTests.swift; sourceTree = ""; }; + 7DE921CD29CA8575004483EB /* MinimalTypeConformances.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalTypeConformances.swift; sourceTree = ""; }; + 7DE921CE29CA8575004483EB /* IndexRangeCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexRangeCollectionTests.swift; sourceTree = ""; }; + 7DE921CF29CA8575004483EB /* CombinatoricsChecks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinatoricsChecks.swift; sourceTree = ""; }; + 7DE921D129CA8575004483EB /* Hash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hash.swift; sourceTree = ""; }; + 7DE921D229CA8575004483EB /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; + 7DE921D329CA8575004483EB /* TreeDictionary Smoke Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary Smoke Tests.swift"; sourceTree = ""; }; + 7DE921D429CA8575004483EB /* TreeDictionary.Keys Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary.Keys Tests.swift"; sourceTree = ""; }; + 7DE921D529CA8575004483EB /* TreeDictionary Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary Tests.swift"; sourceTree = ""; }; + 7DE921D629CA8575004483EB /* Colliders.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colliders.swift; sourceTree = ""; }; + 7DE921D729CA8575004483EB /* TreeHashedCollections Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeHashedCollections Fixtures.swift"; sourceTree = ""; }; + 7DE921D829CA8575004483EB /* TreeDictionary.Values Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeDictionary.Values Tests.swift"; sourceTree = ""; }; + 7DE921D929CA8575004483EB /* TreeSet Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TreeSet Tests.swift"; sourceTree = ""; }; + 7DE921DB29CA8575004483EB /* BitSet.Counted Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitSet.Counted Tests.swift"; sourceTree = ""; }; + 7DE921DC29CA8575004483EB /* BitSetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitSetTests.swift; sourceTree = ""; }; + 7DE921DD29CA8575004483EB /* BitArrayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitArrayTests.swift; sourceTree = ""; }; + 7DE921DF29CA8575004483EB /* TestRope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRope.swift; sourceTree = ""; }; + 7DE921E129CA8575004483EB /* Availability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Availability.swift; sourceTree = ""; }; + 7DE921E229CA8575004483EB /* TestBigString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestBigString.swift; sourceTree = ""; }; + 7DE921E329CA8575004483EB /* SampleStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleStrings.swift; sourceTree = ""; }; + 7DE921E629CA8575004483EB /* OrderedDictionary Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary Tests.swift"; sourceTree = ""; }; + 7DE921E729CA8575004483EB /* OrderedDictionary Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary Utils.swift"; sourceTree = ""; }; + 7DE921E829CA8575004483EB /* OrderedDictionary+Values Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Values Tests.swift"; sourceTree = ""; }; + 7DE921E929CA8575004483EB /* OrderedDictionary+Elements Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Elements Tests.swift"; sourceTree = ""; }; + 7DE921EB29CA8575004483EB /* OrderedSetInternals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedSetInternals.swift; sourceTree = ""; }; + 7DE921EC29CA8575004483EB /* OrderedSet Diffing Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet Diffing Tests.swift"; sourceTree = ""; }; + 7DE921ED29CA8575004483EB /* RandomAccessCollection+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RandomAccessCollection+Extras.swift"; sourceTree = ""; }; + 7DE921EE29CA8575004483EB /* OrderedSet.UnorderedView Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedSet.UnorderedView Tests.swift"; sourceTree = ""; }; + 7DE921EF29CA8575004483EB /* OrderedSetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedSetTests.swift; sourceTree = ""; }; + 7DE921F129CA8575004483EB /* HashTableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashTableTests.swift; sourceTree = ""; }; + 7DE921F329CA8575004483EB /* HeapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeapTests.swift; sourceTree = ""; }; + 7DE921F429CA8575004483EB /* HeapNodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeapNodeTests.swift; sourceTree = ""; }; + 7DE921F629CA8576004483EB /* DequeInternals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DequeInternals.swift; sourceTree = ""; }; + 7DE921F729CA8576004483EB /* RangeReplaceableCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionTests.swift; sourceTree = ""; }; + 7DE921F829CA8576004483EB /* DequeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DequeTests.swift; sourceTree = ""; }; + 7DE921F929CA8576004483EB /* MutableCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutableCollectionTests.swift; sourceTree = ""; }; + 7DEBDAC829CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeMutablePointer+SE-0370.swift.gyb"; sourceTree = ""; }; + 7DEBDAC929CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeMutableBufferPointer+SE-0370.swift.gyb"; sourceTree = ""; }; + 7DEBDACA29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Array+WithContiguousStorage Compatibility.swift.gyb"; sourceTree = ""; }; + 7DEBDACB29CBEE5200ADC226 /* UnsafeRawPointer extensions.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeRawPointer extensions.swift.gyb"; sourceTree = ""; }; + 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutableBufferPointer+SE-0370.swift"; sourceTree = ""; }; + 7DEBDACE29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+WithContiguousStorage Compatibility.swift"; sourceTree = ""; }; + 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutablePointer+SE-0370.swift"; sourceTree = ""; }; + 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeRawPointer extensions.swift"; sourceTree = ""; }; + 7DEBDAD129CBEE5200ADC226 /* Descriptions.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Descriptions.swift.gyb; sourceTree = ""; }; + 7DEBDAD229CBEE5200ADC226 /* RandomAccessCollection+Offsets.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "RandomAccessCollection+Offsets.swift.gyb"; sourceTree = ""; }; + 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutableBufferPointer+Extras.swift"; sourceTree = ""; }; + 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeBufferPointer+Extras.swift"; sourceTree = ""; }; + 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RandomAccessCollection+Offsets.swift"; sourceTree = ""; }; + 7DEBDAD729CBEE5300ADC226 /* Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Descriptions.swift; sourceTree = ""; }; + 7DEBDAD829CBEE5300ADC226 /* Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debugging.swift; sourceTree = ""; }; + 7DEBDAD929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeBufferPointer+Extras.swift.gyb"; sourceTree = ""; }; + 7DEBDADB29CBEE5300ADC226 /* _UnsafeBitSet.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = _UnsafeBitSet.swift.gyb; sourceTree = ""; }; + 7DEBDADC29CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "_UnsafeBitSet+_Word.swift.gyb"; sourceTree = ""; }; + 7DEBDADD29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "_UnsafeBitSet+Index.swift.gyb"; sourceTree = ""; }; + 7DEBDADF29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_UnsafeBitSet+Index.swift"; sourceTree = ""; }; + 7DEBDAE029CBEE5300ADC226 /* _UnsafeBitSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafeBitSet.swift; sourceTree = ""; }; + 7DEBDAE129CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_UnsafeBitSet+_Word.swift"; sourceTree = ""; }; + 7DEBDAE329CBEE5300ADC226 /* UInt+reversed.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UInt+reversed.swift.gyb"; sourceTree = ""; }; + 7DEBDAE429CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb"; sourceTree = ""; }; + 7DEBDAE529CBEE5300ADC226 /* UInt+first and last set bit.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UInt+first and last set bit.swift.gyb"; sourceTree = ""; }; + 7DEBDAE629CBEE5300ADC226 /* Integer rank.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Integer rank.swift.gyb"; sourceTree = ""; }; + 7DEBDAE829CBEE5300ADC226 /* UInt+reversed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt+reversed.swift"; sourceTree = ""; }; + 7DEBDAE929CBEE5300ADC226 /* Integer rank.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Integer rank.swift"; sourceTree = ""; }; + 7DEBDAEA29CBEE5300ADC226 /* UInt+first and last set bit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt+first and last set bit.swift"; sourceTree = ""; }; + 7DEBDAEB29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+roundUpToPowerOfTwo.swift"; sourceTree = ""; }; + 7DEBDAEC29CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeMutableBufferPointer+Extras.swift.gyb"; sourceTree = ""; }; + 7DEBDAED29CBEE5300ADC226 /* Debugging.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Debugging.swift.gyb; sourceTree = ""; }; + 7DEBDB2129CCE43600ADC226 /* Combinatorics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Combinatorics.swift; sourceTree = ""; }; + 7DEBDB2229CCE43600ADC226 /* TestContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestContext.swift; sourceTree = ""; }; + 7DEBDB2329CCE43600ADC226 /* Assertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assertions.swift; sourceTree = ""; }; + 7DEBDB2429CCE43600ADC226 /* CollectionTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionTestCase.swift; sourceTree = ""; }; + 7DEBDB2629CCE43600ADC226 /* RandomStableSample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomStableSample.swift; sourceTree = ""; }; + 7DEBDB2729CCE43600ADC226 /* LifetimeTracked.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeTracked.swift; sourceTree = ""; }; + 7DEBDB2829CCE43600ADC226 /* IndexRangeCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexRangeCollection.swift; sourceTree = ""; }; + 7DEBDB2929CCE43600ADC226 /* Integer Square Root.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Integer Square Root.swift"; sourceTree = ""; }; + 7DEBDB2A29CCE43600ADC226 /* DictionaryAPIChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryAPIChecker.swift; sourceTree = ""; }; + 7DEBDB2B29CCE43600ADC226 /* StringConvertibleValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringConvertibleValue.swift; sourceTree = ""; }; + 7DEBDB2C29CCE43600ADC226 /* AllOnesRandomNumberGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllOnesRandomNumberGenerator.swift; sourceTree = ""; }; + 7DEBDB2D29CCE43600ADC226 /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; + 7DEBDB2E29CCE43600ADC226 /* LifetimeTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeTracker.swift; sourceTree = ""; }; + 7DEBDB2F29CCE43600ADC226 /* RepeatableRandomNumberGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepeatableRandomNumberGenerator.swift; sourceTree = ""; }; + 7DEBDB3029CCE43600ADC226 /* SortedCollectionAPIChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedCollectionAPIChecker.swift; sourceTree = ""; }; + 7DEBDB3129CCE43600ADC226 /* HashableBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashableBox.swift; sourceTree = ""; }; + 7DEBDB3229CCE43600ADC226 /* SetAPIChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetAPIChecker.swift; sourceTree = ""; }; + 7DEBDB3429CCE43600ADC226 /* CheckCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckCollection.swift; sourceTree = ""; }; + 7DEBDB3529CCE43600ADC226 /* CheckHashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckHashable.swift; sourceTree = ""; }; + 7DEBDB3629CCE43600ADC226 /* CheckComparable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckComparable.swift; sourceTree = ""; }; + 7DEBDB3729CCE43600ADC226 /* CheckEquatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckEquatable.swift; sourceTree = ""; }; + 7DEBDB3829CCE43600ADC226 /* CheckSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckSequence.swift; sourceTree = ""; }; + 7DEBDB3929CCE43600ADC226 /* CheckBidirectionalCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckBidirectionalCollection.swift; sourceTree = ""; }; + 7DEBDB3B29CCE43600ADC226 /* _CollectionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _CollectionState.swift; sourceTree = ""; }; + 7DEBDB3C29CCE43600ADC226 /* MinimalRandomAccessCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalRandomAccessCollection.swift; sourceTree = ""; }; + 7DEBDB3D29CCE43600ADC226 /* MinimalMutableRandomAccessCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalMutableRandomAccessCollection.swift; sourceTree = ""; }; + 7DEBDB3E29CCE43600ADC226 /* MinimalSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalSequence.swift; sourceTree = ""; }; + 7DEBDB3F29CCE43600ADC226 /* MinimalMutableRangeReplaceableRandomAccessCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalMutableRangeReplaceableRandomAccessCollection.swift; sourceTree = ""; }; + 7DEBDB4029CCE43600ADC226 /* MinimalBidirectionalCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalBidirectionalCollection.swift; sourceTree = ""; }; + 7DEBDB4129CCE43600ADC226 /* MinimalIndex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalIndex.swift; sourceTree = ""; }; + 7DEBDB4229CCE43600ADC226 /* ResettableValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResettableValue.swift; sourceTree = ""; }; + 7DEBDB4329CCE43600ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalRangeReplaceableRandomAccessCollection.swift; sourceTree = ""; }; + 7DEBDB4429CCE43600ADC226 /* MinimalEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalEncoder.swift; sourceTree = ""; }; + 7DEBDB4529CCE43600ADC226 /* MinimalIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalIterator.swift; sourceTree = ""; }; + 7DEBDB4629CCE43600ADC226 /* _MinimalCollectionCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _MinimalCollectionCore.swift; sourceTree = ""; }; + 7DEBDB4729CCE43600ADC226 /* MinimalDecoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalDecoder.swift; sourceTree = ""; }; + 7DEBDB4829CCE43600ADC226 /* MinimalCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimalCollection.swift; sourceTree = ""; }; + 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CollectionsTests.xcconfig; sourceTree = ""; }; + 7DEBDB9829CCE4A600ADC226 /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; + 7DEBDB9929CCE4A600ADC226 /* Collections.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Collections.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7DE91B2329CA6721004483EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DE91B2B29CA6721004483EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DE91B2F29CA6721004483EB /* Collections.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7DE91B1C29CA6721004483EB = { + isa = PBXGroup; + children = ( + 7DE921A929CA782F004483EB /* Package.swift */, + 7DE921AB29CA782F004483EB /* CMakeLists.txt */, + 7DE921A829CA782F004483EB /* CODE_OF_CONDUCT.md */, + 7DE921A729CA782F004483EB /* CODEOWNERS */, + 7DE921A629CA782F004483EB /* CONTRIBUTING.md */, + 7DE921AA29CA782F004483EB /* LICENSE.txt */, + 7DE921A529CA782F004483EB /* README.md */, + 7DE91E7029CA70F3004483EB /* Sources */, + 7DE921CA29CA8575004483EB /* Tests */, + 7DE921AC29CA78B2004483EB /* Utils */, + 7DEBDB9329CCE4A600ADC226 /* Xcode */, + 7DE91B2729CA6721004483EB /* Products */, + ); + sourceTree = ""; + }; + 7DE91B2729CA6721004483EB /* Products */ = { + isa = PBXGroup; + children = ( + 7DE91B2629CA6721004483EB /* Collections.framework */, + 7DE91B2E29CA6721004483EB /* CollectionsTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7DE91E7029CA70F3004483EB /* Sources */ = { + isa = PBXGroup; + children = ( + 7DE91F2229CA70F3004483EB /* _CollectionsUtilities */, + 7DE91F3929CA70F3004483EB /* BitCollections */, + 7DE91F7929CA70F3004483EB /* Collections */, + 7DE91EB629CA70F3004483EB /* DequeModule */, + 7DE91FA729CA70F3004483EB /* HashTreeCollections */, + 7DE9200229CA70F3004483EB /* HeapModule */, + 7DE91E7129CA70F3004483EB /* OrderedCollections */, + 7DE91ECA29CA70F3004483EB /* RopeModule */, + 7DE91EB529CA70F3004483EB /* CMakeLists.txt */, + ); + name = Sources; + path = ../Sources; + sourceTree = ""; + }; + 7DE91E7129CA70F3004483EB /* OrderedCollections */ = { + isa = PBXGroup; + children = ( + 7DE91EAC29CA70F3004483EB /* HashTable */, + 7DE91E7429CA70F3004483EB /* OrderedDictionary */, + 7DE91E8629CA70F3004483EB /* OrderedSet */, + 7DE91EAA29CA70F3004483EB /* Utilities */, + 7DE91E7229CA70F3004483EB /* OrderedCollections.docc */, + 7DE91E7329CA70F3004483EB /* CMakeLists.txt */, + ); + path = OrderedCollections; + sourceTree = ""; + }; + 7DE91E7429CA70F3004483EB /* OrderedDictionary */ = { + isa = PBXGroup; + children = ( + 7DE91E8529CA70F3004483EB /* OrderedDictionary.swift */, + 7DE91E7F29CA70F3004483EB /* OrderedDictionary+Codable.swift */, + 7DE91E7B29CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift */, + 7DE91E7829CA70F3004483EB /* OrderedDictionary+Deprecations.swift */, + 7DE91E8329CA70F3004483EB /* OrderedDictionary+Descriptions.swift */, + 7DE91E7A29CA70F3004483EB /* OrderedDictionary+Elements.SubSequence.swift */, + 7DE91E7E29CA70F3004483EB /* OrderedDictionary+Elements.swift */, + 7DE91E7529CA70F3004483EB /* OrderedDictionary+Equatable.swift */, + 7DE91E7629CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift */, + 7DE91E7729CA70F3004483EB /* OrderedDictionary+Hashable.swift */, + 7DE91E7929CA70F3004483EB /* OrderedDictionary+Initializers.swift */, + 7DE91E8029CA70F3004483EB /* OrderedDictionary+Invariants.swift */, + 7DE91E8429CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift */, + 7DE91E7D29CA70F3004483EB /* OrderedDictionary+Partial RangeReplaceableCollection.swift */, + 7DE91E8229CA70F3004483EB /* OrderedDictionary+Sendable.swift */, + 7DE91E7C29CA70F3004483EB /* OrderedDictionary+Sequence.swift */, + 7DE91E8129CA70F3004483EB /* OrderedDictionary+Values.swift */, + ); + path = OrderedDictionary; + sourceTree = ""; + }; + 7DE91E8629CA70F3004483EB /* OrderedSet */ = { + isa = PBXGroup; + children = ( + 7DE91E9629CA70F3004483EB /* OrderedSet.swift */, + 7DE91E9229CA70F3004483EB /* OrderedSet+Codable.swift */, + 7DE91E9E29CA70F3004483EB /* OrderedSet+CustomReflectable.swift */, + 7DE91E8929CA70F3004483EB /* OrderedSet+Descriptions.swift */, + 7DE91E8B29CA70F3004483EB /* OrderedSet+Diffing.swift */, + 7DE91E9429CA70F3004483EB /* OrderedSet+Equatable.swift */, + 7DE91E9829CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift */, + 7DE91E8E29CA70F3004483EB /* OrderedSet+Hashable.swift */, + 7DE91E9529CA70F3004483EB /* OrderedSet+Initializers.swift */, + 7DE91E9029CA70F3004483EB /* OrderedSet+Insertions.swift */, + 7DE91E8D29CA70F3004483EB /* OrderedSet+Invariants.swift */, + 7DE91E9A29CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift */, + 7DE91EA229CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift */, + 7DE91EA129CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift */, + 7DE91EA529CA70F3004483EB /* OrderedSet+Partial SetAlgebra formSymmetricDifference.swift */, + 7DE91E9D29CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift */, + 7DE91EA629CA70F3004483EB /* OrderedSet+Partial SetAlgebra intersection.swift */, + 7DE91EA829CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift */, + 7DE91E8A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift */, + 7DE91E8F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift */, + 7DE91EA929CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSuperset.swift */, + 7DE91E9129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSubset.swift */, + 7DE91E9329CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift */, + 7DE91EA729CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtract.swift */, + 7DE91EA429CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift */, + 7DE91E8729CA70F3004483EB /* OrderedSet+Partial SetAlgebra symmetricDifference.swift */, + 7DE91EA029CA70F3004483EB /* OrderedSet+Partial SetAlgebra union.swift */, + 7DE91EA329CA70F3004483EB /* OrderedSet+Partial SetAlgebra+Basics.swift */, + 7DE91E8829CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift */, + 7DE91E9F29CA70F3004483EB /* OrderedSet+ReserveCapacity.swift */, + 7DE91E9B29CA70F3004483EB /* OrderedSet+Sendable.swift */, + 7DE91E9929CA70F3004483EB /* OrderedSet+SubSequence.swift */, + 7DE91E9C29CA70F3004483EB /* OrderedSet+Testing.swift */, + 7DE91E9729CA70F3004483EB /* OrderedSet+UnorderedView.swift */, + 7DE91E8C29CA70F3004483EB /* OrderedSet+UnstableInternals.swift */, + ); + path = OrderedSet; + sourceTree = ""; + }; + 7DE91EAA29CA70F3004483EB /* Utilities */ = { + isa = PBXGroup; + children = ( + 7DE91EAB29CA70F3004483EB /* _UnsafeBitset.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 7DE91EAC29CA70F3004483EB /* HashTable */ = { + isa = PBXGroup; + children = ( + 7DE91EAF29CA70F3004483EB /* _HashTable.swift */, + 7DE91EAE29CA70F3004483EB /* _HashTable+Bucket.swift */, + 7DE91EB429CA70F3004483EB /* _HashTable+BucketIterator.swift */, + 7DE91EB029CA70F3004483EB /* _HashTable+Constants.swift */, + 7DE91EB329CA70F3004483EB /* _HashTable+CustomStringConvertible.swift */, + 7DE91EB129CA70F3004483EB /* _Hashtable+Header.swift */, + 7DE91EB229CA70F3004483EB /* _HashTable+Testing.swift */, + 7DE91EAD29CA70F3004483EB /* _HashTable+UnsafeHandle.swift */, + ); + path = HashTable; + sourceTree = ""; + }; + 7DE91EB629CA70F3004483EB /* DequeModule */ = { + isa = PBXGroup; + children = ( + 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */, + 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */, + 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */, + 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */, + 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */, + 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */, + 7DE91EBB29CA70F3004483EB /* Deque.swift */, + 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */, + 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */, + 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */, + 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */, + 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */, + 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */, + 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */, + 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */, + 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */, + 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */, + 7DE91EC329CA70F3004483EB /* DequeModule.docc */, + 7DE91EB929CA70F3004483EB /* CMakeLists.txt */, + ); + path = DequeModule; + sourceTree = ""; + }; + 7DE91ECA29CA70F3004483EB /* RopeModule */ = { + isa = PBXGroup; + children = ( + 7DE91ECB29CA70F3004483EB /* BigString */, + 7DE91EFE29CA70F3004483EB /* Rope */, + 7DE91F1D29CA70F3004483EB /* Utilities */, + ); + path = RopeModule; + sourceTree = ""; + }; + 7DE91ECB29CA70F3004483EB /* BigString */ = { + isa = PBXGroup; + children = ( + 7DE91ECC29CA70F3004483EB /* Basics */, + 7DE91ED729CA70F3004483EB /* Chunk */, + 7DE91EE129CA70F3004483EB /* Operations */, + 7DE91EEA29CA70F3004483EB /* Views */, + 7DE91EF229CA70F3004483EB /* Conformances */, + ); + path = BigString; + sourceTree = ""; + }; + 7DE91ECC29CA70F3004483EB /* Basics */ = { + isa = PBXGroup; + children = ( + 7DE91ED029CA70F3004483EB /* BigString.swift */, + 7DE91ED629CA70F3004483EB /* BigString+Builder.swift */, + 7DE91ED229CA70F3004483EB /* BigString+Contents.swift */, + 7DE91ED529CA70F3004483EB /* BigString+Debugging.swift */, + 7DE91ECE29CA70F3004483EB /* BigString+Index.swift */, + 7DE91ED429CA70F3004483EB /* BigString+Ingester.swift */, + 7DE91ED329CA70F3004483EB /* BigString+Invariants.swift */, + 7DE91ED129CA70F3004483EB /* BigString+Iterators.swift */, + 7DE91ECD29CA70F3004483EB /* BigString+Metrics.swift */, + 7DE91ECF29CA70F3004483EB /* BigString+Summary.swift */, + ); + path = Basics; + sourceTree = ""; + }; + 7DE91ED729CA70F3004483EB /* Chunk */ = { + isa = PBXGroup; + children = ( + 7DE91EDC29CA70F3004483EB /* BigString+Chunk.swift */, + 7DE91ED829CA70F3004483EB /* BigString+Chunk+Append and Insert.swift */, + 7DE91EDF29CA70F3004483EB /* BigString+Chunk+Breaks.swift */, + 7DE91EDA29CA70F3004483EB /* BigString+Chunk+Counts.swift */, + 7DE91EDD29CA70F3004483EB /* BigString+Chunk+Description.swift */, + 7DE91EDB29CA70F3004483EB /* BigString+Chunk+Indexing by Characters.swift */, + 7DE91ED929CA70F3004483EB /* BigString+Chunk+Indexing by UTF16.swift */, + 7DE91EE029CA70F3004483EB /* BigString+Chunk+RopeElement.swift */, + 7DE91EDE29CA70F3004483EB /* BigString+Chunk+Splitting.swift */, + ); + path = Chunk; + sourceTree = ""; + }; + 7DE91EE129CA70F3004483EB /* Operations */ = { + isa = PBXGroup; + children = ( + 7DE91EE929CA70F3004483EB /* BigString+Append.swift */, + 7DE91EE729CA70F3004483EB /* BigString+Initializers.swift */, + 7DE91EE629CA70F3004483EB /* BigString+Insert.swift */, + 7DE91EE329CA70F3004483EB /* BigString+Managing Breaks.swift */, + 7DE91EE429CA70F3004483EB /* BigString+RemoveSubrange.swift */, + 7DE91EE529CA70F3004483EB /* BigString+ReplaceSubrange.swift */, + 7DE91EE229CA70F3004483EB /* BigString+Split.swift */, + 7DE91EE829CA70F3004483EB /* Range+BigString.swift */, + ); + path = Operations; + sourceTree = ""; + }; + 7DE91EEA29CA70F3004483EB /* Views */ = { + isa = PBXGroup; + children = ( + 7DE91EEB29CA70F3004483EB /* BigString+UnicodeScalarView.swift */, + 7DE91EEC29CA70F3004483EB /* BigString+UTF8View.swift */, + 7DE91EF029CA70F3004483EB /* BigString+UTF16View.swift */, + 7DE91EEE29CA70F3004483EB /* BigSubstring.swift */, + 7DE91EED29CA70F3004483EB /* BigSubstring+UnicodeScalarView.swift */, + 7DE91EF129CA70F3004483EB /* BigSubstring+UTF8View.swift */, + 7DE91EEF29CA70F3004483EB /* BigSubstring+UTF16View.swift */, + ); + path = Views; + sourceTree = ""; + }; + 7DE91EF229CA70F3004483EB /* Conformances */ = { + isa = PBXGroup; + children = ( + 7DE91EF529CA70F3004483EB /* BigString+BidirectionalCollection.swift */, + 7DE91EFD29CA70F3004483EB /* BigString+Comparable.swift */, + 7DE91EF629CA70F3004483EB /* BigString+CustomDebugStringConvertible.swift */, + 7DE91EF429CA70F3004483EB /* BigString+CustomStringConvertible.swift */, + 7DE91EF729CA70F3004483EB /* BigString+Equatable.swift */, + 7DE91EFB29CA70F3004483EB /* BigString+ExpressibleByStringLiteral.swift */, + 7DE91EF329CA70F3004483EB /* BigString+Hashing.swift */, + 7DE91EF929CA70F3004483EB /* BigString+LosslessStringConvertible.swift */, + 7DE91EFC29CA70F3004483EB /* BigString+RangeReplaceableCollection.swift */, + 7DE91EFA29CA70F3004483EB /* BigString+Sequence.swift */, + 7DE91EF829CA70F3004483EB /* BigString+TextOutputStream.swift */, + ); + path = Conformances; + sourceTree = ""; + }; + 7DE91EFE29CA70F3004483EB /* Rope */ = { + isa = PBXGroup; + children = ( + 7DE91EFF29CA70F3004483EB /* Basics */, + 7DE91F0E29CA70F3004483EB /* Operations */, + 7DE91F1929CA70F3004483EB /* Conformances */, + ); + path = Rope; + sourceTree = ""; + }; + 7DE91EFF29CA70F3004483EB /* Basics */ = { + isa = PBXGroup; + children = ( + 7DE91F0129CA70F3004483EB /* _RopeItem.swift */, + 7DE91F0C29CA70F3004483EB /* _RopePath.swift */, + 7DE91F0329CA70F3004483EB /* _RopeVersion.swift */, + 7DE91F0529CA70F3004483EB /* Rope.swift */, + 7DE91F0D29CA70F3004483EB /* Rope+_Node.swift */, + 7DE91F0729CA70F3004483EB /* Rope+_Storage.swift */, + 7DE91F0929CA70F3004483EB /* Rope+_UnmanagedLeaf.swift */, + 7DE91F0629CA70F3004483EB /* Rope+_UnsafeHandle.swift */, + 7DE91F0B29CA70F3004483EB /* Rope+Builder.swift */, + 7DE91F0229CA70F3004483EB /* Rope+Debugging.swift */, + 7DE91F0829CA70F3004483EB /* Rope+Invariants.swift */, + 7DE91F0429CA70F3004483EB /* RopeElement.swift */, + 7DE91F0029CA70F3004483EB /* RopeMetric.swift */, + 7DE91F0A29CA70F3004483EB /* RopeSummary.swift */, + ); + path = Basics; + sourceTree = ""; + }; + 7DE91F0E29CA70F3004483EB /* Operations */ = { + isa = PBXGroup; + children = ( + 7DE91F1029CA70F3004483EB /* Rope+Append.swift */, + 7DE91F0F29CA70F3004483EB /* Rope+Extract.swift */, + 7DE91F1229CA70F3004483EB /* Rope+Find.swift */, + 7DE91F1429CA70F3004483EB /* Rope+ForEachWhile.swift */, + 7DE91F1329CA70F3004483EB /* Rope+Insert.swift */, + 7DE91F1629CA70F3004483EB /* Rope+Join.swift */, + 7DE91F1529CA70F3004483EB /* Rope+MutatingForEach.swift */, + 7DE91F1729CA70F3004483EB /* Rope+Remove.swift */, + 7DE91F1829CA70F3004483EB /* Rope+RemoveSubrange.swift */, + 7DE91F1129CA70F3004483EB /* Rope+Split.swift */, + ); + path = Operations; + sourceTree = ""; + }; + 7DE91F1929CA70F3004483EB /* Conformances */ = { + isa = PBXGroup; + children = ( + 7DE91F1C29CA70F3004483EB /* Rope+Collection.swift */, + 7DE91F1A29CA70F3004483EB /* Rope+Index.swift */, + 7DE91F1B29CA70F3004483EB /* Rope+Sequence.swift */, + ); + path = Conformances; + sourceTree = ""; + }; + 7DE91F1D29CA70F3004483EB /* Utilities */ = { + isa = PBXGroup; + children = ( + 7DE91F1F29CA70F3004483EB /* _CharacterRecognizer.swift */, + 7DE91F2129CA70F3004483EB /* Optional Utilities.swift */, + 7DE91F1E29CA70F3004483EB /* String Utilities.swift */, + 7DE91F2029CA70F3004483EB /* String.Index+ABI.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 7DE91F2229CA70F3004483EB /* _CollectionsUtilities */ = { + isa = PBXGroup; + children = ( + 7DEBDAD329CBEE5300ADC226 /* autogenerated */, + 7DEBDAC729CBEE5200ADC226 /* Compatibility */, + 7DEBDAE229CBEE5300ADC226 /* IntegerTricks */, + 7DEBDADA29CBEE5300ADC226 /* UnsafeBitSet */, + 7DE91F3029CA70F3004483EB /* _SortedCollection.swift */, + 7DE91F3829CA70F3004483EB /* _UniqueCollection.swift */, + 7DEBDAED29CBEE5300ADC226 /* Debugging.swift.gyb */, + 7DEBDAD129CBEE5200ADC226 /* Descriptions.swift.gyb */, + 7DEBDAD229CBEE5200ADC226 /* RandomAccessCollection+Offsets.swift.gyb */, + 7DEBDAD929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift.gyb */, + 7DEBDAEC29CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift.gyb */, + 7DE91F2E29CA70F3004483EB /* CMakeLists.txt */, + ); + path = _CollectionsUtilities; + sourceTree = ""; + }; + 7DE91F3929CA70F3004483EB /* BitCollections */ = { + isa = PBXGroup; + children = ( + 7DE91F6329CA70F3004483EB /* BitArray */, + 7DE91F3A29CA70F3004483EB /* BitSet */, + 7DE91F5D29CA70F3004483EB /* Shared */, + 7DE91F6229CA70F3004483EB /* BitCollections.docc */, + 7DE91F5C29CA70F3004483EB /* CMakeLists.txt */, + ); + path = BitCollections; + sourceTree = ""; + }; + 7DE91F3A29CA70F3004483EB /* BitSet */ = { + isa = PBXGroup; + children = ( + 7DE91F5829CA70F3004483EB /* BitSet._UnsafeHandle.swift */, + 7DE91F4A29CA70F3004483EB /* BitSet.Counted.swift */, + 7DE91F4F29CA70F3004483EB /* BitSet.Index.swift */, + 7DE91F5329CA70F3004483EB /* BitSet.swift */, + 7DE91F4729CA70F3004483EB /* BitSet+BidirectionalCollection.swift */, + 7DE91F3D29CA70F3004483EB /* BitSet+Codable.swift */, + 7DE91F4B29CA70F3004483EB /* BitSet+CustomDebugStringConvertible.swift */, + 7DE91F4C29CA70F3004483EB /* BitSet+CustomReflectable.swift */, + 7DE91F4829CA70F3004483EB /* BitSet+CustomStringConvertible.swift */, + 7DE91F5429CA70F3004483EB /* BitSet+Equatable.swift */, + 7DE91F4529CA70F3004483EB /* BitSet+ExpressibleByArrayLiteral.swift */, + 7DE91F4329CA70F3004483EB /* BitSet+Extras.swift */, + 7DE91F5929CA70F3004483EB /* BitSet+Hashable.swift */, + 7DE91F4D29CA70F3004483EB /* BitSet+Initializers.swift */, + 7DE91F5A29CA70F3004483EB /* BitSet+Invariants.swift */, + 7DE91F4929CA70F3004483EB /* BitSet+Random.swift */, + 7DE91F5029CA70F3004483EB /* BitSet+SetAlgebra basics.swift */, + 7DE91F3E29CA70F3004483EB /* BitSet+SetAlgebra conformance.swift */, + 7DE91F3C29CA70F3004483EB /* BitSet+SetAlgebra formIntersection.swift */, + 7DE91F4429CA70F3004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift */, + 7DE91F4129CA70F3004483EB /* BitSet+SetAlgebra formUnion.swift */, + 7DE91F5729CA70F3004483EB /* BitSet+SetAlgebra intersection.swift */, + 7DE91F5129CA70F3004483EB /* BitSet+SetAlgebra isDisjoint.swift */, + 7DE91F4629CA70F3004483EB /* BitSet+SetAlgebra isEqualSet.swift */, + 7DE91F5B29CA70F3004483EB /* BitSet+SetAlgebra isStrictSubset.swift */, + 7DE91F4229CA70F3004483EB /* BitSet+SetAlgebra isStrictSuperset.swift */, + 7DE91F4E29CA70F3004483EB /* BitSet+SetAlgebra isSubset.swift */, + 7DE91F5529CA70F3004483EB /* BitSet+SetAlgebra isSuperset.swift */, + 7DE91F3F29CA70F3004483EB /* BitSet+SetAlgebra subtract.swift */, + 7DE91F5629CA70F3004483EB /* BitSet+SetAlgebra subtracting.swift */, + 7DE91F3B29CA70F3004483EB /* BitSet+SetAlgebra symmetricDifference.swift */, + 7DE91F5229CA70F3004483EB /* BitSet+SetAlgebra union.swift */, + 7DE91F4029CA70F3004483EB /* BitSet+Sorted Collection APIs.swift */, + ); + path = BitSet; + sourceTree = ""; + }; + 7DE91F5D29CA70F3004483EB /* Shared */ = { + isa = PBXGroup; + children = ( + 7DE91F6129CA70F3004483EB /* _Word.swift */, + 7DE91F5F29CA70F3004483EB /* Range+Utilities.swift */, + 7DE91F5E29CA70F3004483EB /* Slice+Utilities.swift */, + 7DE91F6029CA70F3004483EB /* UInt+Tricks.swift */, + ); + path = Shared; + sourceTree = ""; + }; + 7DE91F6329CA70F3004483EB /* BitArray */ = { + isa = PBXGroup; + children = ( + 7DE91F7229CA70F3004483EB /* BitArray._UnsafeHandle.swift */, + 7DE91F6A29CA70F3004483EB /* BitArray.swift */, + 7DE91F6829CA70F3004483EB /* BitArray+BitwiseOperations.swift */, + 7DE91F6629CA70F3004483EB /* BitArray+ChunkedBitsIterators.swift */, + 7DE91F6F29CA70F3004483EB /* BitArray+Codable.swift */, + 7DE91F6C29CA70F3004483EB /* BitArray+Collection.swift */, + 7DE91F7329CA70F3004483EB /* BitArray+Copy.swift */, + 7DE91F6729CA70F3004483EB /* BitArray+CustomReflectable.swift */, + 7D9B859229E4F74400B291CD /* BitArray+Descriptions.swift */, + 7DE91F7629CA70F3004483EB /* BitArray+Equatable.swift */, + 7DE91F6E29CA70F3004483EB /* BitArray+ExpressibleByArrayLiteral.swift */, + 7D9B859529E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift */, + 7DE91F6D29CA70F3004483EB /* BitArray+Extras.swift */, + 7DE91F6B29CA70F3004483EB /* BitArray+Fill.swift */, + 7DE91F7429CA70F3004483EB /* BitArray+Hashable.swift */, + 7DE91F7129CA70F3004483EB /* BitArray+Initializers.swift */, + 7DE91F6929CA70F3004483EB /* BitArray+Invariants.swift */, + 7D9B859329E4F74400B291CD /* BitArray+LosslessStringConvertible.swift */, + 7D9B859429E4F74400B291CD /* BitArray+RandomBits.swift */, + 7DE91F7029CA70F3004483EB /* BitArray+RangeReplaceableCollection.swift */, + 7D9B859129E4F74400B291CD /* BitArray+Shifts.swift */, + 7DE91F7829CA70F3004483EB /* BitArray+Testing.swift */, + ); + path = BitArray; + sourceTree = ""; + }; + 7DE91F7929CA70F3004483EB /* Collections */ = { + isa = PBXGroup; + children = ( + 7DE91F7B29CA70F3004483EB /* Collections.swift */, + 7DE91F7C29CA70F3004483EB /* Collections.docc */, + 7DE91F7A29CA70F3004483EB /* CMakeLists.txt */, + ); + path = Collections; + sourceTree = ""; + }; + 7DE91FA729CA70F3004483EB /* HashTreeCollections */ = { + isa = PBXGroup; + children = ( + 7DE91FC829CA70F3004483EB /* HashNode */, + 7DE91FF029CA70F3004483EB /* TreeDictionary */, + 7DE91FAA29CA70F3004483EB /* TreeSet */, + 7DE91FA929CA70F3004483EB /* HashTreeCollections.docc */, + 7DE91FA829CA70F3004483EB /* CMakeLists.txt */, + ); + path = HashTreeCollections; + sourceTree = ""; + }; + 7DE91FAA29CA70F3004483EB /* TreeSet */ = { + isa = PBXGroup; + children = ( + 7DE91FC229CA70F3004483EB /* TreeSet.swift */, + 7DE91FC429CA70F3004483EB /* TreeSet+Codable.swift */, + 7DE91FBB29CA70F3004483EB /* TreeSet+Collection.swift */, + 7DE91FB929CA70F3004483EB /* TreeSet+CustomReflectable.swift */, + 7DE91FB529CA70F3004483EB /* TreeSet+Debugging.swift */, + 7DE91FAC29CA70F3004483EB /* TreeSet+Descriptions.swift */, + 7DE91FAF29CA70F3004483EB /* TreeSet+Equatable.swift */, + 7DE91FBC29CA70F3004483EB /* TreeSet+ExpressibleByArrayLiteral.swift */, + 7DE91FB229CA70F3004483EB /* TreeSet+Extras.swift */, + 7DE91FC029CA70F3004483EB /* TreeSet+Filter.swift */, + 7DE91FC329CA70F3004483EB /* TreeSet+Hashable.swift */, + 7DE91FAE29CA70F3004483EB /* TreeSet+Sendable.swift */, + 7DE91FBF29CA70F3004483EB /* TreeSet+Sequence.swift */, + 7DE91FBE29CA70F3004483EB /* TreeSet+SetAlgebra basics.swift */, + 7DE91FB729CA70F3004483EB /* TreeSet+SetAlgebra formIntersection.swift */, + 7DE91FBA29CA70F3004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift */, + 7DE91FB129CA70F3004483EB /* TreeSet+SetAlgebra formUnion.swift */, + 7DE91FC129CA70F3004483EB /* TreeSet+SetAlgebra Initializers.swift */, + 7DE91FB029CA70F3004483EB /* TreeSet+SetAlgebra intersection.swift */, + 7DE91FC729CA70F3004483EB /* TreeSet+SetAlgebra isDisjoint.swift */, + 7DE91FAB29CA70F3004483EB /* TreeSet+SetAlgebra isEqualSet.swift */, + 7DE91FAD29CA70F3004483EB /* TreeSet+SetAlgebra isStrictSubset.swift */, + 7DE91FB629CA70F3004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift */, + 7DE91FC529CA70F3004483EB /* TreeSet+SetAlgebra isSubset.swift */, + 7DE91FB829CA70F3004483EB /* TreeSet+SetAlgebra isSuperset.swift */, + 7DE91FB429CA70F3004483EB /* TreeSet+SetAlgebra subtract.swift */, + 7DE91FB329CA70F3004483EB /* TreeSet+SetAlgebra subtracting.swift */, + 7DE91FBD29CA70F3004483EB /* TreeSet+SetAlgebra symmetricDifference.swift */, + 7DE91FC629CA70F3004483EB /* TreeSet+SetAlgebra union.swift */, + ); + path = TreeSet; + sourceTree = ""; + }; + 7DE91FC829CA70F3004483EB /* HashNode */ = { + isa = PBXGroup; + children = ( + 7DE91FEB29CA70F3004483EB /* _AncestorHashSlots.swift */, + 7DE91FE529CA70F3004483EB /* _Bitmap.swift */, + 7DE91FD829CA70F3004483EB /* _Bucket.swift */, + 7DE91FC929CA70F3004483EB /* _Hash.swift */, + 7DE91FCD29CA70F3004483EB /* _HashTreeIterator.swift */, + 7DE91FD429CA70F3004483EB /* _HashTreeStatistics.swift */, + 7DE91FEC29CA70F3004483EB /* _HashLevel.swift */, + 7DE91FED29CA70F3004483EB /* _HashNode.swift */, + 7DE91FDB29CA70F3004483EB /* _HashNode+Builder.swift */, + 7DE91FD729CA70F3004483EB /* _HashNode+Debugging.swift */, + 7DE91FCA29CA70F3004483EB /* _HashNode+Initializers.swift */, + 7DE91FDC29CA70F3004483EB /* _HashNode+Invariants.swift */, + 7DE91FE729CA70F3004483EB /* _HashNode+Lookups.swift */, + 7DE91FD529CA70F3004483EB /* _HashNode+Primitive Insertions.swift */, + 7DE91FD129CA70F3004483EB /* _HashNode+Primitive Removals.swift */, + 7DE91FD029CA70F3004483EB /* _HashNode+Primitive Replacement.swift */, + 7DE91FEF29CA70F3004483EB /* _HashNode+Storage.swift */, + 7DE91FDF29CA70F3004483EB /* _HashNode+Structural compactMapValues.swift */, + 7DE91FD629CA70F3004483EB /* _HashNode+Structural filter.swift */, + 7DE91FEE29CA70F3004483EB /* _HashNode+Structural intersection.swift */, + 7DE91FE929CA70F3004483EB /* _HashNode+Structural isDisjoint.swift */, + 7DE91FD229CA70F3004483EB /* _HashNode+Structural isEqualSet.swift */, + 7DE91FE229CA70F3004483EB /* _HashNode+Structural isSubset.swift */, + 7DE91FE429CA70F3004483EB /* _HashNode+Structural mapValues.swift */, + 7DE91FEA29CA70F3004483EB /* _HashNode+Structural merge.swift */, + 7DE91FE329CA70F3004483EB /* _HashNode+Structural subtracting.swift */, + 7DE91FCE29CA70F3004483EB /* _HashNode+Structural symmetricDifference.swift */, + 7DE91FE129CA70F3004483EB /* _HashNode+Structural union.swift */, + 7DE91FD329CA70F3004483EB /* _HashNode+Subtree Insertions.swift */, + 7DE91FDA29CA70F3004483EB /* _HashNode+Subtree Modify.swift */, + 7DE91FCC29CA70F3004483EB /* _HashNode+Subtree Removals.swift */, + 7DE91FDD29CA70F3004483EB /* _HashNode+UnsafeHandle.swift */, + 7DE91FE029CA70F3004483EB /* _RawHashNode.swift */, + 7DE91FDE29CA70F3004483EB /* _RawHashNode+UnsafeHandle.swift */, + 7DE91FCF29CA70F3004483EB /* _HashSlot.swift */, + 7DE91FCB29CA70F3004483EB /* _HashStack.swift */, + 7DE91FE629CA70F3004483EB /* _StorageHeader.swift */, + 7DE91FD929CA70F3004483EB /* _UnmanagedHashNode.swift */, + 7DE91FE829CA70F3004483EB /* _UnsafePath.swift */, + ); + path = HashNode; + sourceTree = ""; + }; + 7DE91FF029CA70F3004483EB /* TreeDictionary */ = { + isa = PBXGroup; + children = ( + 7DE91FF429CA70F3004483EB /* TreeDictionary.swift */, + 7DE91FF529CA70F3004483EB /* TreeDictionary+Codable.swift */, + 7DE91FFE29CA70F3004483EB /* TreeDictionary+Collection.swift */, + 7DE91FF929CA70F3004483EB /* TreeDictionary+CustomReflectable.swift */, + 7DE91FF729CA70F3004483EB /* TreeDictionary+Debugging.swift */, + 7DE91FF629CA70F3004483EB /* TreeDictionary+Descriptions.swift */, + 7DE91FF129CA70F3004483EB /* TreeDictionary+Equatable.swift */, + 7DE91FFA29CA70F3004483EB /* TreeDictionary+ExpressibleByDictionaryLiteral.swift */, + 7DE91FF329CA70F3004483EB /* TreeDictionary+Filter.swift */, + 7DE91FF829CA70F3004483EB /* TreeDictionary+Hashable.swift */, + 7DE9200029CA70F3004483EB /* TreeDictionary+Initializers.swift */, + 7DE9200129CA70F3004483EB /* TreeDictionary+Keys.swift */, + 7DE91FFF29CA70F3004483EB /* TreeDictionary+MapValues.swift */, + 7DE91FFD29CA70F3004483EB /* TreeDictionary+Merge.swift */, + 7DE91FFC29CA70F3004483EB /* TreeDictionary+Sendable.swift */, + 7DE91FF229CA70F3004483EB /* TreeDictionary+Sequence.swift */, + 7DE91FFB29CA70F3004483EB /* TreeDictionary+Values.swift */, + ); + path = TreeDictionary; + sourceTree = ""; + }; + 7DE9200229CA70F3004483EB /* HeapModule */ = { + isa = PBXGroup; + children = ( + 7DE9200A29CA70F3004483EB /* _HeapNode.swift */, + 7DE9200829CA70F3004483EB /* Heap.swift */, + 7DE9200529CA70F3004483EB /* Heap+Descriptions.swift */, + 7DE9200929CA70F3004483EB /* Heap+ExpressibleByArrayLiteral.swift */, + 7DE9200629CA70F3004483EB /* Heap+Invariants.swift */, + 7DE9200429CA70F3004483EB /* Heap+UnsafeHandle.swift */, + 7DE9200329CA70F3004483EB /* CMakeLists.txt */, + 7DE9200729CA70F3004483EB /* HeapModule.docc */, + ); + path = HeapModule; + sourceTree = ""; + }; + 7DE921AC29CA78B2004483EB /* Utils */ = { + isa = PBXGroup; + children = ( + 7D5A64D429CCEF1500CB2595 /* generate-sources.sh */, + 7D5A64D629CCEF1500CB2595 /* gyb */, + 7D5A64D529CCEF1500CB2595 /* gyb_utils.py */, + 7D5A64D729CCEF1500CB2595 /* gyb.py */, + 7D5A64D329CCEE9A00CB2595 /* README.md */, + 7DE921B629CA78B2004483EB /* generate-docs.sh */, + 7DE921AD29CA78B2004483EB /* run-benchmarks.sh */, + 7DE921B329CA78B2004483EB /* run-full-tests.sh */, + 7DE921B229CA78B2004483EB /* shuffle-sources.sh */, + ); + name = Utils; + path = ../Utils; + sourceTree = ""; + }; + 7DE921CA29CA8575004483EB /* Tests */ = { + isa = PBXGroup; + children = ( + 7DEBDB1F29CCE43600ADC226 /* _CollectionsTestSupport */, + 7DE921DA29CA8575004483EB /* BitCollectionsTests */, + 7DE921CB29CA8575004483EB /* CollectionsTestSupportTests */, + 7DE921F529CA8575004483EB /* DequeTests */, + 7DE921D029CA8575004483EB /* HashTreeCollectionsTests */, + 7DE921F229CA8575004483EB /* HeapTests */, + 7DE921E429CA8575004483EB /* OrderedCollectionsTests */, + 7DE921DE29CA8575004483EB /* RopeModuleTests */, + 7D5A64D829CCF0FE00CB2595 /* README.md */, + ); + name = Tests; + path = ../Tests; + sourceTree = ""; + }; + 7DE921CB29CA8575004483EB /* CollectionsTestSupportTests */ = { + isa = PBXGroup; + children = ( + 7DE921CF29CA8575004483EB /* CombinatoricsChecks.swift */, + 7DE921CE29CA8575004483EB /* IndexRangeCollectionTests.swift */, + 7DE921CD29CA8575004483EB /* MinimalTypeConformances.swift */, + 7DE921CC29CA8575004483EB /* UtilitiesTests.swift */, + ); + path = CollectionsTestSupportTests; + sourceTree = ""; + }; + 7DE921D029CA8575004483EB /* HashTreeCollectionsTests */ = { + isa = PBXGroup; + children = ( + 7DE921D629CA8575004483EB /* Colliders.swift */, + 7DE921D129CA8575004483EB /* Hash.swift */, + 7DE921D329CA8575004483EB /* TreeDictionary Smoke Tests.swift */, + 7DE921D529CA8575004483EB /* TreeDictionary Tests.swift */, + 7DE921D429CA8575004483EB /* TreeDictionary.Keys Tests.swift */, + 7DE921D829CA8575004483EB /* TreeDictionary.Values Tests.swift */, + 7DE921D729CA8575004483EB /* TreeHashedCollections Fixtures.swift */, + 7DE921D929CA8575004483EB /* TreeSet Tests.swift */, + 7DE921D229CA8575004483EB /* Utilities.swift */, + ); + path = HashTreeCollectionsTests; + sourceTree = ""; + }; + 7DE921DA29CA8575004483EB /* BitCollectionsTests */ = { + isa = PBXGroup; + children = ( + 7DE921DD29CA8575004483EB /* BitArrayTests.swift */, + 7DE921DB29CA8575004483EB /* BitSet.Counted Tests.swift */, + 7DE921DC29CA8575004483EB /* BitSetTests.swift */, + ); + path = BitCollectionsTests; + sourceTree = ""; + }; + 7DE921DE29CA8575004483EB /* RopeModuleTests */ = { + isa = PBXGroup; + children = ( + 7DE921DF29CA8575004483EB /* TestRope.swift */, + 7DE921E129CA8575004483EB /* Availability.swift */, + 7DE921E229CA8575004483EB /* TestBigString.swift */, + 7DE921E329CA8575004483EB /* SampleStrings.swift */, + ); + path = RopeModuleTests; + sourceTree = ""; + }; + 7DE921E429CA8575004483EB /* OrderedCollectionsTests */ = { + isa = PBXGroup; + children = ( + 7DE921E529CA8575004483EB /* OrderedDictionary */, + 7DE921EA29CA8575004483EB /* OrderedSet */, + 7DE921F029CA8575004483EB /* HashTable */, + ); + path = OrderedCollectionsTests; + sourceTree = ""; + }; + 7DE921E529CA8575004483EB /* OrderedDictionary */ = { + isa = PBXGroup; + children = ( + 7DE921E629CA8575004483EB /* OrderedDictionary Tests.swift */, + 7DE921E729CA8575004483EB /* OrderedDictionary Utils.swift */, + 7DE921E929CA8575004483EB /* OrderedDictionary+Elements Tests.swift */, + 7DE921E829CA8575004483EB /* OrderedDictionary+Values Tests.swift */, + ); + path = OrderedDictionary; + sourceTree = ""; + }; + 7DE921EA29CA8575004483EB /* OrderedSet */ = { + isa = PBXGroup; + children = ( + 7DE921EC29CA8575004483EB /* OrderedSet Diffing Tests.swift */, + 7DE921EE29CA8575004483EB /* OrderedSet.UnorderedView Tests.swift */, + 7DE921EB29CA8575004483EB /* OrderedSetInternals.swift */, + 7DE921EF29CA8575004483EB /* OrderedSetTests.swift */, + 7DE921ED29CA8575004483EB /* RandomAccessCollection+Extras.swift */, + ); + path = OrderedSet; + sourceTree = ""; + }; + 7DE921F029CA8575004483EB /* HashTable */ = { + isa = PBXGroup; + children = ( + 7DE921F129CA8575004483EB /* HashTableTests.swift */, + ); + path = HashTable; + sourceTree = ""; + }; + 7DE921F229CA8575004483EB /* HeapTests */ = { + isa = PBXGroup; + children = ( + 7DE921F329CA8575004483EB /* HeapTests.swift */, + 7DE921F429CA8575004483EB /* HeapNodeTests.swift */, + ); + path = HeapTests; + sourceTree = ""; + }; + 7DE921F529CA8575004483EB /* DequeTests */ = { + isa = PBXGroup; + children = ( + 7DE921F629CA8576004483EB /* DequeInternals.swift */, + 7DE921F829CA8576004483EB /* DequeTests.swift */, + 7DE921F929CA8576004483EB /* MutableCollectionTests.swift */, + 7DE921F729CA8576004483EB /* RangeReplaceableCollectionTests.swift */, + ); + path = DequeTests; + sourceTree = ""; + }; + 7DEBDAC729CBEE5200ADC226 /* Compatibility */ = { + isa = PBXGroup; + children = ( + 7DEBDACC29CBEE5200ADC226 /* autogenerated */, + 7DEBDACA29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift.gyb */, + 7DEBDAC929CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift.gyb */, + 7DEBDAC829CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift.gyb */, + 7DEBDACB29CBEE5200ADC226 /* UnsafeRawPointer extensions.swift.gyb */, + ); + path = Compatibility; + sourceTree = ""; + }; + 7DEBDACC29CBEE5200ADC226 /* autogenerated */ = { + isa = PBXGroup; + children = ( + 7DEBDACE29CBEE5200ADC226 /* Array+WithContiguousStorage Compatibility.swift */, + 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */, + 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */, + 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */, + ); + path = autogenerated; + sourceTree = ""; + }; + 7DEBDAD329CBEE5300ADC226 /* autogenerated */ = { + isa = PBXGroup; + children = ( + 7DEBDAD829CBEE5300ADC226 /* Debugging.swift */, + 7DEBDAD729CBEE5300ADC226 /* Descriptions.swift */, + 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */, + 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */, + 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */, + ); + path = autogenerated; + sourceTree = ""; + }; + 7DEBDADA29CBEE5300ADC226 /* UnsafeBitSet */ = { + isa = PBXGroup; + children = ( + 7DEBDADE29CBEE5300ADC226 /* autogenerated */, + 7DEBDADB29CBEE5300ADC226 /* _UnsafeBitSet.swift.gyb */, + 7DEBDADC29CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift.gyb */, + 7DEBDADD29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift.gyb */, + ); + path = UnsafeBitSet; + sourceTree = ""; + }; + 7DEBDADE29CBEE5300ADC226 /* autogenerated */ = { + isa = PBXGroup; + children = ( + 7DEBDAE029CBEE5300ADC226 /* _UnsafeBitSet.swift */, + 7DEBDAE129CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift */, + 7DEBDADF29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift */, + ); + path = autogenerated; + sourceTree = ""; + }; + 7DEBDAE229CBEE5300ADC226 /* IntegerTricks */ = { + isa = PBXGroup; + children = ( + 7DEBDAE729CBEE5300ADC226 /* autogenerated */, + 7DEBDAE429CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb */, + 7DEBDAE629CBEE5300ADC226 /* Integer rank.swift.gyb */, + 7DEBDAE529CBEE5300ADC226 /* UInt+first and last set bit.swift.gyb */, + 7DEBDAE329CBEE5300ADC226 /* UInt+reversed.swift.gyb */, + ); + path = IntegerTricks; + sourceTree = ""; + }; + 7DEBDAE729CBEE5300ADC226 /* autogenerated */ = { + isa = PBXGroup; + children = ( + 7DEBDAEB29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift */, + 7DEBDAE929CBEE5300ADC226 /* Integer rank.swift */, + 7DEBDAEA29CBEE5300ADC226 /* UInt+first and last set bit.swift */, + 7DEBDAE829CBEE5300ADC226 /* UInt+reversed.swift */, + ); + path = autogenerated; + sourceTree = ""; + }; + 7DEBDB1F29CCE43600ADC226 /* _CollectionsTestSupport */ = { + isa = PBXGroup; + children = ( + 7DEBDB2029CCE43600ADC226 /* AssertionContexts */, + 7DEBDB2529CCE43600ADC226 /* Utilities */, + 7DEBDB3329CCE43600ADC226 /* ConformanceCheckers */, + 7DEBDB3A29CCE43600ADC226 /* MinimalTypes */, + ); + path = _CollectionsTestSupport; + sourceTree = ""; + }; + 7DEBDB2029CCE43600ADC226 /* AssertionContexts */ = { + isa = PBXGroup; + children = ( + 7DEBDB2129CCE43600ADC226 /* Combinatorics.swift */, + 7DEBDB2229CCE43600ADC226 /* TestContext.swift */, + 7DEBDB2329CCE43600ADC226 /* Assertions.swift */, + 7DEBDB2429CCE43600ADC226 /* CollectionTestCase.swift */, + ); + path = AssertionContexts; + sourceTree = ""; + }; + 7DEBDB2529CCE43600ADC226 /* Utilities */ = { + isa = PBXGroup; + children = ( + 7DEBDB2629CCE43600ADC226 /* RandomStableSample.swift */, + 7DEBDB2729CCE43600ADC226 /* LifetimeTracked.swift */, + 7DEBDB2829CCE43600ADC226 /* IndexRangeCollection.swift */, + 7DEBDB2929CCE43600ADC226 /* Integer Square Root.swift */, + 7DEBDB2A29CCE43600ADC226 /* DictionaryAPIChecker.swift */, + 7DEBDB2B29CCE43600ADC226 /* StringConvertibleValue.swift */, + 7DEBDB2C29CCE43600ADC226 /* AllOnesRandomNumberGenerator.swift */, + 7DEBDB2D29CCE43600ADC226 /* Box.swift */, + 7DEBDB2E29CCE43600ADC226 /* LifetimeTracker.swift */, + 7DEBDB2F29CCE43600ADC226 /* RepeatableRandomNumberGenerator.swift */, + 7DEBDB3029CCE43600ADC226 /* SortedCollectionAPIChecker.swift */, + 7DEBDB3129CCE43600ADC226 /* HashableBox.swift */, + 7DEBDB3229CCE43600ADC226 /* SetAPIChecker.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 7DEBDB3329CCE43600ADC226 /* ConformanceCheckers */ = { + isa = PBXGroup; + children = ( + 7DEBDB3429CCE43600ADC226 /* CheckCollection.swift */, + 7DEBDB3529CCE43600ADC226 /* CheckHashable.swift */, + 7DEBDB3629CCE43600ADC226 /* CheckComparable.swift */, + 7DEBDB3729CCE43600ADC226 /* CheckEquatable.swift */, + 7DEBDB3829CCE43600ADC226 /* CheckSequence.swift */, + 7DEBDB3929CCE43600ADC226 /* CheckBidirectionalCollection.swift */, + ); + path = ConformanceCheckers; + sourceTree = ""; + }; + 7DEBDB3A29CCE43600ADC226 /* MinimalTypes */ = { + isa = PBXGroup; + children = ( + 7DEBDB3B29CCE43600ADC226 /* _CollectionState.swift */, + 7DEBDB3C29CCE43600ADC226 /* MinimalRandomAccessCollection.swift */, + 7DEBDB3D29CCE43600ADC226 /* MinimalMutableRandomAccessCollection.swift */, + 7DEBDB3E29CCE43600ADC226 /* MinimalSequence.swift */, + 7DEBDB3F29CCE43600ADC226 /* MinimalMutableRangeReplaceableRandomAccessCollection.swift */, + 7DEBDB4029CCE43600ADC226 /* MinimalBidirectionalCollection.swift */, + 7DEBDB4129CCE43600ADC226 /* MinimalIndex.swift */, + 7DEBDB4229CCE43600ADC226 /* ResettableValue.swift */, + 7DEBDB4329CCE43600ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift */, + 7DEBDB4429CCE43600ADC226 /* MinimalEncoder.swift */, + 7DEBDB4529CCE43600ADC226 /* MinimalIterator.swift */, + 7DEBDB4629CCE43600ADC226 /* _MinimalCollectionCore.swift */, + 7DEBDB4729CCE43600ADC226 /* MinimalDecoder.swift */, + 7DEBDB4829CCE43600ADC226 /* MinimalCollection.swift */, + ); + path = MinimalTypes; + sourceTree = ""; + }; + 7DEBDB9329CCE4A600ADC226 /* Xcode */ = { + isa = PBXGroup; + children = ( + 7DEBDB9829CCE4A600ADC226 /* Shared.xcconfig */, + 7DEBDB9929CCE4A600ADC226 /* Collections.xcconfig */, + 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */, + 7D9B859C29E4F77D00B291CD /* Collections.xctestplan */, + 7D5A64D229CCE8CC00CB2595 /* README.md */, + ); + name = Xcode; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 7DE91B2129CA6721004483EB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 7DE91B2529CA6721004483EB /* Collections */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7DE91B3829CA6721004483EB /* Build configuration list for PBXNativeTarget "Collections" */; + buildPhases = ( + 7DE91B2129CA6721004483EB /* Headers */, + 7DE91B2229CA6721004483EB /* Sources */, + 7DE91B2329CA6721004483EB /* Frameworks */, + 7DE91B2429CA6721004483EB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Collections; + productName = Collections; + productReference = 7DE91B2629CA6721004483EB /* Collections.framework */; + productType = "com.apple.product-type.framework"; + }; + 7DE91B2D29CA6721004483EB /* CollectionsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7DE91B3B29CA6721004483EB /* Build configuration list for PBXNativeTarget "CollectionsTests" */; + buildPhases = ( + 7DE91B2A29CA6721004483EB /* Sources */, + 7DE91B2B29CA6721004483EB /* Frameworks */, + 7DE91B2C29CA6721004483EB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7DE91B3129CA6721004483EB /* PBXTargetDependency */, + ); + name = CollectionsTests; + productName = CollectionsTests; + productReference = 7DE91B2E29CA6721004483EB /* CollectionsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7DE91B1D29CA6721004483EB /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 7DE91B2529CA6721004483EB = { + CreatedOnToolsVersion = 14.3; + }; + 7DE91B2D29CA6721004483EB = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = 7DE91B2029CA6721004483EB /* Build configuration list for PBXProject "Collections" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7DE91B1C29CA6721004483EB; + productRefGroup = 7DE91B2729CA6721004483EB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7DE91B2529CA6721004483EB /* Collections */, + 7DE91B2D29CA6721004483EB /* CollectionsTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7DE91B2429CA6721004483EB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DE91B2C29CA6721004483EB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7DE91B2229CA6721004483EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DE920F129CA70F4004483EB /* BitArray+Initializers.swift in Sources */, + 7DE9214529CA70F4004483EB /* _HashNode+Structural symmetricDifference.swift in Sources */, + 7DE9201029CA70F3004483EB /* OrderedDictionary+Deprecations.swift in Sources */, + 7DE9205929CA70F3004483EB /* Deque+Descriptions.swift in Sources */, + 7DEBDAF529CBEE5300ADC226 /* UnsafeRawPointer extensions.swift in Sources */, + 7DE9212729CA70F4004483EB /* TreeSet+Equatable.swift in Sources */, + 7DE9213429CA70F4004483EB /* TreeSet+ExpressibleByArrayLiteral.swift in Sources */, + 7DE9209729CA70F4004483EB /* _RopePath.swift in Sources */, + 7DE9216629CA70F4004483EB /* _HashNode+Storage.swift in Sources */, + 7DE920CE29CA70F4004483EB /* BitSet+CustomReflectable.swift in Sources */, + 7DE9215029CA70F4004483EB /* _UnmanagedHashNode.swift in Sources */, + 7DE920EF29CA70F4004483EB /* BitArray+Codable.swift in Sources */, + 7DE920C029CA70F4004483EB /* BitSet+SetAlgebra conformance.swift in Sources */, + 7DE9203C29CA70F3004483EB /* OrderedSet+Partial SetAlgebra formSymmetricDifference.swift in Sources */, + 7DE9209029CA70F4004483EB /* Rope.swift in Sources */, + 7DE9213B29CA70F4004483EB /* TreeSet+Hashable.swift in Sources */, + 7DE9200D29CA70F3004483EB /* OrderedDictionary+Equatable.swift in Sources */, + 7DE9207C29CA70F4004483EB /* BigSubstring.swift in Sources */, + 7DE9205F29CA70F4004483EB /* BigString+Index.swift in Sources */, + 7DE920C429CA70F4004483EB /* BitSet+SetAlgebra isStrictSuperset.swift in Sources */, + 7DE9216229CA70F4004483EB /* _AncestorHashSlots.swift in Sources */, + 7DE9216B29CA70F4004483EB /* TreeDictionary+Codable.swift in Sources */, + 7DE9201A29CA70F3004483EB /* OrderedDictionary+Sendable.swift in Sources */, + 7DE9205D29CA70F4004483EB /* Deque._Storage.swift in Sources */, + 7DE9203B29CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift in Sources */, + 7DE9213A29CA70F4004483EB /* TreeSet.swift in Sources */, + 7DE9213829CA70F4004483EB /* TreeSet+Filter.swift in Sources */, + 7DE9206D29CA70F4004483EB /* BigString+Chunk+Description.swift in Sources */, + 7DE9208A29CA70F4004483EB /* BigString+Comparable.swift in Sources */, + 7DE9213629CA70F4004483EB /* TreeSet+SetAlgebra basics.swift in Sources */, + 7DE9202C29CA70F3004483EB /* OrderedSet+Initializers.swift in Sources */, + 7DE9213229CA70F4004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift in Sources */, + 7DE9203429CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift in Sources */, + 7DE9214A29CA70F4004483EB /* _HashNode+Subtree Insertions.swift in Sources */, + 7DE9215829CA70F4004483EB /* _HashNode+Structural union.swift in Sources */, + 7DE9204629CA70F3004483EB /* _Hashtable+Header.swift in Sources */, + 7DE9208129CA70F4004483EB /* BigString+CustomStringConvertible.swift in Sources */, + 7DE920A929CA70F4004483EB /* Optional Utilities.swift in Sources */, + 7DE920BD29CA70F4004483EB /* BitSet+SetAlgebra symmetricDifference.swift in Sources */, + 7DE9208229CA70F4004483EB /* BigString+BidirectionalCollection.swift in Sources */, + 7DE9203E29CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtract.swift in Sources */, + 7DE9216929CA70F4004483EB /* TreeDictionary+Filter.swift in Sources */, + 7DE9203129CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift in Sources */, + 7DE9209B29CA70F4004483EB /* Rope+Split.swift in Sources */, + 7DE9206629CA70F4004483EB /* BigString+Debugging.swift in Sources */, + 7DE9205229CA70F3004483EB /* _UnsafeWrappedBuffer.swift in Sources */, + 7DE9201129CA70F3004483EB /* OrderedDictionary+Initializers.swift in Sources */, + 7DE9212B29CA70F4004483EB /* TreeSet+SetAlgebra subtracting.swift in Sources */, + 7DE920F629CA70F4004483EB /* BitArray+Equatable.swift in Sources */, + 7D9B859829E4F74400B291CD /* BitArray+Descriptions.swift in Sources */, + 7DE9212329CA70F4004483EB /* TreeSet+SetAlgebra isEqualSet.swift in Sources */, + 7DE9214629CA70F4004483EB /* _HashSlot.swift in Sources */, + 7DEBDB0129CBEE5300ADC226 /* _UnsafeBitSet+Index.swift in Sources */, + 7DE9204129CA70F3004483EB /* _UnsafeBitset.swift in Sources */, + 7DE9208F29CA70F4004483EB /* RopeElement.swift in Sources */, + 7DE9207229CA70F4004483EB /* BigString+Managing Breaks.swift in Sources */, + 7DE9213F29CA70F4004483EB /* TreeSet+SetAlgebra isDisjoint.swift in Sources */, + 7DE9214D29CA70F4004483EB /* _HashNode+Structural filter.swift in Sources */, + 7DE9209F29CA70F4004483EB /* Rope+MutatingForEach.swift in Sources */, + 7DE9214E29CA70F4004483EB /* _HashNode+Debugging.swift in Sources */, + 7DE920A029CA70F4004483EB /* Rope+Join.swift in Sources */, + 7DE9213E29CA70F4004483EB /* TreeSet+SetAlgebra union.swift in Sources */, + 7DE9214829CA70F4004483EB /* _HashNode+Primitive Removals.swift in Sources */, + 7DE9216029CA70F4004483EB /* _HashNode+Structural isDisjoint.swift in Sources */, + 7DE9203529CA70F3004483EB /* OrderedSet+CustomReflectable.swift in Sources */, + 7DE920C929CA70F4004483EB /* BitSet+BidirectionalCollection.swift in Sources */, + 7DE920ED29CA70F4004483EB /* BitArray+Extras.swift in Sources */, + 7DE9206829CA70F4004483EB /* BigString+Chunk+Append and Insert.swift in Sources */, + 7DE9200E29CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift in Sources */, + 7DE920D229CA70F4004483EB /* BitSet+SetAlgebra basics.swift in Sources */, + 7DE9212A29CA70F4004483EB /* TreeSet+Extras.swift in Sources */, + 7DE9209A29CA70F4004483EB /* Rope+Append.swift in Sources */, + 7DE9203F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift in Sources */, + 7DE9208629CA70F4004483EB /* BigString+LosslessStringConvertible.swift in Sources */, + 7DE9204329CA70F3004483EB /* _HashTable+Bucket.swift in Sources */, + 7DE9216A29CA70F4004483EB /* TreeDictionary.swift in Sources */, + 7DEBDB9D29CCE73D00ADC226 /* Collections.swift in Sources */, + 7DE9215C29CA70F4004483EB /* _Bitmap.swift in Sources */, + 7DE9207029CA70F4004483EB /* BigString+Chunk+RopeElement.swift in Sources */, + 7DEBDB0B29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift in Sources */, + 7DE9213729CA70F4004483EB /* TreeSet+Sequence.swift in Sources */, + 7DE9217629CA70F4004483EB /* TreeDictionary+Initializers.swift in Sources */, + 7DE9207E29CA70F4004483EB /* BigString+UTF16View.swift in Sources */, + 7DEBDAFA29CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift in Sources */, + 7DE9216F29CA70F4004483EB /* TreeDictionary+CustomReflectable.swift in Sources */, + 7DE920D729CA70F4004483EB /* BitSet+SetAlgebra isSuperset.swift in Sources */, + 7DE920F429CA70F4004483EB /* BitArray+Hashable.swift in Sources */, + 7DE920E629CA70F4004483EB /* BitArray+ChunkedBitsIterators.swift in Sources */, + 7DE9206A29CA70F4004483EB /* BigString+Chunk+Counts.swift in Sources */, + 7DE9207529CA70F4004483EB /* BigString+Insert.swift in Sources */, + 7DE9216129CA70F4004483EB /* _HashNode+Structural merge.swift in Sources */, + 7DE9202F29CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift in Sources */, + 7DEBDAFC29CBEE5300ADC226 /* Debugging.swift in Sources */, + 7DE9202B29CA70F3004483EB /* OrderedSet+Equatable.swift in Sources */, + 7DE920EA29CA70F4004483EB /* BitArray.swift in Sources */, + 7DE920CB29CA70F4004483EB /* BitSet+Random.swift in Sources */, + 7DE9201629CA70F3004483EB /* OrderedDictionary+Elements.swift in Sources */, + 7DE9216D29CA70F4004483EB /* TreeDictionary+Debugging.swift in Sources */, + 7DE9213129CA70F4004483EB /* TreeSet+CustomReflectable.swift in Sources */, + 7DE9215329CA70F4004483EB /* _HashNode+Invariants.swift in Sources */, + 7DE9209629CA70F4004483EB /* Rope+Builder.swift in Sources */, + 7DE9215129CA70F4004483EB /* _HashNode+Subtree Modify.swift in Sources */, + 7DE9204729CA70F3004483EB /* _HashTable+Testing.swift in Sources */, + 7DE920E929CA70F4004483EB /* BitArray+Invariants.swift in Sources */, + 7DE9215429CA70F4004483EB /* _HashNode+UnsafeHandle.swift in Sources */, + 7DE9209829CA70F4004483EB /* Rope+_Node.swift in Sources */, + 7DE9204529CA70F3004483EB /* _HashTable+Constants.swift in Sources */, + 7DE9214229CA70F4004483EB /* _HashStack.swift in Sources */, + 7DE9206F29CA70F4004483EB /* BigString+Chunk+Breaks.swift in Sources */, + 7DE9202529CA70F3004483EB /* OrderedSet+Hashable.swift in Sources */, + 7DE9203A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra+Basics.swift in Sources */, + 7DE920B529CA70F4004483EB /* _SortedCollection.swift in Sources */, + 7DE9206929CA70F4004483EB /* BigString+Chunk+Indexing by UTF16.swift in Sources */, + 7DE9202A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift in Sources */, + 7DEBDAF229CBEE5300ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */, + 7DE9207D29CA70F4004483EB /* BigSubstring+UTF16View.swift in Sources */, + 7DEBDAF829CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */, + 7DE9217229CA70F4004483EB /* TreeDictionary+Sendable.swift in Sources */, + 7DE9207729CA70F4004483EB /* Range+BigString.swift in Sources */, + 7DE9204B29CA70F3004483EB /* _DequeBuffer.swift in Sources */, + 7DE920DF29CA70F4004483EB /* Slice+Utilities.swift in Sources */, + 7DE9215729CA70F4004483EB /* _RawHashNode.swift in Sources */, + 7DE9209529CA70F4004483EB /* RopeSummary.swift in Sources */, + 7DE920C229CA70F4004483EB /* BitSet+Sorted Collection APIs.swift in Sources */, + 7DE9205029CA70F3004483EB /* _DequeSlot.swift in Sources */, + 7DE920E229CA70F4004483EB /* _Word.swift in Sources */, + 7DE9201429CA70F3004483EB /* OrderedDictionary+Sequence.swift in Sources */, + 7DE920DB29CA70F4004483EB /* BitSet+Hashable.swift in Sources */, + 7DE9208C29CA70F4004483EB /* _RopeItem.swift in Sources */, + 7D9B859A29E4F74400B291CD /* BitArray+RandomBits.swift in Sources */, + 7DEBDB0929CBEE5300ADC226 /* Integer rank.swift in Sources */, + 7DE920EC29CA70F4004483EB /* BitArray+Collection.swift in Sources */, + 7DE9201729CA70F3004483EB /* OrderedDictionary+Codable.swift in Sources */, + 7DE9208329CA70F4004483EB /* BigString+CustomDebugStringConvertible.swift in Sources */, + 7DE9215D29CA70F4004483EB /* _StorageHeader.swift in Sources */, + 7DE9213329CA70F4004483EB /* TreeSet+Collection.swift in Sources */, + 7DE9217929CA70F4004483EB /* Heap+UnsafeHandle.swift in Sources */, + 7DE920BC29CA70F4004483EB /* _UniqueCollection.swift in Sources */, + 7D9B859B29E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift in Sources */, + 7DE9216529CA70F4004483EB /* _HashNode+Structural intersection.swift in Sources */, + 7DEBDAF329CBEE5300ADC226 /* Array+WithContiguousStorage Compatibility.swift in Sources */, + 7DE920A629CA70F4004483EB /* String Utilities.swift in Sources */, + 7DE920E129CA70F4004483EB /* UInt+Tricks.swift in Sources */, + 7DE920EB29CA70F4004483EB /* BitArray+Fill.swift in Sources */, + 7DE9201929CA70F3004483EB /* OrderedDictionary+Values.swift in Sources */, + 7DE9213929CA70F4004483EB /* TreeSet+SetAlgebra Initializers.swift in Sources */, + 7DE9217729CA70F4004483EB /* TreeDictionary+Keys.swift in Sources */, + 7DE920DA29CA70F4004483EB /* BitSet._UnsafeHandle.swift in Sources */, + 7DE920D129CA70F4004483EB /* BitSet.Index.swift in Sources */, + 7DE9207429CA70F4004483EB /* BigString+ReplaceSubrange.swift in Sources */, + 7DE920DC29CA70F4004483EB /* BitSet+Invariants.swift in Sources */, + 7DE9216729CA70F4004483EB /* TreeDictionary+Equatable.swift in Sources */, + 7DE9204C29CA70F3004483EB /* Deque+Collection.swift in Sources */, + 7DE9201F29CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift in Sources */, + 7DE9202E29CA70F3004483EB /* OrderedSet+UnorderedView.swift in Sources */, + 7DE9215F29CA70F4004483EB /* _UnsafePath.swift in Sources */, + 7DE9202829CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSubset.swift in Sources */, + 7DE9204929CA70F3004483EB /* _HashTable+BucketIterator.swift in Sources */, + 7DE920F029CA70F4004483EB /* BitArray+RangeReplaceableCollection.swift in Sources */, + 7DE920C529CA70F4004483EB /* BitSet+Extras.swift in Sources */, + 7DE920CF29CA70F4004483EB /* BitSet+Initializers.swift in Sources */, + 7DE9201D29CA70F3004483EB /* OrderedDictionary.swift in Sources */, + 7DE920A529CA70F4004483EB /* Rope+Collection.swift in Sources */, + 7DE9203629CA70F3004483EB /* OrderedSet+ReserveCapacity.swift in Sources */, + 7DE9208D29CA70F4004483EB /* Rope+Debugging.swift in Sources */, + 7DE9208829CA70F4004483EB /* BigString+ExpressibleByStringLiteral.swift in Sources */, + 7DE9202329CA70F3004483EB /* OrderedSet+UnstableInternals.swift in Sources */, + 7DE9208529CA70F4004483EB /* BigString+TextOutputStream.swift in Sources */, + 7DE9212829CA70F4004483EB /* TreeSet+SetAlgebra intersection.swift in Sources */, + 7DE9200F29CA70F3004483EB /* OrderedDictionary+Hashable.swift in Sources */, + 7DE9206429CA70F4004483EB /* BigString+Invariants.swift in Sources */, + 7DE9212C29CA70F4004483EB /* TreeSet+SetAlgebra subtract.swift in Sources */, + 7DE9203D29CA70F3004483EB /* OrderedSet+Partial SetAlgebra intersection.swift in Sources */, + 7DE9214429CA70F4004483EB /* _HashTreeIterator.swift in Sources */, + 7DE9216829CA70F4004483EB /* TreeDictionary+Sequence.swift in Sources */, + 7DE9212D29CA70F4004483EB /* TreeSet+Debugging.swift in Sources */, + 7DE920D329CA70F4004483EB /* BitSet+SetAlgebra isDisjoint.swift in Sources */, + 7DE9203229CA70F3004483EB /* OrderedSet+Sendable.swift in Sources */, + 7DE9206E29CA70F4004483EB /* BigString+Chunk+Splitting.swift in Sources */, + 7DE920D829CA70F4004483EB /* BitSet+SetAlgebra subtracting.swift in Sources */, + 7DE9209E29CA70F4004483EB /* Rope+ForEachWhile.swift in Sources */, + 7DE9217B29CA70F4004483EB /* Heap+Invariants.swift in Sources */, + 7DE9212F29CA70F4004483EB /* TreeSet+SetAlgebra formIntersection.swift in Sources */, + 7DE920A329CA70F4004483EB /* Rope+Index.swift in Sources */, + 7DE9207329CA70F4004483EB /* BigString+RemoveSubrange.swift in Sources */, + 7DE9205829CA70F3004483EB /* Deque._UnsafeHandle.swift in Sources */, + 7DE9213529CA70F4004483EB /* TreeSet+SetAlgebra symmetricDifference.swift in Sources */, + 7DE920D429CA70F4004483EB /* BitSet+SetAlgebra union.swift in Sources */, + 7DE920C329CA70F4004483EB /* BitSet+SetAlgebra formUnion.swift in Sources */, + 7DE9217429CA70F4004483EB /* TreeDictionary+Collection.swift in Sources */, + 7DE9214729CA70F4004483EB /* _HashNode+Primitive Replacement.swift in Sources */, + 7DE9201229CA70F3004483EB /* OrderedDictionary+Elements.SubSequence.swift in Sources */, + 7DE9204429CA70F3004483EB /* _HashTable.swift in Sources */, + 7DE9206529CA70F4004483EB /* BigString+Ingester.swift in Sources */, + 7DEBDAFB29CBEE5300ADC226 /* Descriptions.swift in Sources */, + 7DE9209129CA70F4004483EB /* Rope+_UnsafeHandle.swift in Sources */, + 7DE920D529CA70F4004483EB /* BitSet.swift in Sources */, + 7DE9206B29CA70F4004483EB /* BigString+Chunk+Indexing by Characters.swift in Sources */, + 7DE9217D29CA70F4004483EB /* Heap.swift in Sources */, + 7DE9201529CA70F3004483EB /* OrderedDictionary+Partial RangeReplaceableCollection.swift in Sources */, + 7DE9208929CA70F4004483EB /* BigString+RangeReplaceableCollection.swift in Sources */, + 7DE9205E29CA70F4004483EB /* BigString+Metrics.swift in Sources */, + 7DE9213D29CA70F4004483EB /* TreeSet+SetAlgebra isSubset.swift in Sources */, + 7DE9203029CA70F3004483EB /* OrderedSet+SubSequence.swift in Sources */, + 7DE9215529CA70F4004483EB /* _RawHashNode+UnsafeHandle.swift in Sources */, + 7DE9209429CA70F4004483EB /* Rope+_UnmanagedLeaf.swift in Sources */, + 7DE9208429CA70F4004483EB /* BigString+Equatable.swift in Sources */, + 7DE9203729CA70F3004483EB /* OrderedSet+Partial SetAlgebra union.swift in Sources */, + 7DEBDAF929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift in Sources */, + 7DE9215229CA70F4004483EB /* _HashNode+Builder.swift in Sources */, + 7DE9212929CA70F4004483EB /* TreeSet+SetAlgebra formUnion.swift in Sources */, + 7DE920C129CA70F4004483EB /* BitSet+SetAlgebra subtract.swift in Sources */, + 7DE9201B29CA70F3004483EB /* OrderedDictionary+Descriptions.swift in Sources */, + 7DE9213C29CA70F4004483EB /* TreeSet+Codable.swift in Sources */, + 7DE9207129CA70F4004483EB /* BigString+Split.swift in Sources */, + 7DE9214F29CA70F4004483EB /* _Bucket.swift in Sources */, + 7DE9208029CA70F4004483EB /* BigString+Hashing.swift in Sources */, + 7DE920F829CA70F4004483EB /* BitArray+Testing.swift in Sources */, + 7DE920D629CA70F4004483EB /* BitSet+Equatable.swift in Sources */, + 7DE9217A29CA70F4004483EB /* Heap+Descriptions.swift in Sources */, + 7DE9216429CA70F4004483EB /* _HashNode.swift in Sources */, + 7DE9201E29CA70F3004483EB /* OrderedSet+Partial SetAlgebra symmetricDifference.swift in Sources */, + 7DE9207A29CA70F4004483EB /* BigString+UTF8View.swift in Sources */, + 7DE9201329CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift in Sources */, + 7DE9201C29CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift in Sources */, + 7DE9202729CA70F3004483EB /* OrderedSet+Insertions.swift in Sources */, + 7DE9205B29CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift in Sources */, + 7DE9208729CA70F4004483EB /* BigString+Sequence.swift in Sources */, + 7DE9202229CA70F3004483EB /* OrderedSet+Diffing.swift in Sources */, + 7DE920CA29CA70F4004483EB /* BitSet+CustomStringConvertible.swift in Sources */, + 7DE9212E29CA70F4004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift in Sources */, + 7DE920A829CA70F4004483EB /* String.Index+ABI.swift in Sources */, + 7DE9204F29CA70F3004483EB /* Deque.swift in Sources */, + 7DE9214129CA70F4004483EB /* _HashNode+Initializers.swift in Sources */, + 7D9B859929E4F74400B291CD /* BitArray+LosslessStringConvertible.swift in Sources */, + 7DE9202D29CA70F3004483EB /* OrderedSet.swift in Sources */, + 7DEBDB0329CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift in Sources */, + 7DE9216329CA70F4004483EB /* _HashLevel.swift in Sources */, + 7D9B859729E4F74400B291CD /* BitArray+Shifts.swift in Sources */, + 7DE9216C29CA70F4004483EB /* TreeDictionary+Descriptions.swift in Sources */, + 7DE9205429CA70F3004483EB /* Deque+Sendable.swift in Sources */, + 7DE920A229CA70F4004483EB /* Rope+RemoveSubrange.swift in Sources */, + 7DE920C729CA70F4004483EB /* BitSet+ExpressibleByArrayLiteral.swift in Sources */, + 7DE9214929CA70F4004483EB /* _HashNode+Structural isEqualSet.swift in Sources */, + 7DE9206729CA70F4004483EB /* BigString+Builder.swift in Sources */, + 7DE9208B29CA70F4004483EB /* RopeMetric.swift in Sources */, + 7DE9205A29CA70F3004483EB /* Deque+Extras.swift in Sources */, + 7DEBDAF429CBEE5300ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */, + 7DE9205129CA70F3004483EB /* Deque+Hashable.swift in Sources */, + 7DE9209929CA70F4004483EB /* Rope+Extract.swift in Sources */, + 7DE9202629CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift in Sources */, + 7DE920E729CA70F4004483EB /* BitArray+CustomReflectable.swift in Sources */, + 7DE9207B29CA70F4004483EB /* BigSubstring+UnicodeScalarView.swift in Sources */, + 7DE9209D29CA70F4004483EB /* Rope+Insert.swift in Sources */, + 7DEBDB0229CBEE5300ADC226 /* _UnsafeBitSet.swift in Sources */, + 7DE9214329CA70F4004483EB /* _HashNode+Subtree Removals.swift in Sources */, + 7DE9207929CA70F4004483EB /* BigString+UnicodeScalarView.swift in Sources */, + 7DE9205329CA70F3004483EB /* _DequeBufferHeader.swift in Sources */, + 7DE9202429CA70F3004483EB /* OrderedSet+Invariants.swift in Sources */, + 7DE920A729CA70F4004483EB /* _CharacterRecognizer.swift in Sources */, + 7DE920A429CA70F4004483EB /* Rope+Sequence.swift in Sources */, + 7DE9201829CA70F3004483EB /* OrderedDictionary+Invariants.swift in Sources */, + 7DE920CD29CA70F4004483EB /* BitSet+CustomDebugStringConvertible.swift in Sources */, + 7DE9212529CA70F4004483EB /* TreeSet+SetAlgebra isStrictSubset.swift in Sources */, + 7DE9205C29CA70F3004483EB /* Deque+Equatable.swift in Sources */, + 7DE9209229CA70F4004483EB /* Rope+_Storage.swift in Sources */, + 7DE9203929CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift in Sources */, + 7DE9206329CA70F4004483EB /* BigString+Contents.swift in Sources */, + 7DE9215629CA70F4004483EB /* _HashNode+Structural compactMapValues.swift in Sources */, + 7DE920CC29CA70F4004483EB /* BitSet.Counted.swift in Sources */, + 7DE9207F29CA70F4004483EB /* BigSubstring+UTF8View.swift in Sources */, + 7DE920A129CA70F4004483EB /* Rope+Remove.swift in Sources */, + 7DE9215929CA70F4004483EB /* _HashNode+Structural isSubset.swift in Sources */, + 7DE920C629CA70F4004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift in Sources */, + 7DE9202129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift in Sources */, + 7DE9205529CA70F3004483EB /* Deque+Codable.swift in Sources */, + 7DE9206229CA70F4004483EB /* BigString+Iterators.swift in Sources */, + 7DE9206029CA70F4004483EB /* BigString+Summary.swift in Sources */, + 7DE9203829CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift in Sources */, + 7DE9207629CA70F4004483EB /* BigString+Initializers.swift in Sources */, + 7DE9206129CA70F4004483EB /* BigString.swift in Sources */, + 7DE9215A29CA70F4004483EB /* _HashNode+Structural subtracting.swift in Sources */, + 7DE9217E29CA70F4004483EB /* Heap+ExpressibleByArrayLiteral.swift in Sources */, + 7DE9204029CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSuperset.swift in Sources */, + 7DE920BF29CA70F4004483EB /* BitSet+Codable.swift in Sources */, + 7DE9203329CA70F3004483EB /* OrderedSet+Testing.swift in Sources */, + 7DE9212429CA70F4004483EB /* TreeSet+Descriptions.swift in Sources */, + 7DE9217129CA70F4004483EB /* TreeDictionary+Values.swift in Sources */, + 7DE920D029CA70F4004483EB /* BitSet+SetAlgebra isSubset.swift in Sources */, + 7DE920C829CA70F4004483EB /* BitSet+SetAlgebra isEqualSet.swift in Sources */, + 7DE9215B29CA70F4004483EB /* _HashNode+Structural mapValues.swift in Sources */, + 7DE9217029CA70F4004483EB /* TreeDictionary+ExpressibleByDictionaryLiteral.swift in Sources */, + 7DE9216E29CA70F4004483EB /* TreeDictionary+Hashable.swift in Sources */, + 7DE9215E29CA70F4004483EB /* _HashNode+Lookups.swift in Sources */, + 7DE9202029CA70F3004483EB /* OrderedSet+Descriptions.swift in Sources */, + 7DE9214B29CA70F4004483EB /* _HashTreeStatistics.swift in Sources */, + 7DE920E029CA70F4004483EB /* Range+Utilities.swift in Sources */, + 7DE920D929CA70F4004483EB /* BitSet+SetAlgebra intersection.swift in Sources */, + 7DE920DD29CA70F4004483EB /* BitSet+SetAlgebra isStrictSubset.swift in Sources */, + 7DE920F229CA70F4004483EB /* BitArray._UnsafeHandle.swift in Sources */, + 7DE9204229CA70F3004483EB /* _HashTable+UnsafeHandle.swift in Sources */, + 7DE9208E29CA70F4004483EB /* _RopeVersion.swift in Sources */, + 7DE9212629CA70F4004483EB /* TreeSet+Sendable.swift in Sources */, + 7DE9209C29CA70F4004483EB /* Rope+Find.swift in Sources */, + 7DE920E829CA70F4004483EB /* BitArray+BitwiseOperations.swift in Sources */, + 7DE9207829CA70F4004483EB /* BigString+Append.swift in Sources */, + 7DE9217F29CA70F4004483EB /* _HeapNode.swift in Sources */, + 7DE9202929CA70F3004483EB /* OrderedSet+Codable.swift in Sources */, + 7DE9204829CA70F3004483EB /* _HashTable+CustomStringConvertible.swift in Sources */, + 7DE9213029CA70F4004483EB /* TreeSet+SetAlgebra isSuperset.swift in Sources */, + 7DE9217529CA70F4004483EB /* TreeDictionary+MapValues.swift in Sources */, + 7DE9206C29CA70F4004483EB /* BigString+Chunk.swift in Sources */, + 7DE920F329CA70F4004483EB /* BitArray+Copy.swift in Sources */, + 7DE9214029CA70F4004483EB /* _Hash.swift in Sources */, + 7DE9205629CA70F3004483EB /* Deque+Testing.swift in Sources */, + 7DEBDB0829CBEE5300ADC226 /* UInt+reversed.swift in Sources */, + 7DE9209329CA70F4004483EB /* Rope+Invariants.swift in Sources */, + 7DE920EE29CA70F4004483EB /* BitArray+ExpressibleByArrayLiteral.swift in Sources */, + 7DEBDB0A29CBEE5300ADC226 /* UInt+first and last set bit.swift in Sources */, + 7DE9214C29CA70F4004483EB /* _HashNode+Primitive Insertions.swift in Sources */, + 7DE920BE29CA70F4004483EB /* BitSet+SetAlgebra formIntersection.swift in Sources */, + 7DE9217329CA70F4004483EB /* TreeDictionary+Merge.swift in Sources */, + 7DE9204E29CA70F3004483EB /* Deque+CustomReflectable.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DE91B2A29CA6721004483EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DEBDB7F29CCE44A00ADC226 /* CheckBidirectionalCollection.swift in Sources */, + 7DE9221829CA8576004483EB /* HashTableTests.swift in Sources */, + 7DE9220C29CA8576004483EB /* Availability.swift in Sources */, + 7DE9220029CA8576004483EB /* TreeDictionary Smoke Tests.swift in Sources */, + 7DEBDB9029CCE44A00ADC226 /* TestContext.swift in Sources */, + 7DE9221429CA8576004483EB /* OrderedSet Diffing Tests.swift in Sources */, + 7DEBDB1D29CBF6B200ADC226 /* RandomAccessCollection+Offsets.swift in Sources */, + 7DEBDB8529CCE44A00ADC226 /* AllOnesRandomNumberGenerator.swift in Sources */, + 7DEBDB1429CBF68D00ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */, + 7DE921FD29CA8576004483EB /* CombinatoricsChecks.swift in Sources */, + 7DEBDB8B29CCE44A00ADC226 /* _CollectionState.swift in Sources */, + 7DE9220229CA8576004483EB /* TreeDictionary Tests.swift in Sources */, + 7DEBDB8429CCE44A00ADC226 /* CheckSequence.swift in Sources */, + 7DEBDB8029CCE44A00ADC226 /* MinimalRandomAccessCollection.swift in Sources */, + 7DEBDB0F29CBF68900ADC226 /* UInt+reversed.swift in Sources */, + 7DEBDB1E29CBF6B200ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */, + 7DE9221C29CA8576004483EB /* RangeReplaceableCollectionTests.swift in Sources */, + 7DE9220429CA8576004483EB /* TreeHashedCollections Fixtures.swift in Sources */, + 7DEBDB7A29CCE44A00ADC226 /* SetAPIChecker.swift in Sources */, + 7DE9221B29CA8576004483EB /* DequeInternals.swift in Sources */, + 7DEBDB6E29CCE44A00ADC226 /* SortedCollectionAPIChecker.swift in Sources */, + 7DE9220929CA8576004483EB /* BitArrayTests.swift in Sources */, + 7DEBDB1C29CBF6B200ADC226 /* Debugging.swift in Sources */, + 7DE9221629CA8576004483EB /* OrderedSet.UnorderedView Tests.swift in Sources */, + 7DEBDB7529CCE44A00ADC226 /* MinimalCollection.swift in Sources */, + 7DEBDB1029CBF68900ADC226 /* UInt+first and last set bit.swift in Sources */, + 7DEBDB8C29CCE44A00ADC226 /* MinimalIterator.swift in Sources */, + 7DEBDB8829CCE44A00ADC226 /* MinimalSequence.swift in Sources */, + 7DEBDB1129CBF68900ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift in Sources */, + 7DE921FE29CA8576004483EB /* Hash.swift in Sources */, + 7DEBDB8929CCE44A00ADC226 /* Combinatorics.swift in Sources */, + 7DE9221A29CA8576004483EB /* HeapNodeTests.swift in Sources */, + 7DEBDB7029CCE44A00ADC226 /* ResettableValue.swift in Sources */, + 7DE9221129CA8576004483EB /* OrderedDictionary+Values Tests.swift in Sources */, + 7DEBDB7D29CCE44A00ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift in Sources */, + 7DEBDB8E29CCE44A00ADC226 /* Integer Square Root.swift in Sources */, + 7DE9221E29CA8576004483EB /* MutableCollectionTests.swift in Sources */, + 7DEBDB1229CBF68D00ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */, + 7DE9221529CA8576004483EB /* RandomAccessCollection+Extras.swift in Sources */, + 7DEBDB7929CCE44A00ADC226 /* LifetimeTracked.swift in Sources */, + 7DE921FC29CA8576004483EB /* IndexRangeCollectionTests.swift in Sources */, + 7DE9221229CA8576004483EB /* OrderedDictionary+Elements Tests.swift in Sources */, + 7DEBDB7629CCE44A00ADC226 /* MinimalIndex.swift in Sources */, + 7DE9221329CA8576004483EB /* OrderedSetInternals.swift in Sources */, + 7DE9220329CA8576004483EB /* Colliders.swift in Sources */, + 7DE9221729CA8576004483EB /* OrderedSetTests.swift in Sources */, + 7DE9221D29CA8576004483EB /* DequeTests.swift in Sources */, + 7DEBDB1A29CBF6B200ADC226 /* Descriptions.swift in Sources */, + 7DE921FA29CA8576004483EB /* UtilitiesTests.swift in Sources */, + 7DE9220A29CA8576004483EB /* TestRope.swift in Sources */, + 7DE9220529CA8576004483EB /* TreeDictionary.Values Tests.swift in Sources */, + 7DEBDB8D29CCE44A00ADC226 /* RandomStableSample.swift in Sources */, + 7DEBDB7429CCE44A00ADC226 /* DictionaryAPIChecker.swift in Sources */, + 7DEBDB8A29CCE44A00ADC226 /* Box.swift in Sources */, + 7DEBDB8629CCE44A00ADC226 /* LifetimeTracker.swift in Sources */, + 7DEBDB8229CCE44A00ADC226 /* RepeatableRandomNumberGenerator.swift in Sources */, + 7DEBDB7129CCE44A00ADC226 /* MinimalEncoder.swift in Sources */, + 7DE921FB29CA8576004483EB /* MinimalTypeConformances.swift in Sources */, + 7DEBDB8129CCE44A00ADC226 /* CheckCollection.swift in Sources */, + 7DEBDB7229CCE44A00ADC226 /* MinimalBidirectionalCollection.swift in Sources */, + 7DEBDB1B29CBF6B200ADC226 /* UnsafeBufferPointer+Extras.swift in Sources */, + 7DEBDB8729CCE44A00ADC226 /* MinimalMutableRandomAccessCollection.swift in Sources */, + 7DEBDB7829CCE44A00ADC226 /* Assertions.swift in Sources */, + 7DE9220D29CA8576004483EB /* TestBigString.swift in Sources */, + 7DEBDB8329CCE44A00ADC226 /* CheckHashable.swift in Sources */, + 7DE921FF29CA8576004483EB /* Utilities.swift in Sources */, + 7DEBDB8F29CCE44A00ADC226 /* CheckEquatable.swift in Sources */, + 7DEBDB7729CCE44A00ADC226 /* HashableBox.swift in Sources */, + 7DE9220E29CA8576004483EB /* SampleStrings.swift in Sources */, + 7DEBDB9129CCE44A00ADC226 /* CollectionTestCase.swift in Sources */, + 7DEBDB1829CBF69F00ADC226 /* _UnsafeBitSet+_Word.swift in Sources */, + 7DE921B929CA81DC004483EB /* _SortedCollection.swift in Sources */, + 7DEBDB1629CBF69F00ADC226 /* _UnsafeBitSet+Index.swift in Sources */, + 7DEBDB1529CBF68D00ADC226 /* UnsafeRawPointer extensions.swift in Sources */, + 7DEBDB7B29CCE44A00ADC226 /* IndexRangeCollection.swift in Sources */, + 7DE9221929CA8576004483EB /* HeapTests.swift in Sources */, + 7DEBDB1729CBF69F00ADC226 /* _UnsafeBitSet.swift in Sources */, + 7DEBDB7E29CCE44A00ADC226 /* CheckComparable.swift in Sources */, + 7DEBDB7C29CCE44A00ADC226 /* MinimalDecoder.swift in Sources */, + 7DE9220829CA8576004483EB /* BitSetTests.swift in Sources */, + 7DEBDB7329CCE44A00ADC226 /* _MinimalCollectionCore.swift in Sources */, + 7DEBDB1329CBF68D00ADC226 /* Array+WithContiguousStorage Compatibility.swift in Sources */, + 7DE921C829CA81DC004483EB /* _UniqueCollection.swift in Sources */, + 7DE9220629CA8576004483EB /* TreeSet Tests.swift in Sources */, + 7DEBDB9229CCE44A00ADC226 /* StringConvertibleValue.swift in Sources */, + 7DEBDB6F29CCE44A00ADC226 /* MinimalMutableRangeReplaceableRandomAccessCollection.swift in Sources */, + 7DE9220129CA8576004483EB /* TreeDictionary.Keys Tests.swift in Sources */, + 7DEBDB0E29CBF68900ADC226 /* Integer rank.swift in Sources */, + 7DE9220729CA8576004483EB /* BitSet.Counted Tests.swift in Sources */, + 7DE9220F29CA8576004483EB /* OrderedDictionary Tests.swift in Sources */, + 7DE9221029CA8576004483EB /* OrderedDictionary Utils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7DE91B3129CA6721004483EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7DE91B2529CA6721004483EB /* Collections */; + targetProxy = 7DE91B3029CA6721004483EB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 7DE91B3629CA6721004483EB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9829CCE4A600ADC226 /* Shared.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 7DE91B3729CA6721004483EB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9829CCE4A600ADC226 /* Shared.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 7DE91B3929CA6721004483EB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9929CCE4A600ADC226 /* Collections.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 7DE91B3A29CA6721004483EB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9929CCE4A600ADC226 /* Collections.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 7DE91B3C29CA6721004483EB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 7DE91B3D29CA6721004483EB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7DE91B2029CA6721004483EB /* Build configuration list for PBXProject "Collections" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DE91B3629CA6721004483EB /* Debug */, + 7DE91B3729CA6721004483EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7DE91B3829CA6721004483EB /* Build configuration list for PBXNativeTarget "Collections" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DE91B3929CA6721004483EB /* Debug */, + 7DE91B3A29CA6721004483EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7DE91B3B29CA6721004483EB /* Build configuration list for PBXNativeTarget "CollectionsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DE91B3C29CA6721004483EB /* Debug */, + 7DE91B3D29CA6721004483EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7DE91B1D29CA6721004483EB /* Project object */; +} diff --git a/Xcode/Collections.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Xcode/Collections.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Xcode/Collections.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Xcode/Collections.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Xcode/Collections.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Xcode/Collections.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme b/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme new file mode 100644 index 000000000..2c3287f6d --- /dev/null +++ b/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Xcode/Collections.xctestplan b/Xcode/Collections.xctestplan new file mode 100644 index 000000000..446f3f5d8 --- /dev/null +++ b/Xcode/Collections.xctestplan @@ -0,0 +1,25 @@ +{ + "configurations" : [ + { + "id" : "18C3508F-98DB-45BE-9CFC-5159D79BA600", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:Collections.xcodeproj", + "identifier" : "7DE91B2D29CA6721004483EB", + "name" : "CollectionsTests" + } + } + ], + "version" : 1 +} diff --git a/Xcode/CollectionsTests.xcconfig b/Xcode/CollectionsTests.xcconfig new file mode 100644 index 000000000..8c257edce --- /dev/null +++ b/Xcode/CollectionsTests.xcconfig @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +PRODUCT_NAME = CollectionsTests +PRODUCT_BUNDLE_IDENTIFIER = org.swift.CollectionsTests + +SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator +ARCHS = $(ARCHS_STANDARD) + +MACOSX_DEPLOYMENT_TARGET = 12.0 +IPHONEOS_DEPLOYMENT_TARGET = 15.0 +WATCHOS_DEPLOYMENT_TARGET = 8.0 +TVOS_DEPLOYMENT_TARGET = 15.0 + +CURRENT_PROJECT_VERSION = 1 +MARKETING_VERSION = 1.0 + +GENERATE_INFOPLIST_FILE = YES +CODE_SIGN_STYLE = Automatic + +ENABLE_TESTABILITY = NO + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) COLLECTIONS_RANDOMIZED_TESTING diff --git a/Xcode/README.md b/Xcode/README.md new file mode 100644 index 000000000..0b8ae3825 --- /dev/null +++ b/Xcode/README.md @@ -0,0 +1,5 @@ +# Xcode build files + +The project file here is used to build a variant of this package with Xcode. The project file is a regular Xcode project that builds the code base using the COLLECTIONS_SINGLE_MODULE configuration, producing a single framework bundle. Builds settings are entirely configured via the provided xcconfig files. + +Beware! The contents of this directory are not source stable. They are provided as is, with no compatibility promises across package releases. Future versions of this package can arbitrarily change these files or remove them, without any advance notice. (This can include patch releases.) diff --git a/Xcode/Shared.xcconfig b/Xcode/Shared.xcconfig new file mode 100644 index 000000000..f3b041e06 --- /dev/null +++ b/Xcode/Shared.xcconfig @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +SDKROOT = macosx + +SWIFT_VERSION = 5.5 +ONLY_ACTIVE_ARCH[config=Debug] = YES + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) COLLECTIONS_SINGLE_MODULE +SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = $(inherited) COLLECTIONS_INTERNAL_CHECKS DEBUG + +SWIFT_COMPILATION_MODE[config=Release] = wholemodule +SWIFT_COMPILATION_MODE[config=Debug] = singlefile + +SWIFT_OPTIMIZATION_LEVEL[config=Release] = -O +SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone + +SWIFT_EMIT_LOC_STRINGS = NO +SWIFT_INSTALL_OBJC_HEADER = NO + +COPY_PHASE_STRIP = NO + +DEBUG_INFORMATION_FORMAT[config=Release] = dwarf-with-dsym +DEBUG_INFORMATION_FORMAT[config=Debug] = dwarf + +ALWAYS_SEARCH_USER_PATHS = NO + +GCC_DYNAMIC_NO_PIC = NO +GCC_NO_COMMON_BLOCKS = YES +GCC_OPTIMIZATION_LEVEL[config=Debug] = 0 + +GCC_C_LANGUAGE_STANDARD = gnu11 +CLANG_CXX_LANGUAGE_STANDARD = gnu++20 +CLANG_ENABLE_MODULES = YES +CLANG_ENABLE_OBJC_ARC = YES +CLANG_ENABLE_OBJC_WEAK = YES +ENABLE_NS_ASSERTIONS[config=Release] = NO +ENABLE_STRICT_OBJC_MSGSEND = YES +GCC_PREPROCESSOR_DEFINITIONS[config=Release] = DEBUG=1 $(inherited) + +CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES +CLANG_WARN_BOOL_CONVERSION = YES +CLANG_WARN_COMMA = YES +CLANG_WARN_CONSTANT_CONVERSION = YES +CLANG_WARN_DOCUMENTATION_COMMENTS = YES +CLANG_WARN_EMPTY_BODY = YES +CLANG_WARN_ENUM_CONVERSION = YES +CLANG_WARN_INFINITE_RECURSION = YES +CLANG_WARN_INT_CONVERSION = YES +CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN_UNREACHABLE_CODE = YES + +GCC_WARN_64_TO_32_BIT_CONVERSION = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE +GCC_WARN_UNUSED_FUNCTION = YES +GCC_WARN_UNUSED_VARIABLE = YES +CLANG_WARN_RANGE_LOOP_ANALYSIS = YES +CLANG_WARN_SUSPICIOUS_MOVE = YES +CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES +CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR +CLANG_WARN_OBJC_LITERAL_CONVERSION = YES +CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +CLANG_ANALYZER_NONNULL = YES +CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE diff --git a/cmake/modules/SwiftSupport.cmake b/cmake/modules/SwiftSupport.cmake index d3d405918..2a63a91c9 100644 --- a/cmake/modules/SwiftSupport.cmake +++ b/cmake/modules/SwiftSupport.cmake @@ -17,7 +17,7 @@ See https://swift.org/LICENSE.txt for license information function(get_swift_host_arch result_var_name) if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") set("${result_var_name}" "x86_64" PARENT_SCOPE) - elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "AArch64|aarch64|arm64") + elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "AArch64|aarch64|arm64|ARM64") if(CMAKE_SYSTEM_NAME MATCHES Darwin) set("${result_var_name}" "arm64" PARENT_SCOPE) else()