Skip to content

Commit 9064163

Browse files
[SwiftScan] Improve the bridging between [String] and const char **
Borrow the implementation from stdlib test for a faster way to bridging swift `[String]` to `const char **`. Instead of strdup every string which can be costly if the argument list is very long, use one single big allocation to hold all the strings.
1 parent f66e335 commit 9064163

File tree

1 file changed

+39
-14
lines changed

1 file changed

+39
-14
lines changed

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -674,20 +674,45 @@ private extension swiftscan_functions_t {
674674
}
675675
}
676676

677-
// TODO: Move to TSC?
678-
/// Perform an `action` passing it a `const char **` constructed out of `[String]`
679-
@_spi(Testing) public func withArrayOfCStrings<T>(_ strings: [String],
680-
_ action: (UnsafeMutablePointer<UnsafePointer<Int8>?>?) -> T) -> T
681-
{
682-
#if os(Windows)
683-
let cstrings = strings.map { _strdup($0) } + [nil]
684-
#else
685-
let cstrings = strings.map { strdup($0) } + [nil]
686-
#endif
687-
let unsafeCStrings = cstrings.map { UnsafePointer($0) }
688-
let result = unsafeCStrings.withUnsafeBufferPointer {
689-
action(UnsafeMutablePointer(mutating: $0.baseAddress))
677+
// TODO: Move the following functions to TSC?
678+
/// Helper function to scan a sequence type to help generate pointers for C String Arrays.
679+
func scan<
680+
S: Sequence, U
681+
>(_ seq: S, _ initial: U, _ combine: (U, S.Element) -> U) -> [U] {
682+
var result: [U] = []
683+
result.reserveCapacity(seq.underestimatedCount)
684+
var runningResult = initial
685+
for element in seq {
686+
runningResult = combine(runningResult, element)
687+
result.append(runningResult)
690688
}
691-
for ptr in cstrings { if let ptr = ptr { free(ptr) } }
692689
return result
693690
}
691+
692+
/// Perform an `action` passing it a `const char **` constructed out of `[String]`
693+
@_spi(Testing) public func withArrayOfCStrings<T>(
694+
_ args: [String],
695+
_ body: (UnsafeMutablePointer<UnsafePointer<Int8>?>?) -> T
696+
) -> T {
697+
let argsCounts = Array(args.map { $0.utf8.count + 1 })
698+
let argsOffsets = [0] + scan(argsCounts, 0, +)
699+
let argsBufferSize = argsOffsets.last!
700+
var argsBuffer: [UInt8] = []
701+
argsBuffer.reserveCapacity(argsBufferSize)
702+
for arg in args {
703+
argsBuffer.append(contentsOf: arg.utf8)
704+
argsBuffer.append(0)
705+
}
706+
return argsBuffer.withUnsafeMutableBufferPointer {
707+
(argsBuffer) in
708+
let ptr = UnsafeRawPointer(argsBuffer.baseAddress!).bindMemory(
709+
to: Int8.self, capacity: argsBuffer.count)
710+
var cStrings: [UnsafePointer<Int8>?] = argsOffsets.map { ptr + $0 }
711+
cStrings[cStrings.count - 1] = nil
712+
return cStrings.withUnsafeMutableBufferPointer {
713+
let unsafeString = UnsafeMutableRawPointer($0.baseAddress!).bindMemory(
714+
to: UnsafePointer<Int8>?.self, capacity: $0.count)
715+
return body(unsafeString)
716+
}
717+
}
718+
}

0 commit comments

Comments
 (0)