Skip to content

Commit ae224ca

Browse files
committed
[string] Restore _HasContiguousBytes for untyped storage
UnsafeRawBufferPointer cannot implement withContiguousStorageIfAvailable because doing so would potentially create a typed pointer from untyped data.
1 parent e536ad2 commit ae224ca

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

stdlib/public/core/ContiguouslyStored.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
// NOTE: The below is necessary for fast String initialization from untyped
14+
// memory. When we add Collection.withContiguousRawStorageIfAvailabe(), we can
15+
// deprecate this functionality.
16+
1317
@usableFromInline
1418
internal protocol _HasContiguousBytes {
1519
func withUnsafeBytes<R>(

stdlib/public/core/String.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,21 @@ extension String {
422422
codeUnits, encoding: sourceEncoding, repair: true)!.0
423423
return
424424
}
425+
426+
// Fast path for untyped raw storage and known stdlib types
427+
if let contigBytes = codeUnits as? _HasContiguousBytes,
428+
contigBytes._providesContiguousBytesNoCopy
429+
{
430+
self = contigBytes.withUnsafeBytes { rawBufPtr in
431+
return String._fromUTF8Repairing(
432+
UnsafeBufferPointer(
433+
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
434+
count: rawBufPtr.count)).0
435+
}
436+
return
437+
}
438+
439+
// Fast path for user-defined Collections
425440
if let str = codeUnits.withContiguousStorageIfAvailable({
426441
(buffer: UnsafeBufferPointer<C.Element>) -> String in
427442
return String._fromUTF8Repairing(

stdlib/public/core/StringCreate.swift

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -228,28 +228,38 @@ extension String {
228228
return _slowFromCodeUnits(input, encoding: encoding, repair: repair)
229229
}
230230

231-
// Needed for double-optional due to returning optional string in
232-
// _fromASCIIValidating in withContiguousStorageIfAvailable
233-
let resultOpt: String?
231+
// Helper to simplify early returns
232+
func resultOrSlow(_ resultOpt: String?) -> (String, repairsMade: Bool)? {
233+
guard let result = resultOpt else {
234+
return _slowFromCodeUnits(input, encoding: encoding, repair: repair)
235+
}
236+
return (result, repairsMade: false)
237+
}
234238

239+
// Fast path for untyped raw storage and known stdlib types
240+
if let contigBytes = input as? _HasContiguousBytes,
241+
contigBytes._providesContiguousBytesNoCopy {
242+
return resultOrSlow(contigBytes.withUnsafeBytes { rawBufPtr in
243+
let buffer = UnsafeBufferPointer(
244+
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
245+
count: rawBufPtr.count)
246+
return String._fromASCIIValidating(buffer)
247+
})
248+
}
249+
250+
// Fast path for user-defined Collections
235251
if let strOpt = input.withContiguousStorageIfAvailable({
236252
(buffer: UnsafeBufferPointer<Input.Element>) -> String? in
237253
return String._fromASCIIValidating(
238254
UnsafeRawBufferPointer(buffer).bindMemory(to: UInt8.self))
239255
}) {
240-
resultOpt = strOpt
241-
} else {
242-
resultOpt = Array(input).withUnsafeBufferPointer {
243-
let buffer = UnsafeRawBufferPointer($0).bindMemory(to: UInt8.self)
244-
return String._fromASCIIValidating(buffer)
245-
}
246-
}
247-
248-
guard let result = resultOpt else {
249-
return _slowFromCodeUnits(input, encoding: encoding, repair: repair)
256+
return resultOrSlow(strOpt)
250257
}
251258

252-
return (result, repairsMade: false)
259+
return resultOrSlow(Array(input).withUnsafeBufferPointer {
260+
let buffer = UnsafeRawBufferPointer($0).bindMemory(to: UInt8.self)
261+
return String._fromASCIIValidating(buffer)
262+
})
253263
}
254264

255265
public // @testable

0 commit comments

Comments
 (0)