File: _HashNode.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (138 lines) | stat: -rw-r--r-- 4,581 bytes parent folder | download
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)
  }
}