1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2022 - 2024 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
//
//===----------------------------------------------------------------------===//
/// A node in the hash tree, logically representing a hash table with
/// 32 buckets, corresponding to a 5-bit slice of a full hash value.
///
/// Each bucket may store either a single key-value pair or a reference
/// to a child node that contains additional items.
///
/// To save space, children and items are stored compressed into the two
/// ends of a single raw storage buffer, with the free space in between
/// available for use by either side.
///
/// The storage is arranged as shown below.
///
/// ┌───┬───┬───┬───┬───┬──────────────────┬───┬───┬───┬───┐
/// │ 0 │ 1 │ 2 │ 3 │ 4 │→ free space ←│ 3 │ 2 │ 1 │ 0 │
/// └───┴───┴───┴───┴───┴──────────────────┴───┴───┴───┴───┘
/// children items
///
/// Note that the region for items grows downwards from the end, so the item
/// at slot 0 is at the very end of the buffer.
///
/// Two 32-bit bitmaps are used to associate each child and item with their
/// position in the hash table. The bucket occupied by the *n*th child in the
/// buffer above is identified by position of the *n*th true bit in the child
/// map, and the *n*th item's bucket corresponds to the *n*th true bit in the
/// items map.
@usableFromInline
@frozen
internal struct _HashNode<Key: Hashable, Value> {
// Warning: This struct must be kept layout-compatible with _RawHashNode.
// Do not add any new stored properties to this type.
//
// Swift guarantees that frozen structs with a single stored property will
// be layout-compatible with the type they are wrapping.
//
// (_RawHashNode is used as the Element type of the ManagedBuffer underlying
// node storage, and the memory is then rebound to `_HashNode` later.
// This will not work correctly unless `_HashNode` has the exact same alignment
// and stride as `RawNode`.)
@usableFromInline
internal typealias Element = (key: Key, value: Value)
@usableFromInline
internal var raw: _RawHashNode
@inlinable
internal init(storage: _RawHashStorage, count: Int) {
self.raw = _RawHashNode(storage: storage, count: count)
}
}
extension _HashNode {
@inlinable @inline(__always)
internal var count: Int {
get { raw.count }
set { raw.count = newValue }
}
}
extension _HashNode {
@inlinable @inline(__always)
internal var unmanaged: _UnmanagedHashNode {
_UnmanagedHashNode(raw.storage)
}
@inlinable @inline(__always)
internal func isIdentical(to other: _UnmanagedHashNode) -> Bool {
raw.isIdentical(to: other)
}
}
extension _HashNode {
@inlinable @inline(__always)
internal func read<R>(
_ body: (UnsafeHandle) throws -> R
) rethrows -> R {
try UnsafeHandle.read(raw.storage, body)
}
@inlinable @inline(__always)
internal mutating func update<R>(
_ body: (UnsafeHandle) throws -> R
) rethrows -> R {
try UnsafeHandle.update(raw.storage, body)
}
}
// MARK: Shortcuts to reading header data
extension _HashNode {
@inlinable
internal var isCollisionNode: Bool {
read { $0.isCollisionNode }
}
@inlinable
internal var collisionHash: _Hash {
read { $0.collisionHash }
}
@inlinable
internal var hasSingletonItem: Bool {
read { $0.hasSingletonItem }
}
@inlinable
internal var hasSingletonChild: Bool {
read { $0.hasSingletonChild }
}
@inlinable
internal var isAtrophied: Bool {
read { $0.isAtrophiedNode }
}
}
extension _HashNode {
@inlinable
internal var initialVersionNumber: UInt {
// Ideally we would simply just generate a true random number, but the
// memory address of the root node is a reasonable substitute.
// Alternatively, we could use a per-thread counter along with a thread
// id, or some sort of global banks of atomic integer counters.
let address = Unmanaged.passUnretained(raw.storage).toOpaque()
return UInt(bitPattern: address)
}
}
|