22
22
public struct __ExpressionID : Sendable {
23
23
/// The ID of the root node in an expression graph.
24
24
static var root : Self {
25
- Self ( _elements : . none)
25
+ Self ( elements : . none)
26
26
}
27
27
28
28
/// An enumeration that attempts to efficiently store the key path elements
@@ -44,74 +44,102 @@ public struct __ExpressionID: Sendable {
44
44
}
45
45
46
46
/// The elements of this identifier.
47
- private var _elements : Elements
47
+ fileprivate var elements : Elements
48
+ }
48
49
49
- /// A representation of this instance suitable for use as a key path in an
50
- /// instance of `Graph` where the key type is `UInt32`.
50
+ // MARK: - Equatable, Hashable
51
+
52
+ extension __ExpressionID : Equatable , Hashable { }
53
+ extension __ExpressionID . Elements : Equatable , Hashable { }
54
+
55
+ // MARK: - Collection
56
+
57
+ extension __ExpressionID {
58
+ /// A type representing the elements in a key path produced from the unique
59
+ /// identifier of an expression.
51
60
///
52
- /// The values in this collection, being swift-syntax node IDs, are never more
53
- /// than 32 bits wide .
54
- var keyPath : some RandomAccessCollection < UInt32 > {
55
- // Helper function to unpack a sequence of words into bit indices for use as
56
- // a Graph's key path.
57
- func makeKeyPath ( from words : some RandomAccessCollection < UInt64 > ) -> [ UInt32 ] {
58
- // Assume approximately 1/4 of the bits are populated. We can always tweak
59
- // this guesstimate after gathering more real-world data.
60
- var result = [ UInt32 ] ( )
61
- result . reserveCapacity ( ( words . count * UInt64 . bitWidth ) / 4 )
62
-
63
- for (bitOffset , word ) in words . enumerated ( ) {
64
- var word = word
65
- while word != 0 {
66
- let bit = word . trailingZeroBitCount
67
- result . append ( UInt32 ( bit + bitOffset ) )
68
- word = word & ( word &- 1 ) // Mask off the bit we just counted.
69
- }
61
+ /// Instances of this type can be used to produce keys and key paths for an
62
+ /// instance of `Graph` whose key type is `UInt32` .
63
+ fileprivate struct KeyPath : Collection {
64
+ /// Underlying storage for the collection.
65
+ var elements : __ExpressionID . Elements
66
+
67
+ var underestimatedCount : Int {
68
+ count
69
+ }
70
+
71
+ var count : Int {
72
+ switch elements {
73
+ case . none :
74
+ 0
75
+ case let . packed ( word ) :
76
+ word . nonzeroBitCount
77
+ case let . keyPath ( keyPath ) :
78
+ keyPath . count
70
79
}
80
+ }
71
81
72
- return result
82
+ var startIndex : Int {
83
+ switch elements {
84
+ case . none, . keyPath:
85
+ 0
86
+ case let . packed( word) :
87
+ word. trailingZeroBitCount
88
+ }
73
89
}
74
90
75
- switch _elements {
76
- case . none:
77
- return [ ]
78
- case let . packed( word) :
79
- // Assume approximately 1/4 of the bits are populated. We can always tweak
80
- // this guesstimate after gathering more real-world data.
81
- var result = [ UInt32] ( )
82
- result. reserveCapacity ( UInt64 . bitWidth / 4 )
83
-
84
- var word = word
85
- while word != 0 {
86
- let bit = word. trailingZeroBitCount
87
- result. append ( UInt32 ( bit) )
88
- word = word & ( word &- 1 ) // Mask off the bit we just counted.
91
+ var endIndex : Int {
92
+ switch elements {
93
+ case . none:
94
+ 0
95
+ case . packed:
96
+ UInt64 . bitWidth
97
+ case let . keyPath( keyPath) :
98
+ keyPath. count
89
99
}
100
+ }
90
101
91
- return result
92
- case let . keyPath( keyPath) :
93
- return keyPath
102
+ func index( after i: Int ) -> Int {
103
+ switch elements {
104
+ case . none, . keyPath:
105
+ return i + 1
106
+ case let . packed( word) :
107
+ // Mask off the low bits including the one at `i`. The trailing zero
108
+ // count of the resulting value equals the next actual bit index.
109
+ let nextIndex = i + 1
110
+ let maskedWord = word & ( ~ 0 << nextIndex)
111
+ return maskedWord. trailingZeroBitCount
112
+ }
94
113
}
95
- }
96
- }
97
114
98
- // MARK: - Equatable, Hashable
115
+ subscript( position: Int ) -> UInt32 {
116
+ switch elements {
117
+ case . none:
118
+ fatalError ( " Unreachable " )
119
+ case . packed:
120
+ UInt32 ( position)
121
+ case let . keyPath( keyPath) :
122
+ keyPath [ position]
123
+ }
124
+ }
125
+ }
99
126
100
- extension __ExpressionID : Equatable , Hashable { }
101
- extension __ExpressionID . Elements : Equatable , Hashable { }
127
+ /// A representation of this instance suitable for use as a key path in an
128
+ /// instance of `Graph` where the key type is `UInt32`.
129
+ ///
130
+ /// The values in this collection, being swift-syntax node IDs, are never more
131
+ /// than 32 bits wide.
132
+ var keyPath : some Collection < UInt32 > {
133
+ KeyPath ( elements: elements)
134
+ }
135
+ }
102
136
103
137
#if DEBUG
104
138
// MARK: - CustomStringConvertible, CustomDebugStringConvertible
105
139
106
140
extension __ExpressionID : CustomStringConvertible , CustomDebugStringConvertible {
107
- /// The number of bits in a nybble.
108
- private static var _bitsPerNybble : Int { 4 }
109
-
110
- /// The number of nybbles in a word.
111
- private static var _nybblesPerWord : Int { UInt64 . bitWidth / _bitsPerNybble }
112
-
113
141
public var description : String {
114
- switch _elements {
142
+ switch elements {
115
143
case . none:
116
144
return " 0 "
117
145
case let . packed( word) :
@@ -134,14 +162,14 @@ extension __ExpressionID: CustomStringConvertible, CustomDebugStringConvertible
134
162
extension __ExpressionID : ExpressibleByIntegerLiteral {
135
163
public init ( integerLiteral: UInt64 ) {
136
164
if integerLiteral == 0 {
137
- self . init ( _elements : . none)
165
+ self . init ( elements : . none)
138
166
} else {
139
- self . init ( _elements : . packed( integerLiteral) )
167
+ self . init ( elements : . packed( integerLiteral) )
140
168
}
141
169
}
142
170
143
171
public init ( _ keyPath: UInt32 ... ) {
144
- self . init ( _elements : . keyPath( keyPath) )
172
+ self . init ( elements : . keyPath( keyPath) )
145
173
}
146
174
}
147
175
0 commit comments