Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ART: Adaptive Radix Tree implementation #307

Draft
wants to merge 67 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
8a54021
ART: Adaptive Radix Tree implementation
vishesh Aug 2, 2023
b6c1de1
Add license headers
vishesh Aug 4, 2023
66a6727
Use ManagedBuffer for underlying storage
vishesh Aug 18, 2023
5346d29
Fix test formatting
vishesh Aug 18, 2023
dd98a04
Add FixedArray type using tuples
vishesh Aug 18, 2023
3fd67ac
Fixed some test case formatting
vishesh Aug 18, 2023
21ebcb6
Make NodeLeaf follow ManagedBuffer semantics
vishesh Aug 18, 2023
579aabe
Remove Header pointer
vishesh Aug 18, 2023
8d3a75d
Initialize and bindMemory properly
vishesh Aug 18, 2023
f52d685
Update size test
vishesh Aug 18, 2023
0ed8b2b
Some refactor, cleanup and regression fixes
vishesh Aug 18, 2023
69ca513
Iterate empty tree
vishesh Aug 19, 2023
439c6a1
Remove some TODO's
vishesh Aug 19, 2023
91e2c7c
Move around and some cleanup
vishesh Aug 19, 2023
f9a5452
Fix deletion when node4/16 is full
vishesh Aug 19, 2023
4174087
Cleanup some NodeLeaf generic and some warnings
vishesh Aug 19, 2023
accbd63
Replace unsafe parent updating in delete functions
vishesh Aug 19, 2023
8ade1e4
Replace unsafe update of parent in insertion
vishesh Aug 19, 2023
8634916
Fix warnings
vishesh Aug 19, 2023
79e1966
Make insert work, but still using unsafe ChildSlotPtr
vishesh Aug 19, 2023
9d313d1
Removed unused `child(at: .., ref: ...)` methods
vishesh Aug 19, 2023
5d76b25
Don't use ChildSlotPtr out as InternalNode method params directly
vishesh Aug 19, 2023
1ce7b8f
Break ARTree.insert into two parts
vishesh Aug 20, 2023
4d5a9c9
Use Node.rawNode instead of constructor
vishesh Aug 20, 2023
dd52ccb
Add NodeReference type to avoid exposing pointer directly
vishesh Aug 20, 2023
784b788
Generalize Tree a bit more
vishesh Aug 21, 2023
e29d1da
Refactor ARTree into ARTreeImpl
vishesh Aug 21, 2023
2de02bd
Rename ChildSlotRef to RawNode.SlotRef
vishesh Aug 21, 2023
cdecdd1
Retain refcounts when expanding/shrinking to a new node
vishesh Aug 21, 2023
485f91a
Add clone() method to ManagedNodes
vishesh Aug 21, 2023
e95ea5f
Copy-on-write for inserts and delete
vishesh Aug 21, 2023
06255c0
Remove some warnings
vishesh Aug 21, 2023
32db022
Remove some redundant constructors
vishesh Aug 22, 2023
6cc2899
Use UnmanagedStorage for Node* functions
vishesh Aug 22, 2023
0953a32
Start adding tests for checking if we optimizing for unique reference…
vishesh Aug 23, 2023
da71164
Enforce no-copy for copy in unique tree/subtree inserts
vishesh Aug 23, 2023
0ff4d4c
Add optional printaddress to string description
vishesh Aug 23, 2023
30bbb36
Add some test cases for delete
vishesh Aug 23, 2023
6ab1b17
Start writing some documentation
vishesh Aug 23, 2023
5cdc9a5
Move things around
vishesh Aug 23, 2023
602b4c5
Check unique references for delete
vishesh Aug 24, 2023
3831595
Format ARTree
vishesh Aug 24, 2023
9e7c5f9
Rename some private variables
vishesh Aug 24, 2023
7046527
Add CMakeLists.txt
vishesh Aug 24, 2023
6e065aa
Add some more tests
vishesh Aug 24, 2023
17604fb
Add few test and fixed a bug when setting partial length
vishesh Aug 25, 2023
5fa86d5
Refactor to use getters
vishesh Aug 25, 2023
7641a5a
Implement replace when inserting
vishesh Aug 25, 2023
e6fc5b7
Add some refcounting tests
vishesh Aug 25, 2023
d0c680e
More testing
vishesh Aug 25, 2023
9f74116
Move ARTree into separate submodule
vishesh Sep 1, 2023
1490717
Add support for unsigned integers and RadixTree type
vishesh Sep 4, 2023
f7d27b1
Support signed integer
vishesh Sep 4, 2023
bd143f9
Reorganize some tests
vishesh Sep 4, 2023
a67393a
Reorganize some files
vishesh Sep 4, 2023
c9578a2
Remove old FixedArray implementation
vishesh Sep 4, 2023
27bfed1
Add README
vishesh Sep 4, 2023
6ecc661
Rename FixedStorage to FixedArrayStorage
vishesh Sep 4, 2023
1b74fbc
Supress some warnings
vishesh Sep 4, 2023
6ef8f08
Use collection test library
vishesh Sep 4, 2023
5b18390
change testRefCountBasic
vishesh Sep 4, 2023
79abfd9
Fix IntMapTest unique number generation
vishesh Sep 5, 2023
46f2054
Add String as BinaryComparableBytes
vishesh Sep 5, 2023
cf652bc
Add subscript and dictionary literal
vishesh Sep 5, 2023
562a42c
Some documentation
vishesh Sep 5, 2023
2613cb2
More documentation
vishesh Sep 6, 2023
1cf3b88
WIP on Collection protocol
vishesh Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ let targets: [CustomTarget] = [
name: "SortedCollectionsTests",
dependencies: ["SortedCollections", "_CollectionsTestSupport"]),

.target(
kind: .exported,
name: "ARTreeModule",
dependencies: ["_CollectionsUtilities"],
exclude: ["CMakeLists.txt"]),
.target(
kind: .test,
name: "ARTreeModuleTests",
dependencies: ["ARTreeModule", "_CollectionsTestSupport"]),

.target(
kind: .exported,
name: "Collections",
Expand Down
23 changes: 23 additions & 0 deletions Sources/ARTreeModule/ARTree/ARTree+Collection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===----------------------------------------------------------------------===//
//
// 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 ARTreeImpl {
var startIndex: Index {
var idx = Index(forTree: self)
idx.descentToLeftMostChild()
return idx
}

var endIndex: Index {
return Index(forTree: self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right?

}
}
82 changes: 82 additions & 0 deletions Sources/ARTreeModule/ARTree/ARTree+Sequence.swift
Original file line number Diff line number Diff line change
@@ -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
//
//===----------------------------------------------------------------------===//

@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *)
extension ARTreeImpl: Sequence {
public typealias Iterator = _Iterator

public struct _Iterator {
typealias _ChildIndex = InternalNode<Spec>.Index

private let tree: ARTreeImpl<Spec>
private var path: [(any InternalNode<Spec>, _ChildIndex)]

init(tree: ARTreeImpl<Spec>) {
self.tree = tree
self.path = []
guard let node = tree._root else { return }

assert(node.type != .leaf, "root can't be leaf")
let n: any InternalNode<Spec> = node.toInternalNode()
if n.count > 0 {
self.path = [(n, n.startIndex)]
}
}
}

public func makeIterator() -> Iterator {
return Iterator(tree: self)
}
}

// TODO: Instead of index, use node iterators, to advance to next child.
@available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *)
extension ARTreeImpl._Iterator: IteratorProtocol {
public typealias Element = (Key, Spec.Value) // TODO: Why just Value fails?

// Exhausted childs on the tip of path. Forward to sibling.
mutating private func advanceToSibling() {
let _ = path.popLast()
advanceToNextChild()
}

mutating private func advanceToNextChild() {
guard let (node, index) = path.popLast() else {
return
}

path.append((node, node.index(after: index)))
}

mutating func next() -> Element? {
while !path.isEmpty {
while let (node, index) = path.last {
if index == node.endIndex {
advanceToSibling()
break
}

let next = node.child(at: index)!
if next.type == .leaf {
let leaf: NodeLeaf<Spec> = next.toLeafNode()
let result = (leaf.key, leaf.value)
advanceToNextChild()
return result
}

let nextNode: any InternalNode<Spec> = next.toInternalNode()
path.append((nextNode, nextNode.startIndex))
}
}

return nil
}
}
64 changes: 64 additions & 0 deletions Sources/ARTreeModule/ARTree/ARTree+delete.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// 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 ARTreeImpl {
public mutating func delete(key: Key) {
if _root == nil {
return
}

let isUnique = _root!.isUnique
var child = _root
switch _delete(child: &child, key: key, depth: 0, isUniquePath: isUnique) {
case .noop:
return
case .replaceWith(let newValue):
_root = newValue
}
}

public mutating func deleteRange(start: Key, end: Key) {
// TODO
fatalError("not implemented")
}

private mutating func _delete(
child: inout RawNode?,
key: Key,
depth: Int,
isUniquePath: Bool
) -> UpdateResult<RawNode?> {
if child?.type == .leaf {
let leaf: NodeLeaf<Spec> = child!.toLeafNode()
if !leaf.keyEquals(with: key, depth: depth) {
return .noop
}

return .replaceWith(nil)
}

assert(!Const.testCheckUnique || isUniquePath, "unique path is expected in this test")
var node: any InternalNode<Spec> = child!.toInternalNode()
var newDepth = depth

if node.partialLength > 0 {
let matchedBytes = node.prefixMismatch(withKey: key, fromIndex: depth)
assert(matchedBytes <= node.partialLength)
newDepth += matchedBytes
}

return node.updateChild(forKey: key[newDepth], isUniquePath: isUniquePath) {
var child = $0
return _delete(child: &child, key: key, depth: newDepth + 1, isUniquePath: $1)
}
}
}
50 changes: 50 additions & 0 deletions Sources/ARTreeModule/ARTree/ARTree+get.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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 ARTreeImpl {
public func getValue(key: Key) -> Value? {
var current = _root
var depth = 0
while depth <= key.count {
guard let _rawNode = current else {
return nil
}

if _rawNode.type == .leaf {
let leaf: NodeLeaf<Spec> = _rawNode.toLeafNode()
return leaf.keyEquals(with: key)
? leaf.value
: nil
}

let node: any InternalNode<Spec> = _rawNode.toInternalNode()
if node.partialLength > 0 {
let prefixLen = node.prefixMismatch(withKey: key, fromIndex: depth)
assert(prefixLen <= Const.maxPartialLength, "partial length is always bounded")
if prefixLen != node.partialLength {
return nil
}
depth = depth + node.partialLength
}

current = node.child(forKey: key[depth])
depth += 1
}

return nil
}

public mutating func getRange(start: Key, end: Key) {
// TODO
fatalError("not implemented")
}
}