Skip to content

Commit fe1a84f

Browse files
committed
Avoid Unnecessary Copies While Deserializing String Payloads
Creating a buffer of elements then reinterpreting them as unicode scalars involves extra allocations and copies of the underlying data. Instead, create a scratch buffer for an array of bytes and write directly into that.
1 parent ee32a9f commit fe1a84f

File tree

1 file changed

+21
-5
lines changed

1 file changed

+21
-5
lines changed

Sources/TSCUtility/BitstreamReader.swift

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,29 @@ private struct BitstreamReader {
176176
switch lastOperand {
177177
case .array(let element):
178178
let length = try cursor.readVBR(6)
179-
var elements = [UInt64]()
180-
for _ in 0..<length {
181-
elements.append(try readSingleAbbreviatedRecordOperand(element))
182-
}
183179
if case .char6 = element {
184-
payload = .char6String(String(String.UnicodeScalarView(elements.map { UnicodeScalar(UInt8($0)) })))
180+
// FIXME: Once the minimum deployment target bumps to macOS 11, use
181+
// the more ergonomic stdlib API everywhere.
182+
if #available(macOS 11.0, *) {
183+
payload = try .char6String(String(unsafeUninitializedCapacity: Int(length)) { buffer in
184+
for i in 0..<Int(length) {
185+
buffer[i] = try UInt8(readSingleAbbreviatedRecordOperand(element))
186+
}
187+
return Int(length)
188+
})
189+
} else {
190+
let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: Int(length))
191+
defer { buffer.deallocate() }
192+
for i in 0..<Int(length) {
193+
buffer[i] = try UInt8(readSingleAbbreviatedRecordOperand(element))
194+
}
195+
payload = .char6String(String(decoding: buffer, as: UTF8.self))
196+
}
185197
} else {
198+
var elements = [UInt64]()
199+
for _ in 0..<length {
200+
elements.append(try readSingleAbbreviatedRecordOperand(element))
201+
}
186202
payload = .array(elements)
187203
}
188204
case .blob:

0 commit comments

Comments
 (0)