@@ -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