Skip to content

Commit 84f581d

Browse files
authored
Merge pull request #2801 from spevans/base64decode-speedup2
2 parents 1c97d54 + 8af6d0f commit 84f581d

File tree

1 file changed

+26
-12
lines changed

1 file changed

+26
-12
lines changed

Sources/Foundation/NSData.swift

+26-12
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
210210
return nil
211211
}
212212
super.init()
213-
_init(bytes: &decodedBytes, length: decodedBytes.count, copy: true)
213+
_init(bytes: decodedBytes.baseAddress!, length: decodedBytes.count, copy: false, deallocator: { (ptr, length) in
214+
ptr.deallocate()
215+
})
214216
}
215217

216218
/// Initializes a data object with the given Base64 encoded data.
@@ -219,7 +221,9 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
219221
return nil
220222
}
221223
super.init()
222-
_init(bytes: &decodedBytes, length: decodedBytes.count, copy: true)
224+
_init(bytes: decodedBytes.baseAddress!, length: decodedBytes.count, copy: false, deallocator: { (ptr, length) in
225+
ptr.deallocate()
226+
})
223227
}
224228

225229
deinit {
@@ -634,7 +638,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
634638
- parameter options: Options for handling invalid input
635639
- returns: The decoded bytes.
636640
*/
637-
private static func base64DecodeBytes<T: Collection>(_ bytes: T, options: Base64DecodingOptions = []) -> [UInt8]? where T.Element == UInt8 {
641+
private static func base64DecodeBytes<T: Collection>(_ bytes: T, options: Base64DecodingOptions = []) -> UnsafeMutableRawBufferPointer? where T.Element == UInt8 {
638642

639643
// This table maps byte values 0-127, input bytes >127 are always invalid.
640644
// Map the ASCII characters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -> 0...63
@@ -659,14 +663,21 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
659663
return nil
660664
}
661665

662-
var decodedBytes: [UInt8] = []
663666
let capacity = (bytes.count * 3) / 4 // Every 4 valid ASCII bytes maps to 3 output bytes.
664-
decodedBytes.reserveCapacity(capacity)
667+
let buffer = UnsafeMutableRawPointer.allocate(byteCount: capacity, alignment: 1)
668+
var outputIndex = 0
669+
670+
func append(_ byte: UInt8) {
671+
assert(outputIndex < capacity)
672+
buffer.storeBytes(of: byte, toByteOffset: outputIndex, as: UInt8.self)
673+
outputIndex += 1
674+
}
665675

666676
var currentByte: UInt8 = 0
667677
var validCharacterCount = 0
668678
var paddingCount = 0
669679
var index = 0
680+
var error = false
670681

671682
for base64Char in bytes {
672683
var value: UInt8 = 0
@@ -690,30 +701,32 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
690701
if ignoreUnknown {
691702
continue
692703
} else {
693-
return nil
704+
error = true
705+
break
694706
}
695707
}
696708
validCharacterCount += 1
697709

698710
// Padding found in the middle of the sequence is invalid.
699711
if paddingCount > 0 {
700-
return nil
712+
error = true
713+
break
701714
}
702715

703716
switch index {
704717
case 0:
705718
currentByte = (value << 2)
706719
case 1:
707720
currentByte |= (value >> 4)
708-
decodedBytes.append(currentByte)
721+
append(currentByte)
709722
currentByte = (value << 4)
710723
case 2:
711724
currentByte |= (value >> 2)
712-
decodedBytes.append(currentByte)
725+
append(currentByte)
713726
currentByte = (value << 6)
714727
case 3:
715728
currentByte |= value
716-
decodedBytes.append(currentByte)
729+
append(currentByte)
717730
index = -1
718731
default:
719732
fatalError()
@@ -722,11 +735,12 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
722735
index += 1
723736
}
724737

725-
guard (validCharacterCount + paddingCount) % 4 == 0 else {
738+
guard error == false && (validCharacterCount + paddingCount) % 4 == 0 else {
726739
// Invalid character count of valid input characters.
740+
buffer.deallocate()
727741
return nil
728742
}
729-
return decodedBytes
743+
return UnsafeMutableRawBufferPointer(start: buffer, count: outputIndex)
730744
}
731745

732746
/**

0 commit comments

Comments
 (0)