Skip to content

Commit

Permalink
[string] Restore _HasContiguousBytes for untyped storage
Browse files Browse the repository at this point in the history
UnsafeRawBufferPointer cannot implement
withContiguousStorageIfAvailable because doing so would potentially
create a typed pointer from untyped data.
  • Loading branch information
milseman committed Apr 9, 2020
1 parent e536ad2 commit ae224ca
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 14 deletions.
4 changes: 4 additions & 0 deletions stdlib/public/core/ContiguouslyStored.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
//
//===----------------------------------------------------------------------===//

// NOTE: The below is necessary for fast String initialization from untyped
// memory. When we add Collection.withContiguousRawStorageIfAvailabe(), we can
// deprecate this functionality.

@usableFromInline
internal protocol _HasContiguousBytes {
func withUnsafeBytes<R>(
Expand Down
15 changes: 15 additions & 0 deletions stdlib/public/core/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,21 @@ extension String {
codeUnits, encoding: sourceEncoding, repair: true)!.0
return
}

// Fast path for untyped raw storage and known stdlib types
if let contigBytes = codeUnits as? _HasContiguousBytes,
contigBytes._providesContiguousBytesNoCopy
{
self = contigBytes.withUnsafeBytes { rawBufPtr in
return String._fromUTF8Repairing(
UnsafeBufferPointer(
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
count: rawBufPtr.count)).0
}
return
}

// Fast path for user-defined Collections
if let str = codeUnits.withContiguousStorageIfAvailable({
(buffer: UnsafeBufferPointer<C.Element>) -> String in
return String._fromUTF8Repairing(
Expand Down
38 changes: 24 additions & 14 deletions stdlib/public/core/StringCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,28 +228,38 @@ extension String {
return _slowFromCodeUnits(input, encoding: encoding, repair: repair)
}

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

// Fast path for untyped raw storage and known stdlib types
if let contigBytes = input as? _HasContiguousBytes,
contigBytes._providesContiguousBytesNoCopy {
return resultOrSlow(contigBytes.withUnsafeBytes { rawBufPtr in
let buffer = UnsafeBufferPointer(
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
count: rawBufPtr.count)
return String._fromASCIIValidating(buffer)
})
}

// Fast path for user-defined Collections
if let strOpt = input.withContiguousStorageIfAvailable({
(buffer: UnsafeBufferPointer<Input.Element>) -> String? in
return String._fromASCIIValidating(
UnsafeRawBufferPointer(buffer).bindMemory(to: UInt8.self))
}) {
resultOpt = strOpt
} else {
resultOpt = Array(input).withUnsafeBufferPointer {
let buffer = UnsafeRawBufferPointer($0).bindMemory(to: UInt8.self)
return String._fromASCIIValidating(buffer)
}
}

guard let result = resultOpt else {
return _slowFromCodeUnits(input, encoding: encoding, repair: repair)
return resultOrSlow(strOpt)
}

return (result, repairsMade: false)
return resultOrSlow(Array(input).withUnsafeBufferPointer {
let buffer = UnsafeRawBufferPointer($0).bindMemory(to: UInt8.self)
return String._fromASCIIValidating(buffer)
})
}

public // @testable
Expand Down

0 comments on commit ae224ca

Please sign in to comment.