99//
1010//===----------------------------------------------------------------------===//
1111
12+ // This contains `_BTree`'s general implementation of BidirectionalCollection.
13+ // These operations are bounds in contrast to most other methods on _BTree as
14+ // they are designed to be easily propogated to a higher-level data type.
15+ // However, they still do not perform index validation
16+
1217extension _BTree : BidirectionalCollection {
1318 /// The total number of elements contained within the BTree
1419 /// - Complexity: O(1)
@@ -26,36 +31,45 @@ extension _BTree: BidirectionalCollection {
2631 /// - Complexity: O(`log n`)
2732 @inlinable
2833 internal var startIndex : Index {
29- if count == 0 { return Index ( nil , forTree: self ) }
30- var depth : UInt8 = 0
31- var currentNode = self . root
32- while !currentNode. read ( { $0. isLeaf } ) {
33- // TODO: figure out how to avoid the swift retain here
34- currentNode = currentNode. read ( { $0 [ childAt: 0 ] } )
35- depth += 1
34+ if count == 0 { return endIndex }
35+ var depth : Int8 = 0
36+ var currentNode : Unmanaged = . passUnretained( self . root. storage)
37+ while true {
38+ let shouldStop : Bool = currentNode. _withUnsafeGuaranteedRef {
39+ $0. read { handle in
40+ if handle. isLeaf {
41+ return true
42+ } else {
43+ depth += 1
44+ currentNode = . passUnretained( handle [ childAt: 0 ] . storage)
45+ return false
46+ }
47+ }
48+ }
49+
50+ if shouldStop { break }
3651 }
3752
38- let path = UnsafePath (
53+ return Index (
3954 node: currentNode,
4055 slot: 0 ,
4156 childSlots: FixedSizeArray ( repeating: 0 , depth: depth) ,
42- offset: 0
57+ offset: 0 ,
58+ forTree: self
4359 )
44-
45- return Index ( path, forTree: self )
4660 }
4761
4862 /// Returns a sentinel value for the last element
4963 /// - Complexity: O(1)
5064 @inlinable
51- internal var endIndex : Index { Index ( nil , forTree : self ) }
52-
53- /// Gets the effective offset of an index
54- /// - Warning: this does not
55- @ inlinable
56- @ inline ( __always )
57- internal func offset ( of index : Index ) -> Int {
58- return index . path ? . offset ?? self . count
65+ internal var endIndex : Index {
66+ Index (
67+ node : . passUnretained ( self . root . storage ) ,
68+ slot : - 1 ,
69+ childSlots : Index . Offsets ( repeating : 0 ) ,
70+ offset : self . count ,
71+ forTree : self
72+ )
5973 }
6074
6175 /// Returns the distance between two indices.
@@ -67,27 +81,25 @@ extension _BTree: BidirectionalCollection {
6781 /// - Complexity: O(1)
6882 @inlinable
6983 internal func distance( from start: Index , to end: Index ) -> Int {
70- return self . offset ( of : end ) - self . offset ( of : start )
84+ return end . offset - start . offset
7185 }
7286
7387 /// Replaces the given index with its successor.
7488 /// - Parameter index: A valid index of the collection. i must be less than endIndex.
7589 /// - Complexity: O(`log n`) in the worst-case.
7690 @inlinable
7791 internal func formIndex( after index: inout Index ) {
78- guard var path = index. path else {
79- preconditionFailure ( " Attempt to advance out of collection bounds. " )
80- }
92+ precondition ( index. offset < self . count,
93+ " Attempt to advance out of collection bounds. " )
8194
82- let shouldSeekWithinLeaf = path . readNode {
83- $0. isLeaf && _fastPath ( path . slot + 1 < $0. elementCount)
95+ let shouldSeekWithinLeaf = index . readNode {
96+ $0. isLeaf && _fastPath ( index . slot + 1 < $0. elementCount)
8497 }
8598
8699 if shouldSeekWithinLeaf {
87100 // Continue searching within the same leaf
88- path. slot += 1
89- path. offset += 1
90- index. path = path
101+ index. slot += 1
102+ index. offset += 1
91103 } else {
92104 self . formIndex ( & index, offsetBy: 1 )
93105 }
@@ -109,7 +121,8 @@ extension _BTree: BidirectionalCollection {
109121 /// - Complexity: O(`log n`) in the worst-case.
110122 @inlinable
111123 internal func formIndex( before index: inout Index ) {
112- assert ( !self . isEmpty && self . offset ( of: index) != 0 , " Attempt to advance out of collection bounds. " )
124+ precondition ( !self . isEmpty && index. offset != 0 ,
125+ " Attempt to advance out of collection bounds. " )
113126 // TODO: implement more efficient logic to better move through the tree
114127 self . formIndex ( & index, offsetBy: - 1 )
115128 }
@@ -135,29 +148,29 @@ extension _BTree: BidirectionalCollection {
135148 /// - Complexity: O(`log n`) in the worst-case.
136149 @inlinable
137150 internal func formIndex( _ i: inout Index , offsetBy distance: Int ) {
138- let newIndex = self . offset ( of: i) + distance
139- assert ( 0 <= newIndex && newIndex <= self . count, " Attempt to advance out of collection bounds. " )
151+ let newIndex = i. offset + distance
152+ precondition ( 0 <= newIndex && newIndex <= self . count,
153+ " Attempt to advance out of collection bounds. " )
140154
141155 if newIndex == self . count {
142- i. path = nil
156+ i = endIndex
143157 return
144158 }
145159
146160 // TODO: optimization for searching within children
147161
148- if var path = i . path , path . readNode ( { $0. isLeaf } ) {
162+ if i != endIndex && i . readNode ( { $0. isLeaf } ) {
149163 // Check if the target element will be in the same node
150- let targetSlot = path. slot + distance
151- if 0 <= targetSlot && targetSlot < path. readNode ( { $0. elementCount } ) {
152- path. slot = targetSlot
153- path. offset = newIndex
154- i. path = path
164+ let targetSlot = i. slot + distance
165+ if 0 <= targetSlot && targetSlot < i. readNode ( { $0. elementCount } ) {
166+ i. slot = targetSlot
167+ i. offset = newIndex
155168 return
156169 }
157170 }
158171
159172 // Otherwise, re-seek
160- i = Index ( self . path ( atOffset: newIndex) , forTree : self )
173+ i = self . index ( atOffset: newIndex)
161174 }
162175
163176 /// Returns an index that is the specified distance from the given index.
@@ -175,177 +188,11 @@ extension _BTree: BidirectionalCollection {
175188 return newIndex
176189 }
177190
178- /// Offsets the given index by the specified distance, or so that it equals the given limiting index.
179- ///
180- /// - Parameters:
181- /// - i: A valid index of the collection.
182- /// - distance: The distance to offset `i`.
183- /// - limit: A valid index of the collection to use as a limit. If `distance > 0`, a limit that is
184- /// less than `i` has no effect. Likewise, if `distance < 0`, a limit that is greater than `i`
185- /// has no effect.
186- /// - Returns: `true` if `i` has been offset by exactly `distance` steps without going beyond
187- /// `limit`; otherwise, `false`. When the return value is `false`, the value of `i` is equal
188- /// to `limit`.
189- /// - Complexity: O(`log n`) in the worst-case.
190- @inlinable
191- internal func formIndex( _ i: inout Index , offsetBy distance: Int , limitedBy limit: Index ) -> Bool {
192- let distanceToLimit = self . distance ( from: i, to: limit)
193- if distance < 0 ? distanceToLimit > distance : distanceToLimit < distance {
194- self . formIndex ( & i, offsetBy: distanceToLimit)
195- return false
196- } else {
197- self . formIndex ( & i, offsetBy: distance)
198- return true
199- }
200- }
201-
202- /// Returns an index that is the specified distance from the given index, unless that distance
203- /// is beyond a given limiting index.
204- ///
205- /// - Parameters:
206- /// - i: A valid index of the collection.
207- /// - distance: The distance to offset `i`.
208- /// - limit: A valid index of the collection to use as a limit. If `distance > 0`, a `limit`
209- /// that is less than `i` has no effect. Likewise, if `distance < 0`, a `limit` that is
210- /// greater twhan `i` has no effect.
211- /// - Returns: An index offset by `distance` from the index `i`, unless that index would
212- /// be beyond `limit` in the direction of movement. In that case, the method returns `nil`.
213- @inlinable
214- internal func index( _ i: Index , offsetBy distance: Int , limitedBy limit: Index ) -> Index ? {
215- let distanceToLimit = self . distance ( from: i, to: limit)
216- if distance < 0 ? distanceToLimit > distance : distanceToLimit < distance {
217- return nil
218- } else {
219- var newIndex = i
220- self . formIndex ( & newIndex, offsetBy: distance)
221- return newIndex
222- }
223- }
224-
225191 @inlinable
226192 @inline ( __always)
227193 internal subscript( index: Index ) -> Element {
228- assert ( index. path != nil , " Attempt to subscript out of range index. " )
229- return index. path. unsafelyUnwrapped. element
230- }
231- }
232-
233- // MARK: Custom Indexing Operations
234- extension _BTree {
235- /// Returns a path to the key at absolute offset `i`.
236- /// - Parameter offset: 0-indexed offset within BTree bounds, else may panic.
237- /// - Returns: the index of the appropriate element.
238- /// - Complexity: O(`log n`)
239-
240-
241- /// Obtains the start index for a key (or where it would exist).
242- @inlinable
243- internal func startIndex( forKey key: Key ) -> Index {
244- var childSlots = UnsafePath . Offsets ( repeating: 0 )
245- var targetSlot : Int = 0
246- var offset = 0
247-
248- func search( in node: Node ) -> Unmanaged < Node . Storage > ? {
249- node. read ( { handle in
250- let slot = handle. startSlot ( forKey: key)
251- if slot < handle. elementCount {
252- if handle. isLeaf {
253- offset += slot
254- targetSlot = slot
255- return . passUnretained( node. storage)
256- } else {
257- // Calculate offset by summing previous subtrees
258- for i in 0 ... slot {
259- offset += handle [ childAt: i] . read ( { $0. subtreeCount } )
260- }
261-
262- let currentOffset = offset
263- let currentDepth = childSlots. depth
264- childSlots. append ( UInt16 ( slot) )
265-
266- if let foundEarlier = search ( in: handle [ childAt: slot] ) {
267- return foundEarlier
268- } else {
269- childSlots. depth = currentDepth
270- targetSlot = slot
271- offset = currentOffset
272-
273- return . passUnretained( node. storage)
274- }
275- }
276- } else {
277- // Start index exceeds node and is therefore not in this.
278- return nil
279- }
280- } )
281- }
282-
283- if let targetChild = search ( in: self . root) {
284- return Index ( UnsafePath (
285- node: targetChild,
286- slot: targetSlot,
287- childSlots: childSlots,
288- offset: offset
289- ) , forTree: self )
290- } else {
291- return Index ( nil , forTree: self )
292- }
194+ // Ensure we don't attempt to dereference the endIndex
195+ precondition ( index != endIndex, " Attempt to subscript out of range index. " )
196+ return index. element
293197 }
294-
295- /// Obtains the last index at which a value less than or equal to the key appears.
296- @inlinable
297- internal func lastIndex( forKey key: Key ) -> Index {
298- var childSlots = UnsafePath . Offsets ( repeating: 0 )
299- var targetSlot : Int = 0
300- var offset = 0
301-
302- func search( in node: Node ) -> Unmanaged < Node . Storage > ? {
303- node. read ( { handle in
304- let slot = handle. endSlot ( forKey: key) - 1
305- if slot > 0 {
306- // Sanity Check
307- assert ( slot < handle. elementCount, " Slot out of bounds. " )
308-
309- if handle. isLeaf {
310- offset += slot
311- targetSlot = slot
312- return . passUnretained( node. storage)
313- } else {
314- for i in 0 ... slot {
315- offset += handle [ childAt: i] . read ( { $0. subtreeCount } )
316- }
317-
318- let currentOffset = offset
319- let currentDepth = childSlots. depth
320- childSlots. append ( UInt16 ( slot + 1 ) )
321-
322- if let foundLater = search ( in: handle [ childAt: slot + 1 ] ) {
323- return foundLater
324- } else {
325- childSlots. depth = currentDepth
326- targetSlot = slot
327- offset = currentOffset
328-
329- return . passUnretained( node. storage)
330- }
331- }
332- } else {
333- // Start index exceeds node and is therefore not in this.
334- return nil
335- }
336- } )
337- }
338-
339- if let targetChild = search ( in: self . root) {
340- return Index ( UnsafePath (
341- node: targetChild,
342- slot: targetSlot,
343- childSlots: childSlots,
344- offset: offset
345- ) , forTree: self )
346- } else {
347- return Index ( nil , forTree: self )
348- }
349- }
350-
351198}
0 commit comments