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

Persistent collections updates (part 4) #177

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ let package = Package(

.target(
name: "_CollectionsUtilities",
exclude: ["CMakeLists.txt"],
swiftSettings: settings),

// Deque<Element>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,25 @@ extension _Bitmap: Equatable {
}
}

extension _Bitmap: CustomStringConvertible {
@usableFromInline
internal var description: String {
let b = String(_value, radix: 2)
let bits = String(repeating: "0", count: _Bitmap.capacity - b.count) + b
return "\(String(bits.reversed())) (\(_value))"
}
}

extension _Bitmap {
@inlinable @inline(__always)
internal static var empty: Self { .init(_value: 0) }

@inlinable @inline(__always)
internal static var capacity: Int { Value.bitWidth }

@inlinable @inline(__always)
internal static var bitWidth: Int { capacity.trailingZeroBitCount }

@inlinable @inline(__always)
internal var count: Int { _value.nonzeroBitCount }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ internal struct _Bucket {
internal var value: UInt

@inlinable @inline(__always)
internal init(_ value: UInt) { self.value = value }
internal init(_ value: UInt) {
assert(value < _Bitmap.capacity || value == .max)
self.value = value
}
}

extension _Bucket {
@inlinable @inline(__always)
static var bitWidth: Int { _Bitmap.capacity.trailingZeroBitCount }

Expand All @@ -28,6 +33,9 @@ internal struct _Bucket {

@inlinable @inline(__always)
static var invalid: _Bucket { _Bucket(.max) }

@inlinable @inline(__always)
var isInvalid: Bool { value == .max }
}

extension _Bucket: Equatable {
Expand All @@ -43,3 +51,10 @@ extension _Bucket: Comparable {
left.value < right.value
}
}

extension _Bucket: CustomStringConvertible {
@usableFromInline
internal var description: String {
String(value, radix: _Bitmap.capacity)
}
}
89 changes: 89 additions & 0 deletions Sources/PersistentCollections/Node/_Hash.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2022 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
//
//===----------------------------------------------------------------------===//

/// An abstract representation of a hash value.
@usableFromInline
@frozen
internal struct _Hash {
@usableFromInline
internal var value: UInt

@inlinable
internal init<Key: Hashable>(_ key: Key) {
let hashValue = key._rawHashValue(seed: 0)
self.value = UInt(bitPattern: hashValue)
}

@inlinable
internal init(_value: UInt) {
self.value = _value
}
}

extension _Hash: Equatable {
@inlinable @inline(__always)
internal static func ==(left: Self, right: Self) -> Bool {
left.value == right.value
}
}

extension _Hash: CustomStringConvertible {
@usableFromInline
internal var description: String {
// Print hash values in radix 32 & reversed, so that the path in the hash
// tree is readily visible.
let p = String(value, radix: _Bitmap.capacity, uppercase: true)
let c = _Level.limit
let path = String(repeating: "0", count: Swift.max(0, c - p.count)) + p
return String(path.reversed())
}
}


extension _Hash {
@inlinable @inline(__always)
internal static var bitWidth: Int { UInt.bitWidth }
}

extension _Hash {
@inlinable
internal subscript(_ level: _Level) -> _Bucket {
get {
assert(!level.isAtBottom)
return _Bucket((value &>> level.shift) & _Bucket.bitMask)
}
set {
let mask = _Bucket.bitMask &<< level.shift
self.value &= ~mask
self.value |= newValue.value &<< level.shift
}
}
}

extension _Hash {
@inlinable
internal func appending(_ bucket: _Bucket, at level: _Level) -> Self {
assert(value >> level.shift == 0)
var copy = self
copy[level] = bucket
return copy
}

@inlinable
internal func isEqual(to other: _Hash, upTo level: _Level) -> Bool {
if level.isAtRoot { return true }
if level.isAtBottom { return self == other }
let s = UInt(UInt.bitWidth) - level.shift
let v1 = self.value &<< s
let v2 = self.value &<< s
return v1 == v2
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,74 +9,55 @@
//
//===----------------------------------------------------------------------===//

/// An abstract representation of a hash value.
@usableFromInline
@frozen
internal struct _HashValue {
@usableFromInline
internal var value: UInt

@inlinable
internal init<Key: Hashable>(_ key: Key) {
let hashValue = key._rawHashValue(seed: 0)
self.value = UInt(bitPattern: hashValue)
}
}

extension _HashValue: Equatable {
@inlinable @inline(__always)
internal static func ==(left: Self, right: Self) -> Bool {
left.value == right.value
}
}

extension _HashValue {
@inlinable
internal subscript(_ level: _Level) -> _Bucket {
assert(!level.isAtBottom)
return _Bucket((value &>> level.shift) & _Bucket.bitMask)
}
}

@usableFromInline
@frozen
internal struct _Level {
@usableFromInline
internal var shift: UInt

@inlinable
@inlinable @inline(__always)
init(shift: UInt) {
self.shift = shift
}
}

extension _Level {
@inlinable
@inlinable @inline(__always)
internal static var limit: Int {
(_Hash.bitWidth + _Bitmap.bitWidth - 1) / _Bitmap.bitWidth
}

@inlinable @inline(__always)
internal static var _step: UInt {
UInt(bitPattern: _Bitmap.bitWidth)
}

@inlinable @inline(__always)
internal static var top: _Level {
_Level(shift: 0)
}

@inlinable
@inlinable @inline(__always)
internal var isAtRoot: Bool { shift == 0 }

@inlinable
@inlinable @inline(__always)
internal var isAtBottom: Bool { shift >= UInt.bitWidth }

@inlinable
@inlinable @inline(__always)
internal func descend() -> _Level {
// FIXME: Consider returning nil when we run out of bits
_Level(shift: shift &+ UInt(bitPattern: _Bucket.bitWidth))
_Level(shift: shift &+ Self._step)
}

@inlinable
@inlinable @inline(__always)
internal func ascend() -> _Level {
assert(!isAtRoot)
return _Level(shift: shift &+ UInt(bitPattern: _Bucket.bitWidth))
return _Level(shift: shift &+ Self._step)
}
}

extension _Level: Equatable {
@inlinable
@inlinable @inline(__always)
internal static func ==(left: Self, right: Self) -> Bool {
left.shift == right.shift
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
//===----------------------------------------------------------------------===//

extension PersistentDictionary._Node: CustomStringConvertible {
extension _Node: CustomStringConvertible {
@usableFromInline
internal var description: String {
guard count > 0 else {
Expand All @@ -18,21 +18,23 @@ extension PersistentDictionary._Node: CustomStringConvertible {

var result = "["
var first = true
for (key, value) in _items {
if first {
first = false
} else {
result += ", "
read {
for (key, value) in $0._items {
if first {
first = false
} else {
result += ", "
}
result += "\(key): \(value)"
}
result += "\(key): \(value)"
}
for node in _children {
if first {
first = false
} else {
result += ", "
for child in $0._children {
if first {
first = false
} else {
result += ", "
}
result += "\(child.description)"
}
result += "\(node.description)"
}
result += "]"
return result
Expand Down
82 changes: 82 additions & 0 deletions Sources/PersistentCollections/Node/_Node+Debugging.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) 2022 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
//
//===----------------------------------------------------------------------===//

import _CollectionsUtilities

extension _Node {
@usableFromInline
internal func dump(
firstPrefix: String = "", restPrefix: String = "", limit: Int = Int.max
) {
read {
$0.dump(
firstPrefix: firstPrefix,
restPrefix: restPrefix,
extra: "count: \(count), ",
limit: limit)
}
}
}

extension _Node.Storage {
@usableFromInline
final internal func dump() {
UnsafeHandle.read(self) { $0.dump() }
}
}

extension _Node.UnsafeHandle {
@usableFromInline
internal func dump(
firstPrefix: String = "",
restPrefix: String = "",
extra: String = "",
limit: Int = Int.max
) {
print("""
\(firstPrefix)\(isCollisionNode ? "CollisionNode" : "Node")(\
at: \(_addressString(for: _header)), \
\(extra)\
byteCapacity: \(byteCapacity), \
freeBytes: \(bytesFree))
""")
guard limit > 0 else { return }
if isCollisionNode {
let items = self._items
for offset in items.indices {
let item = items[offset]
let hash = _Hash(item.key).description
let itemStr = "hash: \(hash), key: \(item.key), value: \(item.value)"
print("\(restPrefix) \(offset): \(itemStr)")
}
} else {
var itemOffset = 0
var childOffset = 0
for b in 0 ..< UInt(_Bitmap.capacity) {
let bucket = _Bucket(b)
let bucketStr = "#\(String(b, radix: _Bitmap.capacity, uppercase: true))"
if itemMap.contains(bucket) {
let item = self[item: itemOffset]
let hash = _Hash(item.key).description
let itemStr = "hash: \(hash), key: \(item.key), value: \(item.value)"
print("\(restPrefix) \(bucketStr) \(itemStr)")
itemOffset += 1
} else if childMap.contains(bucket) {
self[child: childOffset].dump(
firstPrefix: "\(restPrefix) \(bucketStr) ",
restPrefix: "\(restPrefix) ",
limit: limit - 1)
childOffset += 1
}
}
}
}
}
Loading