Skip to content

Base64: Speedup decoding fix 3 #2805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions Sources/Foundation/NSData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,22 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {

/// Initializes a data object with the given Base64 encoded string.
public init?(base64Encoded base64String: String, options: Base64DecodingOptions = []) {
guard var decodedBytes = NSData.base64DecodeBytes(base64String.utf8, options: options) else {
return nil

let result: UnsafeMutableRawBufferPointer?
if let _result = base64String.utf8.withContiguousStorageIfAvailable({ buffer -> UnsafeMutableRawBufferPointer? in
let rawBuffer = UnsafeRawBufferPointer(start: buffer.baseAddress!, count: buffer.count)
return NSData.base64DecodeBytes(rawBuffer, options: options)
}) {
result = _result
} else {
// Slow path, unlikely that withContiguousStorageIfAvailable will fail but if it does, fall back to .utf8CString.
// This will allocate and copy but it is the simplest way to get a contiguous buffer.
result = base64String.utf8CString.withUnsafeBufferPointer { buffer -> UnsafeMutableRawBufferPointer? in
let rawBuffer = UnsafeRawBufferPointer(start: buffer.baseAddress!, count: buffer.count - 1) // -1 to ignore the terminating NUL
return NSData.base64DecodeBytes(rawBuffer, options: options)
}
}
guard let decodedBytes = result else { return nil }
super.init()
_init(bytes: decodedBytes.baseAddress!, length: decodedBytes.count, copy: false, deallocator: { (ptr, length) in
ptr.deallocate()
Expand All @@ -217,7 +230,9 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {

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

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