|
10 | 10 | //===----------------------------------------------------------------------===//
|
11 | 11 |
|
12 | 12 | 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 | + |
13 | 38 | @usableFromInline
|
14 | 39 | internal struct Iterator: IteratorProtocol {
|
15 | 40 | @usableFromInline
|
16 | 41 | internal let tree: _BTree
|
17 | 42 |
|
18 | 43 | @usableFromInline
|
19 |
| - internal var offsets: Index.Offsets |
20 |
| - |
21 |
| - @usableFromInline |
22 |
| - internal var parents: FixedSizeArray<Unmanaged<Node.Storage>> |
| 44 | + internal var slots: FixedSizeArray<Slot> |
23 | 45 |
|
24 | 46 | @usableFromInline
|
25 |
| - internal var currentNode: Unmanaged<Node.Storage> |
| 47 | + internal var path: FixedSizeArray<Unmanaged<Node.Storage>> |
26 | 48 |
|
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 | + } |
29 | 75 |
|
| 76 | + /// Creates an iterator to the first element within a tree. |
30 | 77 | @inlinable
|
31 | 78 | @inline(__always)
|
32 |
| - internal init(tree: _BTree) { |
| 79 | + internal init(forTree tree: _BTree) { |
33 | 80 | self.tree = tree
|
34 | 81 |
|
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)) |
37 | 84 |
|
38 | 85 | // Simple case for an empty tree
|
39 | 86 | if self.tree.isEmpty {
|
40 |
| - self.currentNode = .passUnretained(tree.root.storage) |
41 |
| - self.slot = -1 |
42 | 87 | return
|
43 | 88 | }
|
44 | 89 |
|
45 | 90 | // 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) |
50 | 95 |
|
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 | + } |
52 | 105 | }
|
53 |
| - |
54 |
| - |
55 |
| - self.currentNode = .passUnretained(node.storage) |
56 |
| - self.slot = 0 |
57 | 106 | }
|
58 | 107 |
|
59 | 108 | @inlinable
|
60 | 109 | @inline(__always)
|
61 | 110 | internal mutating func next() -> Element? {
|
62 | 111 | // Check slot sentinel value for end of tree.
|
63 |
| - if _slowPath(self.slot == -1) { |
| 112 | + if _slowPath(path.isEmpty) { |
64 | 113 | return nil
|
65 | 114 | }
|
66 | 115 |
|
67 |
| - return self.currentNode._withUnsafeGuaranteedRef { storage in |
| 116 | + return path.last._withUnsafeGuaranteedRef { storage in |
68 | 117 | storage.read({ handle in
|
69 | 118 | defer {
|
70 | 119 | // If we're not a leaf, descend to the next child
|
71 | 120 | 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) |
77 | 124 |
|
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 | + } |
82 | 138 | }
|
83 |
| - |
84 |
| - self.currentNode = .passUnretained(node.storage) |
85 |
| - self.slot = 0 |
86 | 139 | } else {
|
87 |
| - if self.slot < handle.elementCount - 1 { |
88 |
| - self.slot += 1 |
| 140 | + if slots.last < handle.elementCount - 1 { |
| 141 | + slots.last += 1 |
89 | 142 | } 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 |
95 | 149 |
|
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 | + } |
101 | 153 |
|
102 |
| - if offset < parentElements { |
103 |
| - self.currentNode = parent |
104 |
| - self.slot = Int(offset) |
| 154 | + if slot < parentElementCount { |
105 | 155 | break
|
| 156 | + } else { |
| 157 | + _ = path.pop() |
| 158 | + _ = slots.pop() |
106 | 159 | }
|
107 | 160 | }
|
108 | 161 | }
|
109 | 162 | }
|
110 | 163 | }
|
111 | 164 |
|
112 |
| - return storage.read({ $0[elementAtSlot: self.slot] }) |
| 165 | + return storage.read({ $0[elementAt: Int(slots.last)] }) |
113 | 166 | })
|
114 | 167 | }
|
115 | 168 | }
|
116 | 169 | }
|
117 | 170 |
|
118 | 171 | @inlinable
|
119 | 172 | internal func makeIterator() -> Iterator {
|
120 |
| - return Iterator(tree: self) |
| 173 | + return Iterator(forTree: self) |
121 | 174 | }
|
122 | 175 | }
|
0 commit comments