2222public struct __ExpressionID : Sendable {
2323 /// The ID of the root node in an expression graph.
2424 static var root : Self {
25- Self ( _elements : . none)
25+ Self ( elements : . none)
2626 }
2727
2828 /// An enumeration that attempts to efficiently store the key path elements
@@ -44,74 +44,102 @@ public struct __ExpressionID: Sendable {
4444 }
4545
4646 /// The elements of this identifier.
47- private var _elements : Elements
47+ fileprivate var elements : Elements
48+ }
4849
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.
5160 ///
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
7079 }
80+ }
7181
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+ }
7389 }
7490
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
8999 }
100+ }
90101
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+ }
94113 }
95- }
96- }
97114
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+ }
99126
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+ }
102136
103137#if DEBUG
104138// MARK: - CustomStringConvertible, CustomDebugStringConvertible
105139
106140extension __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-
113141 public var description : String {
114- switch _elements {
142+ switch elements {
115143 case . none:
116144 return " 0 "
117145 case let . packed( word) :
@@ -134,14 +162,14 @@ extension __ExpressionID: CustomStringConvertible, CustomDebugStringConvertible
134162extension __ExpressionID : ExpressibleByIntegerLiteral {
135163 public init ( integerLiteral: UInt64 ) {
136164 if integerLiteral == 0 {
137- self . init ( _elements : . none)
165+ self . init ( elements : . none)
138166 } else {
139- self . init ( _elements : . packed( integerLiteral) )
167+ self . init ( elements : . packed( integerLiteral) )
140168 }
141169 }
142170
143171 public init ( _ keyPath: UInt32 ... ) {
144- self . init ( _elements : . keyPath( keyPath) )
172+ self . init ( elements : . keyPath( keyPath) )
145173 }
146174}
147175
0 commit comments