Skip to content

Commit 4617553

Browse files
committed
[se-0405] improve slow path
1 parent 0ba58de commit 4617553

File tree

1 file changed

+53
-21
lines changed

1 file changed

+53
-21
lines changed

stdlib/public/core/StringCreate.swift

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -317,29 +317,61 @@ extension String {
317317
}
318318

319319
// slow-path
320-
// this multiplier is a worst-case estimate
321-
let multiplier = if encoding.self == UTF16.self { 3 } else { 4 }
322-
return withUnsafeTemporaryAllocation(
323-
of: UInt8.self, capacity: input.count * multiplier
324-
) {
325-
output -> String? in
326-
var isASCII = true
327-
var index = output.startIndex
328-
let error = transcode(
329-
input.makeIterator(),
330-
from: encoding.self,
331-
to: UTF8.self,
332-
stoppingOnError: true,
333-
into: {
334-
uint8 in
335-
output[index] = uint8
336-
output.formIndex(after: &index)
337-
if isASCII && (uint8 & 0x80) == 0x80 { isASCII = false }
320+
var isASCII = true
321+
var buffer: UnsafeMutableBufferPointer<UInt8>
322+
buffer = UnsafeMutableBufferPointer.allocate(capacity: input.count*3)
323+
var written = buffer.startIndex
324+
325+
var parser = Encoding.ForwardParser()
326+
var input = input.makeIterator()
327+
328+
transcodingLoop:
329+
while true {
330+
switch parser.parseScalar(from: &input) {
331+
case .valid(let s):
332+
let scalar = Encoding.decode(s)
333+
guard let utf8 = Unicode.UTF8.encode(scalar) else {
334+
// transcoding error: clean up and return nil
335+
fallthrough
338336
}
337+
if buffer.count < written + utf8.count {
338+
let newCapacity = buffer.count + (buffer.count >> 1)
339+
let copy: UnsafeMutableBufferPointer<UInt8>
340+
copy = UnsafeMutableBufferPointer.allocate(capacity: newCapacity)
341+
let copied = copy.moveInitialize(
342+
fromContentsOf: buffer.prefix(through: written)
343+
)
344+
buffer.deallocate()
345+
buffer = copy
346+
written = copied
347+
}
348+
if isASCII && utf8.count > 1 {
349+
isASCII = false
350+
}
351+
written = buffer.suffix(from: written).initialize(fromContentsOf: utf8)
352+
break
353+
case .error:
354+
// validation error: clean up and return nil
355+
buffer.prefix(through: written).deinitialize()
356+
buffer.deallocate()
357+
return nil
358+
case .emptyInput:
359+
break transcodingLoop
360+
}
361+
}
362+
363+
let storage = buffer.baseAddress.map {
364+
__SharedStringStorage(
365+
_mortal: $0,
366+
countAndFlags: _StringObject.CountAndFlags(
367+
count: buffer.startIndex.distance(to: written),
368+
isASCII: isASCII,
369+
isNFC: isASCII,
370+
isNativelyStored: false,
371+
isTailAllocated: false
372+
)
339373
)
340-
if error { return nil }
341-
let bytes = UnsafeBufferPointer(start: output.baseAddress, count: index)
342-
return String._uncheckedFromUTF8(bytes, asciiPreScanResult: isASCII)
343374
}
375+
return storage?.asString
344376
}
345377
}

0 commit comments

Comments
 (0)