Skip to content

Commit 0ffe913

Browse files
committed
Fixes
1 parent 72f2eb3 commit 0ffe913

File tree

1 file changed

+29
-48
lines changed

1 file changed

+29
-48
lines changed

Sources/FoundationEssentials/Data/Data+Base64.swift

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -444,21 +444,8 @@ extension Base64 {
444444

445445
static func decode(string encoded: String, options: Data.Base64DecodingOptions = []) throws -> Data {
446446
let decoded = try encoded.utf8.withContiguousStorageIfAvailable { characterPointer -> Data in
447-
guard characterPointer.count > 0 else {
448-
return Data()
449-
}
450-
451-
let outputLength = ((characterPointer.count + 3) / 4) * 3
452-
453-
return try characterPointer.withMemoryRebound(to: UInt8.self) { input -> Data in
454-
let pointer = malloc(outputLength)
455-
let other = pointer?.bindMemory(to: UInt8.self, capacity: outputLength)
456-
let target = UnsafeMutableBufferPointer(start: other, count: outputLength)
457-
var length = outputLength
458-
try Self._decodeChromiumIgnoringErrors(from: input, into: target, length: &length, options: options)
459-
460-
return Data(bytesNoCopy: pointer!, count: length, deallocator: .free)
461-
}
447+
// `withContiguousStorageIfAvailable` sadly does not support typed throws
448+
try Self._decodeToData(from: characterPointer, options: options)
462449
}
463450

464451
if decoded != nil {
@@ -471,23 +458,9 @@ extension Base64 {
471458
}
472459

473460
static func decode(data encoded: Data, options: Data.Base64DecodingOptions = []) throws -> Data {
474-
let decoded = try encoded.withContiguousStorageIfAvailable { characterPointer -> Data in
461+
let decoded = try encoded.withContiguousStorageIfAvailable { bufferPointer -> Data in
475462
// `withContiguousStorageIfAvailable` sadly does not support typed throws
476-
guard characterPointer.count > 0 else {
477-
return Data()
478-
}
479-
480-
let outputLength = ((characterPointer.count + 3) / 4) * 3
481-
482-
return try characterPointer.withMemoryRebound(to: UInt8.self) { input -> Data in
483-
let pointer = malloc(outputLength)
484-
let other = pointer?.bindMemory(to: UInt8.self, capacity: outputLength)
485-
let target = UnsafeMutableBufferPointer(start: other, count: outputLength)
486-
var length = outputLength
487-
try Self._decodeChromiumIgnoringErrors(from: input, into: target, length: &length, options: options)
488-
489-
return Data(bytesNoCopy: pointer!, count: length, deallocator: .free)
490-
}
463+
try Self._decodeToData(from: bufferPointer, options: options)
491464
}
492465

493466
if decoded != nil {
@@ -502,23 +475,9 @@ extension Base64 {
502475
return Data()
503476
}
504477

505-
let decoded = try bytes.withContiguousStorageIfAvailable { characterPointer -> Data in
478+
let decoded = try bytes.withContiguousStorageIfAvailable { bufferPointer -> Data in
506479
// `withContiguousStorageIfAvailable` sadly does not support typed throws
507-
guard characterPointer.count > 0 else {
508-
return Data()
509-
}
510-
511-
let outputLength = ((characterPointer.count + 3) / 4) * 3
512-
513-
return try characterPointer.withMemoryRebound(to: UInt8.self) { input -> Data in
514-
let pointer = malloc(outputLength)
515-
let other = pointer?.bindMemory(to: UInt8.self, capacity: outputLength)
516-
let target = UnsafeMutableBufferPointer(start: other, count: outputLength)
517-
var length = outputLength
518-
try Self._decodeChromiumIgnoringErrors(from: input, into: target, length: &length, options: options)
519-
520-
return Data(bytesNoCopy: pointer!, count: length, deallocator: .free)
521-
}
480+
try Self._decodeToData(from: bufferPointer, options: options)
522481
}
523482

524483
if decoded != nil {
@@ -528,11 +487,33 @@ extension Base64 {
528487
return try self.decode(bytes: Array(bytes), options: options)
529488
}
530489

490+
static func _decodeToData(from inBuffer: UnsafeBufferPointer<UInt8>, options: Data.Base64DecodingOptions) throws(DecodingError) -> Data {
491+
guard inBuffer.count > 0 else {
492+
return Data()
493+
}
494+
495+
let outputLength = ((inBuffer.count + 3) / 4) * 3
496+
497+
let pointer = malloc(outputLength)
498+
let other = pointer?.bindMemory(to: UInt8.self, capacity: outputLength)
499+
let target = UnsafeMutableBufferPointer(start: other, count: outputLength)
500+
var length = outputLength
501+
if options.contains(.ignoreUnknownCharacters) {
502+
try Self._decodeChromiumIgnoringErrors(from: inBuffer, into: target, length: &length, options: options)
503+
} else {
504+
// for whatever reason I can see this being 10% faster for larger payloads. Maybe better
505+
// branch prediction?
506+
try self._decodeChromium(from: inBuffer, into: target, length: &length, options: options)
507+
}
508+
509+
return Data(bytesNoCopy: pointer!, count: length, deallocator: .free)
510+
}
511+
531512
static func _decodeChromium(
532513
from inBuffer: UnsafeBufferPointer<UInt8>,
533514
into outBuffer: UnsafeMutableBufferPointer<UInt8>,
534515
length: inout Int,
535-
options: Data.Base64DecodingOptions = []
516+
options: Data.Base64DecodingOptions
536517
) throws(DecodingError) {
537518
let remaining = inBuffer.count % 4
538519
guard remaining == 0 else { throw DecodingError.invalidLength }

0 commit comments

Comments
 (0)