Skip to content

Commit 0d0d66f

Browse files
committed
Documentation refactors, polishes, and add SubSequences
Key Changes: - Add custom SubSequences - Custom SortedDictionary.(Keys|Values).Sequence iterator - Add SortedDictionary.modifyValue(forKey:default:_:) Minor Changes: - Update documentation - Various API optimizations - Fix bug where offsetBy:limitedBy: calls did not validate indices - Remove unique initializers with documented duplicate handling behavior. - Implement _Node.UnsafeHandle.append*() methods - SortedSet no longer allocates a 'values' buffer (_Node.Header.values is now optional) Documentation: - Complexity guarantees - BidirectionalCollection conformances - Sequence/IteratorProtocol methods Optimizations: - Add optimized dictionary value mapping (through Node.init(mappingFrom:_:)) - Custom _BTree.forEach - Greater explicit @inline(__always) annotations throughout. - Simplify _BTree: Sequence implementation - Move methods to using _BTree.Builder - _BTree.FixedSizeArray now uses `withUnsafePointer` to prevent branching.
1 parent 21d9bde commit 0d0d66f

29 files changed

+1545
-241
lines changed

Sources/SortedCollections/BTree/_BTree+Partial RangeReplaceableCollection.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ extension _BTree {
2020
_ isIncluded: (Element) throws -> Bool
2121
) rethrows -> _BTree {
2222
// TODO: optimize implementation to O(n)
23-
var newTree: _BTree = _BTree()
23+
var builder = Builder()
2424
for element in self where try isIncluded(element) {
25-
newTree.updateAnyValue(element.value, forKey: element.key)
25+
builder.append(element)
2626
}
27-
return newTree
27+
return builder.finish()
2828
}
2929

3030

Sources/SortedCollections/BTree/_BTree+Sequence.swift

Lines changed: 105 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,113 +10,166 @@
1010
//===----------------------------------------------------------------------===//
1111

1212
extension _BTree: Sequence {
13+
@inlinable
14+
internal func forEach(_ body: (Element) throws -> Void) rethrows {
15+
func loop(node: Unmanaged<Node.Storage>) throws {
16+
try node._withUnsafeGuaranteedRef { storage in
17+
try storage.read { handle in
18+
for i in 0..<handle.elementCount {
19+
if !handle.isLeaf {
20+
try loop(node: .passUnretained(handle[childAt: i].storage))
21+
}
22+
23+
try body(handle[elementAt: i])
24+
}
25+
26+
if !handle.isLeaf {
27+
let lastChild: Unmanaged =
28+
.passUnretained(handle[childAt: handle.childCount - 1].storage)
29+
try loop(node: lastChild)
30+
}
31+
}
32+
}
33+
}
34+
35+
try loop(node: .passUnretained(self.root.storage))
36+
}
37+
1338
@usableFromInline
1439
internal struct Iterator: IteratorProtocol {
1540
@usableFromInline
1641
internal let tree: _BTree
1742

1843
@usableFromInline
19-
internal var offsets: Index.Offsets
20-
21-
@usableFromInline
22-
internal var parents: FixedSizeArray<Unmanaged<Node.Storage>>
44+
internal var slots: FixedSizeArray<Slot>
2345

2446
@usableFromInline
25-
internal var currentNode: Unmanaged<Node.Storage>
47+
internal var path: FixedSizeArray<Unmanaged<Node.Storage>>
2648

27-
@usableFromInline
28-
internal var slot: Int
49+
/// Creates an iterator to the element within a tree corresponding to a specific index
50+
@inlinable
51+
@inline(__always)
52+
internal init(forTree tree: _BTree, startingAt index: Index) {
53+
self.tree = tree
54+
self.slots = index.childSlots
55+
self.slots.append(UInt16(index.slot))
56+
57+
self.path = .init(repeating: .passUnretained(tree.root.storage))
58+
59+
if self.tree.isEmpty {
60+
return
61+
}
62+
63+
var node: Unmanaged = .passUnretained(tree.root.storage)
64+
for depth in 0..<index.childSlots.depth {
65+
let childSlot = index.childSlots[depth]
66+
self.path.append(node)
67+
68+
node._withUnsafeGuaranteedRef {
69+
$0.read { handle in
70+
node = .passUnretained(handle[childAt: Int(childSlot)].storage)
71+
}
72+
}
73+
}
74+
}
2975

76+
/// Creates an iterator to the first element within a tree.
3077
@inlinable
3178
@inline(__always)
32-
internal init(tree: _BTree) {
79+
internal init(forTree tree: _BTree) {
3380
self.tree = tree
3481

35-
self.offsets = .init(repeating: 0)
36-
self.parents = .init(repeating: .passUnretained(tree.root.storage))
82+
self.slots = .init(repeating: 0)
83+
self.path = .init(repeating: .passUnretained(tree.root.storage))
3784

3885
// Simple case for an empty tree
3986
if self.tree.isEmpty {
40-
self.currentNode = .passUnretained(tree.root.storage)
41-
self.slot = -1
4287
return
4388
}
4489

4590
// TODO: maybe convert to unowned(unsafe)
46-
var node = tree.root
47-
while !node.read({ $0.isLeaf }) {
48-
self.parents.append(Unmanaged.passUnretained(node.storage))
49-
self.offsets.append(0)
91+
var nextNode: Unmanaged? = .passUnretained(tree.root.storage)
92+
while let node = nextNode {
93+
self.path.append(node)
94+
self.slots.append(0)
5095

51-
node = node.read({ $0[childAt: 0] })
96+
node._withUnsafeGuaranteedRef {
97+
$0.read { handle in
98+
if handle.isLeaf {
99+
nextNode = nil
100+
} else {
101+
nextNode = .passUnretained(handle[childAt: 0].storage)
102+
}
103+
}
104+
}
52105
}
53-
54-
55-
self.currentNode = .passUnretained(node.storage)
56-
self.slot = 0
57106
}
58107

59108
@inlinable
60109
@inline(__always)
61110
internal mutating func next() -> Element? {
62111
// Check slot sentinel value for end of tree.
63-
if _slowPath(self.slot == -1) {
112+
if _slowPath(path.isEmpty) {
64113
return nil
65114
}
66115

67-
return self.currentNode._withUnsafeGuaranteedRef { storage in
116+
return path.last._withUnsafeGuaranteedRef { storage in
68117
storage.read({ handle in
69118
defer {
70119
// If we're not a leaf, descend to the next child
71120
if !handle.isLeaf {
72-
self.parents.append(self.currentNode)
73-
self.offsets.append(UInt16(self.slot + 1))
74-
75-
// TODO: make these descents Unmanaged
76-
var node = handle[childAt: self.slot + 1]
121+
// Go to the right child
122+
slots.last += 1
123+
var nextNode: Unmanaged? = .passUnretained(handle[childAt: Int(slots.last)].storage)
77124

78-
while !node.read({ $0.isLeaf }) {
79-
self.parents.append(.passUnretained(node.storage))
80-
self.offsets.append(0)
81-
node = node.read({ $0[childAt: 0] })
125+
while let node = nextNode {
126+
self.path.append(node)
127+
self.slots.append(0)
128+
129+
node._withUnsafeGuaranteedRef {
130+
$0.read { handle in
131+
if handle.isLeaf {
132+
nextNode = nil
133+
} else {
134+
nextNode = .passUnretained(handle[childAt: 0].storage)
135+
}
136+
}
137+
}
82138
}
83-
84-
self.currentNode = .passUnretained(node.storage)
85-
self.slot = 0
86139
} else {
87-
if self.slot < handle.elementCount - 1 {
88-
self.slot += 1
140+
if slots.last < handle.elementCount - 1 {
141+
slots.last += 1
89142
} else {
90-
while true {
91-
if self.parents.depth == 0 {
92-
self.slot = -1
93-
break
94-
}
143+
_ = path.pop()
144+
_ = slots.pop()
145+
146+
while !path.isEmpty {
147+
let parent = path.last
148+
let slot = slots.last
95149

96-
// If we are at a leaf, then ascend to the
97-
let parent = self.parents.pop()
98-
let offset = self.offsets.pop()
99-
100-
let parentElements = parent._withUnsafeGuaranteedRef { $0.read({ $0.elementCount }) }
150+
let parentElementCount = parent._withUnsafeGuaranteedRef {
151+
$0.read({ $0.elementCount })
152+
}
101153

102-
if offset < parentElements {
103-
self.currentNode = parent
104-
self.slot = Int(offset)
154+
if slot < parentElementCount {
105155
break
156+
} else {
157+
_ = path.pop()
158+
_ = slots.pop()
106159
}
107160
}
108161
}
109162
}
110163
}
111164

112-
return storage.read({ $0[elementAtSlot: self.slot] })
165+
return storage.read({ $0[elementAt: Int(slots.last)] })
113166
})
114167
}
115168
}
116169
}
117170

118171
@inlinable
119172
internal func makeIterator() -> Iterator {
120-
return Iterator(tree: self)
173+
return Iterator(forTree: self)
121174
}
122175
}

0 commit comments

Comments
 (0)