Skip to content

Commit 01699b6

Browse files
authored
Merge pull request #2805 from spevans/base64decode-speedup3
2 parents cfac32b + bb51fd5 commit 01699b6

File tree

1 file changed

+19
-4
lines changed

1 file changed

+19
-4
lines changed

Sources/Foundation/NSData.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,22 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
206206

207207
/// Initializes a data object with the given Base64 encoded string.
208208
public init?(base64Encoded base64String: String, options: Base64DecodingOptions = []) {
209-
guard var decodedBytes = NSData.base64DecodeBytes(base64String.utf8, options: options) else {
210-
return nil
209+
210+
let result: UnsafeMutableRawBufferPointer?
211+
if let _result = base64String.utf8.withContiguousStorageIfAvailable({ buffer -> UnsafeMutableRawBufferPointer? in
212+
let rawBuffer = UnsafeRawBufferPointer(start: buffer.baseAddress!, count: buffer.count)
213+
return NSData.base64DecodeBytes(rawBuffer, options: options)
214+
}) {
215+
result = _result
216+
} else {
217+
// Slow path, unlikely that withContiguousStorageIfAvailable will fail but if it does, fall back to .utf8CString.
218+
// This will allocate and copy but it is the simplest way to get a contiguous buffer.
219+
result = base64String.utf8CString.withUnsafeBufferPointer { buffer -> UnsafeMutableRawBufferPointer? in
220+
let rawBuffer = UnsafeRawBufferPointer(start: buffer.baseAddress!, count: buffer.count - 1) // -1 to ignore the terminating NUL
221+
return NSData.base64DecodeBytes(rawBuffer, options: options)
222+
}
211223
}
224+
guard let decodedBytes = result else { return nil }
212225
super.init()
213226
_init(bytes: decodedBytes.baseAddress!, length: decodedBytes.count, copy: false, deallocator: { (ptr, length) in
214227
ptr.deallocate()
@@ -217,7 +230,9 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
217230

218231
/// Initializes a data object with the given Base64 encoded data.
219232
public init?(base64Encoded base64Data: Data, options: Base64DecodingOptions = []) {
220-
guard var decodedBytes = NSData.base64DecodeBytes(base64Data, options: options) else {
233+
guard let decodedBytes = base64Data.withUnsafeBytes({ rawBuffer in
234+
NSData.base64DecodeBytes(rawBuffer, options: options)
235+
}) else {
221236
return nil
222237
}
223238
super.init()
@@ -638,7 +653,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
638653
- parameter options: Options for handling invalid input
639654
- returns: The decoded bytes.
640655
*/
641-
private static func base64DecodeBytes<T: Collection>(_ bytes: T, options: Base64DecodingOptions = []) -> UnsafeMutableRawBufferPointer? where T.Element == UInt8 {
656+
private static func base64DecodeBytes(_ bytes: UnsafeRawBufferPointer, options: Base64DecodingOptions = []) -> UnsafeMutableRawBufferPointer? {
642657

643658
// This table maps byte values 0-127, input bytes >127 are always invalid.
644659
// Map the ASCII characters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -> 0...63

0 commit comments

Comments
 (0)