@@ -31,12 +31,16 @@ open class SyntaxVisitor {
31
31
/// We can then re-use them to create new syntax nodes.
32
32
///
33
33
/// The array's size should be a typical nesting depth of a Swift file. That way we can store all allocated syntax
34
- /// nodes when unwinding the visitation stack. It shouldn't be much larger because that would mean that we need to
35
- /// look through more memory to find a cache miss. 40 has been chosen empirically to strike a good balance here.
34
+ /// nodes when unwinding the visitation stack.
36
35
///
37
36
/// The actual `info` stored in the `Syntax.Info` objects is garbage. It needs to be set when any of the `Syntax.Info`
38
37
/// objects get re-used.
39
- private var recyclableNodeInfos : ContiguousArray < Syntax . Info ? > = ContiguousArray ( repeating: nil , count: 40 )
38
+ private var recyclableNodeInfos : ContiguousArray < Syntax . Info ? > = ContiguousArray ( repeating: nil , count: 64 )
39
+
40
+ /// A bit is set to 1 if the corresponding index in `recyclableNodeInfos` is occupied and ready to be reused.
41
+ ///
42
+ /// The last bit in this UInt64 corresponds to index 0 in `recyclableNodeInfos`.
43
+ private var recyclableNodeInfosUsageBitmap : UInt64 = 0
40
44
41
45
public init ( viewMode: SyntaxTreeViewMode ) {
42
46
self . viewMode = viewMode
@@ -5291,11 +5295,12 @@ open class SyntaxVisitor {
5291
5295
for childRaw in NonNilRawSyntaxChildren ( syntaxNode, viewMode: viewMode) {
5292
5296
// syntaxNode gets retained here. That seems unnecessary but I don't know how to remove it.
5293
5297
var childNode : Syntax
5294
- if let recycledInfoIndex = recyclableNodeInfos. firstIndex ( where: { $0 != nil
5295
- } ) {
5298
+ if let recycledInfoIndex = recyclableNodeInfosUsageBitmap. indexOfRightmostOne {
5296
5299
var recycledInfo : Syntax . Info ? = nil
5297
5300
// Use `swap` to extract the recyclable syntax node without incurring ref-counting.
5298
5301
swap ( & recycledInfo, & recyclableNodeInfos[ recycledInfoIndex] )
5302
+ assert ( recycledInfo != nil , " Slot indicated by the bitmap did not contain a value " )
5303
+ recyclableNodeInfosUsageBitmap. setBitToZero ( at: recycledInfoIndex)
5299
5304
// syntaxNode.info gets retained here. This is necessary because we build up the parent tree.
5300
5305
recycledInfo!. info = . nonRoot( . init( parent: syntaxNode, absoluteInfo: childRaw. info) )
5301
5306
childNode = Syntax ( childRaw. raw, info: recycledInfo!)
@@ -5307,12 +5312,39 @@ open class SyntaxVisitor {
5307
5312
// The node didn't get stored by the subclass's visit method. We can re-use the memory of its `Syntax.Info`
5308
5313
// for future syntax nodes.
5309
5314
childNode. info. info = nil
5310
- if let emptySlot = recyclableNodeInfos. firstIndex ( where: { $0 == nil
5311
- } ) {
5315
+ if let emptySlot = recyclableNodeInfosUsageBitmap. indexOfRightmostZero {
5312
5316
// Use `swap` to store the recyclable syntax node without incurring ref-counting.
5313
5317
swap ( & recyclableNodeInfos[ emptySlot] , & childNode. info)
5318
+ assert ( childNode. info == nil , " Slot should not have contained a value " )
5319
+ recyclableNodeInfosUsageBitmap. setBitToOne ( at: emptySlot)
5314
5320
}
5315
5321
}
5316
5322
}
5317
5323
}
5318
5324
}
5325
+
5326
+ fileprivate extension UInt64 {
5327
+ var indexOfRightmostZero : Int ? {
5328
+ return ( ~ self ) . indexOfRightmostOne
5329
+ }
5330
+
5331
+
5332
+ var indexOfRightmostOne : Int ? {
5333
+ let trailingZeroCount = self . trailingZeroBitCount
5334
+ if trailingZeroCount == Self . bitWidth {
5335
+ // All indicies are 0
5336
+ return nil
5337
+ }
5338
+ return trailingZeroCount
5339
+ }
5340
+
5341
+
5342
+ mutating func setBitToZero( at index: Int ) {
5343
+ self &= ~ ( 1 << index)
5344
+ }
5345
+
5346
+
5347
+ mutating func setBitToOne( at index: Int ) {
5348
+ self |= 1 << index
5349
+ }
5350
+ }
0 commit comments