Skip to content

Commit b98db39

Browse files
authored
[Base64] Fix: Dont add line separators in last line when last line fits (#1184)
1 parent e4b6e26 commit b98db39

File tree

2 files changed

+72
-31
lines changed

2 files changed

+72
-31
lines changed

Sources/FoundationEssentials/Data/Data+Base64.swift

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -351,16 +351,8 @@ extension Base64 {
351351
Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { (e0, e1) throws(Never) -> Void in
352352
let to = input.count / 3 * 3
353353
var outIndex = 0
354-
for index in stride(from: 0, to: to, by: 3) {
355-
let i1 = input[index]
356-
let i2 = input[index &+ 1]
357-
let i3 = input[index &+ 2]
358-
buffer[outIndex] = e0[Int(i1)]
359-
buffer[outIndex &+ 1] = e1[Int(((i1 & 0x03) &<< 4) | ((i2 &>> 4) & 0x0F))]
360-
buffer[outIndex &+ 2] = e1[Int(((i2 & 0x0F) &<< 2) | ((i3 &>> 6) & 0x03))]
361-
buffer[outIndex &+ 3] = e1[Int(i3)]
362-
outIndex += 4
363-
}
354+
355+
self.loopEncode(e0, e1, input: input, from: 0, to: to, output: buffer, outIndex: &outIndex)
364356

365357
if to < input.count {
366358
let index = to
@@ -435,37 +427,35 @@ extension Base64 {
435427

436428
Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { e0, e1 in
437429
var outIndex = 0
438-
for lineInputIndex in stride(from: 0, to: lines * lineLength, by: lineLength) {
439-
for index in stride(from: lineInputIndex, to: lineInputIndex + lineLength, by: 3) {
440-
let i1 = input[index]
441-
let i2 = input[index + 1]
442-
let i3 = input[index + 2]
443-
buffer[outIndex] = e0[Int(i1)]
444-
buffer[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))]
445-
buffer[outIndex + 2] = e1[Int(((i2 & 0x0F) << 2) | ((i3 >> 6) & 0x03))]
446-
buffer[outIndex + 3] = e1[Int(i3)]
447-
outIndex += 4
448-
}
449430

431+
// first full line
432+
if input.count >= lineLength {
433+
self.loopEncode(e0, e1, input: input, from: 0, to: lineLength, output: buffer, outIndex: &outIndex)
434+
}
435+
436+
// following full lines
437+
for lineInputIndex in stride(from: lineLength, to: lines * lineLength, by: lineLength) {
450438
buffer[outIndex] = separatorByte1
451439
outIndex += 1
452440
if let separatorByte2 {
453441
buffer[outIndex] = separatorByte2
454442
outIndex += 1
455443
}
444+
445+
self.loopEncode(e0, e1, input: input, from: lineInputIndex, to: lineInputIndex + lineLength, output: buffer, outIndex: &outIndex)
456446
}
457447

458-
let to = input.count / 3 * 3
459-
for index in stride(from: lines * lineLength, to: to, by: 3) {
460-
let i1 = input[index]
461-
let i2 = input[index + 1]
462-
let i3 = input[index + 2]
463-
buffer[outIndex] = e0[Int(i1)]
464-
buffer[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))]
465-
buffer[outIndex + 2] = e1[Int(((i2 & 0x0F) << 2) | ((i3 >> 6) & 0x03))]
466-
buffer[outIndex + 3] = e1[Int(i3)]
467-
outIndex += 4
448+
// last line beginning
449+
if lines > 0 && lines * lineLength < input.count {
450+
buffer[outIndex] = separatorByte1
451+
outIndex += 1
452+
if let separatorByte2 {
453+
buffer[outIndex] = separatorByte2
454+
outIndex += 1
455+
}
468456
}
457+
let to = input.count / 3 * 3
458+
self.loopEncode(e0, e1, input: input, from: lines * lineLength, to: to, output: buffer, outIndex: &outIndex)
469459

470460
if to < input.count {
471461
let index = to
@@ -504,6 +494,27 @@ extension Base64 {
504494
}
505495
}
506496

497+
private static func loopEncode(
498+
_ e0: UnsafeBufferPointer<UInt8>,
499+
_ e1: UnsafeBufferPointer<UInt8>,
500+
input: UnsafeBufferPointer<UInt8>,
501+
from: Int,
502+
to: Int,
503+
output: UnsafeMutableBufferPointer<UInt8>,
504+
outIndex: inout Int
505+
) {
506+
for index in stride(from: from, to: to, by: 3) {
507+
let i1 = input[index]
508+
let i2 = input[index + 1]
509+
let i3 = input[index + 2]
510+
output[outIndex] = e0[Int(i1)]
511+
output[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))]
512+
output[outIndex + 2] = e1[Int(((i2 & 0x0F) << 2) | ((i3 >> 6) & 0x03))]
513+
output[outIndex + 3] = e1[Int(i3)]
514+
outIndex += 4
515+
}
516+
}
517+
507518
static func encodeComputeCapacity(bytes: Int, options: Data.Base64EncodingOptions) -> Int {
508519
let capacityWithoutBreaks = ((bytes + 2) / 3) * 4
509520

Tests/FoundationEssentialsTests/DataTests.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,36 @@ extension DataTests {
19021902
)
19031903
}
19041904

1905+
func test_base64Encode_DoesNotAddLineSeparatorsInLastLineWhenStringFitsInLine() {
1906+
XCTAssertEqual(
1907+
Data(repeating: 0, count: 48).base64EncodedString(options: .lineLength64Characters),
1908+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1909+
)
1910+
1911+
XCTAssertEqual(
1912+
Data(repeating: 0, count: 96).base64EncodedString(options: .lineLength64Characters),
1913+
"""
1914+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n\
1915+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
1916+
""",
1917+
)
1918+
print([UInt8](Data(repeating: 0, count: 96).base64EncodedString(options: .lineLength64Characters).utf8))
1919+
1920+
XCTAssertEqual(
1921+
Data(repeating: 0, count: 57).base64EncodedString(options: .lineLength76Characters),
1922+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
1923+
)
1924+
1925+
XCTAssertEqual(
1926+
Data(repeating: 0, count: 114).base64EncodedString(options: .lineLength76Characters),
1927+
"""
1928+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n\
1929+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
1930+
""",
1931+
)
1932+
1933+
}
1934+
19051935
func test_base64Decode_emptyString() {
19061936
XCTAssertEqual(Data(), Data(base64Encoded: ""))
19071937
}

0 commit comments

Comments
 (0)