Skip to content

Commit 21d9bde

Browse files
committed
Add cursor tests and set iterator.
Major Changes: - Implement Sequence for SortedSet Minor Changes: - Fix bug within _Node.UnsafeHandle.pointerToChild(atSlot:) - Cleanup cursor implementation and fix bugs. - Fix various bugs in _BTree.FixedSizeArray
1 parent 5690056 commit 21d9bde

File tree

6 files changed

+178
-59
lines changed

6 files changed

+178
-59
lines changed

Sources/SortedCollections/BTree/_BTree+UnsafeCursor.swift

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,8 @@ extension _BTree {
4242
/// This property is what takes ownership of the tree during the lifetime of the cursor. Once the cursor
4343
/// is consumed, it is set to nil and it is invalid to use the cursor.
4444
@usableFromInline
45-
internal var __root: Node.Storage?
45+
internal var _root: Node.Storage?
4646

47-
@inlinable
48-
@inline(__always)
49-
internal var root: Node.Storage { __root.unsafelyUnwrapped }
5047

5148
/// Position of each of the parent nodes in their parents, including the bottom-most node.
5249
///
@@ -68,17 +65,6 @@ extension _BTree {
6865
internal var path: Path
6966

7067
/// Bottom most node that the index point to.
71-
// TODO: look at implementing _modify accessor.
72-
@usableFromInline
73-
internal var currentNode: Unmanaged<Node.Storage> {
74-
get { path[path.depth - 1] }
75-
set { path[path.depth - 1] = newValue }
76-
}
77-
78-
/// Slot within the bottom most node which the index points to.
79-
@inlinable
80-
@inline(__always)
81-
internal var slot: Int { Int(slots[slots.depth - 1]) }
8268

8369
/// The depth at which the last instance of sequential unique nodes starting at the root was found.
8470
///
@@ -106,60 +92,63 @@ extension _BTree {
10692
root: Node.Storage,
10793
slots: FixedSizeArray<_BTree.Slot>,
10894
path: Path,
109-
lastUniqueDepth: Int,
110-
// Debug-only properties
111-
isConcrete: Bool
95+
lastUniqueDepth: Int
11296
) {
11397
// Slots and path should be non-empty
11498
assert(slots.depth >= 1, "Invalid tree cursor.")
11599
assert(path.depth >= 1, "Invalid tree cursor.")
116100

117-
self.__root = root
101+
self._root = root
118102
self.slots = slots
119103
self.path = path
120104
self.lastUniqueDepth = lastUniqueDepth
121-
122-
#if COLLECTIONS_INTERNAL_CHECKS
123-
self.isConcrete = isConcrete
124-
#endif
125105
}
126106

127107
// MARK: Internal Checks
128-
#if COLLECTIONS_INTERNAL_CHECKS
129-
/// A concrete cursor is one that refers to a specific element, not just a generic position.
130-
@usableFromInline internal let isConcrete: Bool
131-
#endif
132-
133-
/// Check that this cursor is concrete
108+
/// Check that this cursor is still valid
109+
///
134110
/// Every member that operates on the element of the cursor must start by calling this function.
135111
///
136112
/// Note that this is a noop in release builds.
137113
@inlinable
138114
@inline(__always)
139-
internal func assertConcrete() {
115+
internal func assertValid() {
140116
#if COLLECTIONS_INTERNAL_CHECKS
141-
assert(self.isConcrete,
142-
"Attempt to operate on an element using a non-concrete cursor.")
117+
assert(self._root != nil,
118+
"Attempt to operate on an element using an invalid cursor.")
143119
#endif
144120
}
145121

146-
// MARK: Basic Cursor Operations
122+
// MARK: Core Cursor Operations
147123
/// Finishes operating on a cursor and restores a tree
148124
@inlinable
149125
@inline(__always)
150126
internal mutating func apply(to tree: inout _BTree) {
127+
assertValid()
151128
assert(tree.root._storage == nil, "Must apply to same tree as original.")
152-
swap(&tree.root._storage, &self.__root)
129+
swap(&tree.root._storage, &self._root)
130+
}
131+
132+
/// Declares that the cursor is completely unique
133+
@inlinable
134+
@inline(__always)
135+
internal mutating func _declareUnique() {
136+
self.lastUniqueDepth = Int(path.depth)
153137
}
154138

155139
/// Operators on a handle of the node
156140
/// - Warning: Ensure this is never called on an endIndex.
157141
@inlinable
158142
@inline(__always)
159143
internal func readCurrentNode<R>(
160-
_ body: (Node.UnsafeHandle) throws -> R
144+
_ body: (Node.UnsafeHandle, Int) throws -> R
161145
) rethrows -> R {
162-
return try currentNode._withUnsafeGuaranteedRef { try $0.read(body) }
146+
assertValid()
147+
148+
let slot = Int(slots[slots.depth - 1])
149+
return try path[path.depth - 1]._withUnsafeGuaranteedRef {
150+
try $0.read({ try body($0, slot) })
151+
}
163152
}
164153

165154
/// Updates the node at a given depth.
@@ -170,19 +159,26 @@ extension _BTree {
170159
at depth: Int8,
171160
_ body: (Node.UnsafeHandle, Int) throws -> R
172161
) rethrows -> (node: Node, result: R) {
173-
var node = Node(path[depth].takeUnretainedValue())
162+
assertValid()
163+
174164
let slot = Int(slots[depth])
175165
let isOnUniquePath = depth <= lastUniqueDepth
176-
let result = try node.update(isUnique: isOnUniquePath) {
177-
try body($0, slot)
166+
167+
return try path[depth]._withUnsafeGuaranteedRef { storage in
168+
if isOnUniquePath {
169+
let result = try storage.updateGuaranteedUnique({ try body($0, slot) })
170+
return (Node(storage), result)
171+
} else {
172+
let storage = storage.copy()
173+
path[depth] = .passUnretained(storage)
174+
let result = try storage.updateGuaranteedUnique({ try body($0, slot) })
175+
return (Node(storage), result)
176+
}
178177
}
179-
path[depth] = .passUnretained(node.storage)
180-
return (node, result)
181178
}
182179

183180

184-
// MARK: Mutating with the Cursor
185-
181+
// MARK: Mutations with the Cursor
186182
/// Operates on a handle of the node.
187183
///
188184
/// This MUST be followed by an operation which consumes the cursor and returns the new tree, as
@@ -197,7 +193,8 @@ extension _BTree {
197193
internal mutating func updateCurrentNode<R>(
198194
_ body: (Node.UnsafeHandle, Int) throws -> R
199195
) rethrows -> R {
200-
defer { self.lastUniqueDepth = .max }
196+
assertValid()
197+
defer { self._declareUnique() }
201198

202199
// Update the bottom-most node
203200
var (node, result) = try self.updateNode(at: path.depth - 1, body)
@@ -224,6 +221,8 @@ extension _BTree {
224221
_ = handle.exchangeChild(atSlot: slot, with: child)
225222
}
226223
}
224+
225+
return result
227226
} else {
228227
// depth < lastUniqueDepth
229228

@@ -235,7 +234,7 @@ extension _BTree {
235234
depth -= 1
236235
}
237236

238-
self.__root = node.storage
237+
self._root = node.storage
239238
return result
240239
}
241240

@@ -254,7 +253,8 @@ extension _BTree {
254253
_ element: Node.Element,
255254
capacity: Int
256255
) {
257-
defer { self.lastUniqueDepth = .max }
256+
assertValid()
257+
defer { self._declareUnique() }
258258

259259
// TODO: make this not invalidate the cursor by updating slots
260260

@@ -283,9 +283,9 @@ extension _BTree {
283283

284284
if let splinter = splinter {
285285
let newRoot = splinter.toNode(leftChild: node, capacity: capacity)
286-
self.__root = newRoot.storage
286+
self._root = newRoot.storage
287287
} else {
288-
self.__root = node.storage
288+
self._root = node.storage
289289
}
290290
}
291291

@@ -295,7 +295,8 @@ extension _BTree {
295295
/// - Complexity: O(`log n`). Ascends the tree once.
296296
@inlinable
297297
internal mutating func removeElement(hasValueHole: Bool = false) {
298-
defer { self.lastUniqueDepth = .max }
298+
assertValid()
299+
defer { self._declareUnique() }
299300

300301
var (node, _) = self.updateNode(
301302
at: path.depth - 1
@@ -337,6 +338,7 @@ extension _BTree {
337338
while depth >= 0 {
338339
var (newNode, _) = self.updateNode(at: depth) { (handle, slot) in
339340
handle.exchangeChild(atSlot: slot, with: node)
341+
handle.subtreeCount -= 1
340342
handle.balance(atSlot: slot)
341343
}
342344

@@ -349,7 +351,7 @@ extension _BTree {
349351
depth -= 1
350352
}
351353

352-
self.__root = node.storage
354+
self._root = node.storage
353355
}
354356
}
355357

@@ -375,7 +377,6 @@ extension _BTree {
375377
var parents =
376378
UnsafeCursor.Path(repeating: .passUnretained(self.root.storage))
377379

378-
// TODO: swap
379380
var ownedRoot: Node.Storage
380381
do {
381382
var tempRoot: Node.Storage? = nil
@@ -423,7 +424,7 @@ extension _BTree {
423424
if shouldStop { break }
424425
}
425426

426-
assert(slot != -1 && lastUniqueDepth != -1, "B-Tree sanity check fail.")
427+
assert(slot != -1, "B-Tree sanity check fail.")
427428

428429
parents.append(node)
429430
slots.append(UInt16(slot))
@@ -432,8 +433,7 @@ extension _BTree {
432433
root: ownedRoot,
433434
slots: slots,
434435
path: parents,
435-
lastUniqueDepth: lastUniqueDepth,
436-
isConcrete: found
436+
lastUniqueDepth: lastUniqueDepth
437437
)
438438

439439
return (cursor, found)

Sources/SortedCollections/BTree/_BTree.FixedSizeArray.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ extension _BTree {
6969
internal mutating func append(_ value: __owned Value) {
7070
assert(depth < FixedSizeArray.maxSize,
7171
"Out of bounds access in fixed sized array.")
72-
self.depth &+= 1
72+
defer { self.depth &+= 1 }
7373
self[self.depth] = value
7474
}
7575

@@ -79,16 +79,17 @@ extension _BTree {
7979
internal mutating func prepend(_ value: __owned Value) {
8080
assert(depth < FixedSizeArray.maxSize,
8181
"Out of bounds access in fixed sized array.")
82-
self.depth &+= 1
82+
defer { self.depth &+= 1 }
8383
self._shiftOffset(&self.start, by: -1)
84+
self[self.start] = value
8485
}
8586

8687
/// Pops a value from the end of offset list
8788
@inlinable
8889
@inline(__always)
8990
internal mutating func pop() -> Value {
9091
assert(depth > 0, "Cannot pop empty fixed sized array")
91-
defer { self.depth &-= 1 }
92+
self.depth &-= 1
9293
return self[self.depth]
9394
}
9495

Sources/SortedCollections/BTree/_Node.UnsafeHandle.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ extension _Node.UnsafeHandle {
222222
internal func pointerToChild(
223223
atSlot slot: Int
224224
) -> UnsafeMutablePointer<_Node> {
225-
assert(0 <= slot && slot < self.elementCount,
225+
assert(0 <= slot && slot < self.childCount,
226226
"Node child slot out of bounds.")
227227
assert(!isLeaf, "Cannot access children of leaf node.")
228228
return self.children.unsafelyUnwrapped.advanced(by: slot)

Sources/SortedCollections/SortedDictionary/SortedDictionary+Subscripts.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,37 @@ extension SortedDictionary {
9999
set {
100100
self[key] = newValue
101101
}
102+
103+
_modify {
104+
var (cursor, found) = self._root.findAnyCursor(forKey: key)
105+
106+
var value: Value
107+
if found {
108+
value = cursor.updateCurrentNode { handle, slot in
109+
handle.pointerToValue(atSlot: slot).move()
110+
}
111+
} else {
112+
value = defaultValue()
113+
}
114+
115+
defer {
116+
if found {
117+
cursor.updateCurrentNode { handle, slot in
118+
handle.pointerToValue(atSlot: slot).initialize(to: value)
119+
}
120+
} else {
121+
cursor.insertElement(
122+
(key, value),
123+
capacity: self._root.internalCapacity
124+
)
125+
}
126+
127+
cursor.apply(to: &self._root)
128+
}
129+
130+
yield &value
131+
}
132+
102133
}
103134

104135
/// Accesses the key-value pair at the specified position.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Collections open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
// TODO: add custom forEach?
13+
// TODO: benchmark
14+
15+
extension SortedSet: Sequence {
16+
/// An iterator over the elements of the sorted set
17+
public struct Iterator: IteratorProtocol {
18+
@usableFromInline
19+
internal var _iterator: _Tree.Iterator
20+
21+
@inlinable
22+
internal init(_base: SortedSet) {
23+
self._iterator = _base._root.makeIterator()
24+
}
25+
26+
@inlinable
27+
public mutating func next() -> Element? {
28+
return self._iterator.next()?.key
29+
}
30+
}
31+
32+
/// Returns an iterator over the elements of the sorted dictionary.
33+
///
34+
/// - Complexity: O(1)
35+
@inlinable
36+
public __consuming func makeIterator() -> Iterator {
37+
return Iterator(_base: self)
38+
}
39+
}

0 commit comments

Comments
 (0)