@@ -317,29 +317,61 @@ extension String {
317
317
}
318
318
319
319
// 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
338
336
}
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
+ )
339
373
)
340
- if error { return nil }
341
- let bytes = UnsafeBufferPointer ( start: output. baseAddress, count: index)
342
- return String . _uncheckedFromUTF8 ( bytes, asciiPreScanResult: isASCII)
343
374
}
375
+ return storage? . asString
344
376
}
345
377
}
0 commit comments