|
26 | 26 | met: |
27 | 27 |
|
28 | 28 | 1. Redistributions of source code must retain the above copyright |
29 | | - notice, this list of conditions and the following disclaimer. |
| 29 | + notice, this list of conditions and the following disclaimer. |
30 | 30 |
|
31 | 31 | 2. Redistributions in binary form must reproduce the above copyright |
32 | | - notice, this list of conditions and the following disclaimer in the |
33 | | - documentation and/or other materials provided with the distribution. |
| 32 | + notice, this list of conditions and the following disclaimer in the |
| 33 | + documentation and/or other materials provided with the distribution. |
34 | 34 |
|
35 | 35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
36 | 36 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
70 | 70 | SOFTWARE. |
71 | 71 | */ |
72 | 72 |
|
73 | | -internal enum Base64 {} |
74 | | - |
75 | | -extension Base64 { |
76 | | - internal struct DecodingOptions: OptionSet { |
| 73 | +// swift-format-ignore: DontRepeatTypeInStaticProperties |
| 74 | +enum Base64 { |
| 75 | + struct DecodingOptions: OptionSet { |
77 | 76 | internal let rawValue: UInt |
78 | 77 | internal init(rawValue: UInt) { self.rawValue = rawValue } |
79 | 78 |
|
80 | 79 | internal static let base64UrlAlphabet = DecodingOptions(rawValue: UInt(1 << 0)) |
81 | 80 | internal static let omitPaddingCharacter = DecodingOptions(rawValue: UInt(1 << 1)) |
82 | 81 | } |
83 | 82 |
|
84 | | - internal enum DecodingError: Error, Equatable { |
| 83 | + enum DecodingError: Error, Equatable { |
85 | 84 | case invalidLength |
86 | 85 | case invalidCharacter(UInt8) |
87 | 86 | case unexpectedPaddingCharacter |
88 | 87 | case unexpectedEnd |
89 | 88 | } |
90 | 89 |
|
91 | | - internal static func decode( |
| 90 | + static func encode<Buffer: Collection>(bytes: Buffer) -> String where Buffer.Element == UInt8 { |
| 91 | + guard !bytes.isEmpty else { |
| 92 | + return "" |
| 93 | + } |
| 94 | + |
| 95 | + // In Base64, 3 bytes become 4 output characters, and we pad to the |
| 96 | + // nearest multiple of four. |
| 97 | + let base64StringLength = ((bytes.count + 2) / 3) * 4 |
| 98 | + let alphabet = Base64.encodeBase64 |
| 99 | + |
| 100 | + return String(customUnsafeUninitializedCapacity: base64StringLength) { backingStorage in |
| 101 | + var input = bytes.makeIterator() |
| 102 | + var offset = 0 |
| 103 | + while let firstByte = input.next() { |
| 104 | + let secondByte = input.next() |
| 105 | + let thirdByte = input.next() |
| 106 | + |
| 107 | + backingStorage[offset] = Base64.encode(alphabet: alphabet, firstByte: firstByte) |
| 108 | + backingStorage[offset + 1] = Base64.encode( |
| 109 | + alphabet: alphabet, |
| 110 | + firstByte: firstByte, |
| 111 | + secondByte: secondByte |
| 112 | + ) |
| 113 | + backingStorage[offset + 2] = Base64.encode( |
| 114 | + alphabet: alphabet, |
| 115 | + secondByte: secondByte, |
| 116 | + thirdByte: thirdByte |
| 117 | + ) |
| 118 | + backingStorage[offset + 3] = Base64.encode(alphabet: alphabet, thirdByte: thirdByte) |
| 119 | + offset += 4 |
| 120 | + } |
| 121 | + return offset |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + static func decode( |
92 | 126 | string encoded: String, |
93 | 127 | options: DecodingOptions = [] |
94 | 128 | ) throws -> [UInt8] { |
@@ -204,7 +238,7 @@ extension Base64 { |
204 | 238 | } |
205 | 239 | } |
206 | 240 |
|
207 | | - static func withUnsafeDecodingTablesAsBufferPointers<R>( |
| 241 | + private static func withUnsafeDecodingTablesAsBufferPointers<R>( |
208 | 242 | options: Base64.DecodingOptions, |
209 | 243 | _ body: ( |
210 | 244 | UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>, |
@@ -232,10 +266,64 @@ extension Base64 { |
232 | 266 | } |
233 | 267 | } |
234 | 268 |
|
235 | | - internal static let encodePaddingCharacter: UInt8 = 61 |
236 | | - static let badCharacter: UInt32 = 0x01FF_FFFF |
| 269 | + private static let encodePaddingCharacter: UInt8 = 61 |
| 270 | + |
| 271 | + private static let encodeBase64: [UInt8] = [ |
| 272 | + UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), |
| 273 | + UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), |
| 274 | + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), |
| 275 | + UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), |
| 276 | + UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), |
| 277 | + UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), |
| 278 | + UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), |
| 279 | + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), |
| 280 | + UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), |
| 281 | + UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), |
| 282 | + UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), |
| 283 | + UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), |
| 284 | + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), |
| 285 | + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), |
| 286 | + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), |
| 287 | + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), |
| 288 | + ] |
| 289 | + |
| 290 | + private static func encode(alphabet: [UInt8], firstByte: UInt8) -> UInt8 { |
| 291 | + let index = firstByte >> 2 |
| 292 | + return alphabet[Int(index)] |
| 293 | + } |
| 294 | + |
| 295 | + private static func encode(alphabet: [UInt8], firstByte: UInt8, secondByte: UInt8?) -> UInt8 { |
| 296 | + var index = (firstByte & 0b00000011) << 4 |
| 297 | + if let secondByte = secondByte { |
| 298 | + index += (secondByte & 0b11110000) >> 4 |
| 299 | + } |
| 300 | + return alphabet[Int(index)] |
| 301 | + } |
237 | 302 |
|
238 | | - static let decoding0: [UInt32] = [ |
| 303 | + private static func encode(alphabet: [UInt8], secondByte: UInt8?, thirdByte: UInt8?) -> UInt8 { |
| 304 | + guard let secondByte = secondByte else { |
| 305 | + // No second byte means we are just emitting padding. |
| 306 | + return Base64.encodePaddingCharacter |
| 307 | + } |
| 308 | + var index = (secondByte & 0b00001111) << 2 |
| 309 | + if let thirdByte = thirdByte { |
| 310 | + index += (thirdByte & 0b11000000) >> 6 |
| 311 | + } |
| 312 | + return alphabet[Int(index)] |
| 313 | + } |
| 314 | + |
| 315 | + private static func encode(alphabet: [UInt8], thirdByte: UInt8?) -> UInt8 { |
| 316 | + guard let thirdByte = thirdByte else { |
| 317 | + // No third byte means just padding. |
| 318 | + return Base64.encodePaddingCharacter |
| 319 | + } |
| 320 | + let index = thirdByte & 0b00111111 |
| 321 | + return alphabet[Int(index)] |
| 322 | + } |
| 323 | + |
| 324 | + private static let badCharacter: UInt32 = 0x01FF_FFFF |
| 325 | + |
| 326 | + private static let decoding0: [UInt32] = [ |
239 | 327 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
240 | 328 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
241 | 329 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
@@ -281,7 +369,7 @@ extension Base64 { |
281 | 369 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
282 | 370 | ] |
283 | 371 |
|
284 | | - static let decoding1: [UInt32] = [ |
| 372 | + private static let decoding1: [UInt32] = [ |
285 | 373 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
286 | 374 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
287 | 375 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
@@ -327,7 +415,7 @@ extension Base64 { |
327 | 415 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
328 | 416 | ] |
329 | 417 |
|
330 | | - static let decoding2: [UInt32] = [ |
| 418 | + private static let decoding2: [UInt32] = [ |
331 | 419 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
332 | 420 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
333 | 421 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
@@ -373,7 +461,7 @@ extension Base64 { |
373 | 461 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
374 | 462 | ] |
375 | 463 |
|
376 | | - static let decoding3: [UInt32] = [ |
| 464 | + private static let decoding3: [UInt32] = [ |
377 | 465 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
378 | 466 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
379 | 467 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
@@ -419,7 +507,7 @@ extension Base64 { |
419 | 507 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
420 | 508 | ] |
421 | 509 |
|
422 | | - static let decoding0url: [UInt32] = [ |
| 510 | + private static let decoding0url: [UInt32] = [ |
423 | 511 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 |
424 | 512 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 |
425 | 513 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 |
@@ -465,7 +553,7 @@ extension Base64 { |
465 | 553 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
466 | 554 | ] |
467 | 555 |
|
468 | | - static let decoding1url: [UInt32] = [ |
| 556 | + private static let decoding1url: [UInt32] = [ |
469 | 557 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 |
470 | 558 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 |
471 | 559 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 |
@@ -511,7 +599,7 @@ extension Base64 { |
511 | 599 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
512 | 600 | ] |
513 | 601 |
|
514 | | - static let decoding2url: [UInt32] = [ |
| 602 | + private static let decoding2url: [UInt32] = [ |
515 | 603 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 |
516 | 604 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 |
517 | 605 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 |
@@ -557,7 +645,7 @@ extension Base64 { |
557 | 645 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
558 | 646 | ] |
559 | 647 |
|
560 | | - static let decoding3url: [UInt32] = [ |
| 648 | + private static let decoding3url: [UInt32] = [ |
561 | 649 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 |
562 | 650 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 |
563 | 651 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 |
@@ -603,3 +691,42 @@ extension Base64 { |
603 | 691 | 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, |
604 | 692 | ] |
605 | 693 | } |
| 694 | + |
| 695 | +extension String { |
| 696 | + /// This is a backport of a proposed String initializer that will allow writing directly into an uninitialized String's backing memory. |
| 697 | + /// |
| 698 | + /// As this API does not exist prior to 5.3 on Linux, or on older Apple platforms, we fake it out with a pointer and accept the extra copy. |
| 699 | + init( |
| 700 | + backportUnsafeUninitializedCapacity capacity: Int, |
| 701 | + initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer<UInt8>) throws -> Int |
| 702 | + ) rethrows { |
| 703 | + |
| 704 | + // The buffer will store zero terminated C string |
| 705 | + let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: capacity + 1) |
| 706 | + defer { |
| 707 | + buffer.deallocate() |
| 708 | + } |
| 709 | + |
| 710 | + let initializedCount = try initializer(buffer) |
| 711 | + precondition(initializedCount <= capacity, "Overran buffer in initializer!") |
| 712 | + |
| 713 | + // add zero termination |
| 714 | + buffer[initializedCount] = 0 |
| 715 | + |
| 716 | + self = String(cString: buffer.baseAddress!) |
| 717 | + } |
| 718 | + |
| 719 | + init( |
| 720 | + customUnsafeUninitializedCapacity capacity: Int, |
| 721 | + initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer<UInt8>) throws -> Int |
| 722 | + ) rethrows { |
| 723 | + if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) { |
| 724 | + try self.init(unsafeUninitializedCapacity: capacity, initializingUTF8With: initializer) |
| 725 | + } else { |
| 726 | + try self.init( |
| 727 | + backportUnsafeUninitializedCapacity: capacity, |
| 728 | + initializingUTF8With: initializer |
| 729 | + ) |
| 730 | + } |
| 731 | + } |
| 732 | +} |
0 commit comments