Skip to content

Commit e6f3d73

Browse files
committed
[Perf] Optimize RawSyntax.syntaxTextBytes
Copy consecutive SyntaxText at once
1 parent 4960545 commit e6f3d73

File tree

2 files changed

+29
-25
lines changed

2 files changed

+29
-25
lines changed

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -361,16 +361,23 @@ extension RawSyntax {
361361
}
362362

363363
extension RawTriviaPiece {
364-
func withSyntaxText(body: (SyntaxText) throws -> Void) rethrows {
364+
/// Call `body` with the syntax text of this trivia piece.
365+
///
366+
/// If `isEphemeral` is `true`, the ``SyntaxText`` argument is only guaranteed
367+
/// to be valid within the call.
368+
func withSyntaxText(body: (SyntaxText, _ isEphemeral: Bool) throws -> Void) rethrows {
365369
if let syntaxText = storedText {
366-
try body(syntaxText)
370+
try body(syntaxText, /*isEphemeral*/ false)
367371
return
368372
}
369373

370374
var description = ""
371375
write(to: &description)
372376
try description.withUTF8 { buffer in
373-
try body(SyntaxText(baseAddress: buffer.baseAddress, count: buffer.count))
377+
try body(
378+
SyntaxText(baseAddress: buffer.baseAddress, count: buffer.count),
379+
/*isEphemeral*/ true
380+
)
374381
}
375382
}
376383
}
@@ -382,21 +389,21 @@ extension RawSyntax {
382389
/// Unlike `description`, this provides a source-accurate representation
383390
/// even in the presence of malformed UTF-8 in the input source.
384391
///
385-
/// The ``SyntaxText`` arguments passed to the visitor are only guaranteed
386-
/// to be valid within that call. It is unsafe to escape the `SyntaxValue`
387-
/// values outside of the closure.
388-
public func withEachSyntaxText(body: (SyntaxText) throws -> Void) rethrows {
392+
/// If `isEphemeral` is `true`, the ``SyntaxText`` arguments passed to the
393+
/// visitor are only guaranteed to be valid within that call. Otherwise, they
394+
/// are valid as long as the raw syntax is alive.
395+
public func withEachSyntaxText(body: (SyntaxText, _ isEphemeral: Bool) throws -> Void) rethrows {
389396
switch rawData.payload {
390397
case .parsedToken(let dat):
391398
if dat.presence == .present {
392-
try body(dat.wholeText)
399+
try body(dat.wholeText, /*isEphemeral*/ false)
393400
}
394401
case .materializedToken(let dat):
395402
if dat.presence == .present {
396403
for p in dat.leadingTrivia {
397404
try p.withSyntaxText(body: body)
398405
}
399-
try body(dat.tokenText)
406+
try body(dat.tokenText, /*isEphemeral*/ false)
400407
for p in dat.trailingTrivia {
401408
try p.withSyntaxText(body: body)
402409
}
@@ -412,9 +419,20 @@ extension RawSyntax {
412419
/// source even in the presence of invalid UTF-8.
413420
public var syntaxTextBytes: [UInt8] {
414421
var result: [UInt8] = []
415-
withEachSyntaxText { syntaxText in
416-
result.append(contentsOf: syntaxText)
422+
var buf: SyntaxText = ""
423+
withEachSyntaxText { syntaxText, isEphemeral in
424+
if isEphemeral {
425+
result.append(contentsOf: buf)
426+
result.append(contentsOf: syntaxText)
427+
buf = ""
428+
} else if let base = buf.baseAddress, base + buf.count == syntaxText.baseAddress {
429+
buf = SyntaxText(baseAddress: base, count: buf.count + syntaxText.count)
430+
} else {
431+
result.append(contentsOf: buf)
432+
buf = syntaxText
433+
}
417434
}
435+
result.append(contentsOf: buf)
418436
return result
419437
}
420438
}

Sources/SwiftSyntax/Syntax.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -365,20 +365,6 @@ extension Syntax: Identifiable {
365365
}
366366

367367
extension Syntax {
368-
/// Enumerate all of the syntax text present in this node, and all
369-
/// of its children, to give a source-accurate view of the bytes.
370-
///
371-
/// Unlike `description`, this provides a source-accurate representation
372-
/// even in the presence of malformed UTF-8 in the input source.
373-
///
374-
/// The ``SyntaxText`` arguments passed to the visitor are only guaranteed
375-
/// to be valid within that call. It is unsafe to escape the `SyntaxValue`
376-
/// values outside of the closure.
377-
@_spi(RawSyntax)
378-
public func withEachSyntaxText(body: (SyntaxText) throws -> Void) rethrows {
379-
try raw.withEachSyntaxText(body: body)
380-
}
381-
382368
/// Retrieve the syntax text as an array of bytes that models the input
383369
/// source even in the presence of invalid UTF-8.
384370
public var syntaxTextBytes: [UInt8] {

0 commit comments

Comments
 (0)