From b8af49699d59ad065b801715a5009619100245ca Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Mon, 26 Oct 2020 14:15:10 +0100 Subject: [PATCH] Faster encoding and decoding thanks Chromium lookup tables (#19) --- .codecov.yml | 24 +- .github/workflows/ci.yaml | 32 +- .swiftformat | 13 + .../template/scaffolding.swift | 12 +- .../test_base64_decoding.swift | 2 +- .../test_base64_encoding.swift | 2 +- Package.swift | 20 +- README.md | 38 +- Sources/Base64Kit/Base64.swift | 326 +------- Sources/Base64Kit/Chromium.swift | 782 ++++++++++++++++++ Sources/Base64KitPerformanceTest/main.swift | 72 -- Sources/PerformanceTest/main.swift | 110 +++ .../Base64KitTests/Base64/DecodingTests.swift | 56 -- .../Base64KitTests/Base64/EncodingTests.swift | 28 - .../Base64/IntegrationTests.swift | 16 - Tests/Base64KitTests/ChromiumTests.swift | 101 +++ Tests/Base64KitTests/IntegrationTests.swift | 16 + scripts/{sanity.sh => validity.sh} | 0 18 files changed, 1077 insertions(+), 573 deletions(-) create mode 100644 .swiftformat create mode 100644 Sources/Base64Kit/Chromium.swift delete mode 100644 Sources/Base64KitPerformanceTest/main.swift create mode 100644 Sources/PerformanceTest/main.swift delete mode 100644 Tests/Base64KitTests/Base64/DecodingTests.swift delete mode 100644 Tests/Base64KitTests/Base64/EncodingTests.swift delete mode 100644 Tests/Base64KitTests/Base64/IntegrationTests.swift create mode 100644 Tests/Base64KitTests/ChromiumTests.swift create mode 100644 Tests/Base64KitTests/IntegrationTests.swift rename scripts/{sanity.sh => validity.sh} (100%) diff --git a/.codecov.yml b/.codecov.yml index b352797..a66a28b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,21 +1,3 @@ -coverage: - status: - project: - default: off - base64: - flags: base64 - target: 100% - unittest: - flags: unittest - performance: - flags: performance -flags: - base64: - paths: - - Sources/Base64 - performance: - paths: - - Sources/PerformanceTest - unittests: - paths: - - Tests/Base64Tests \ No newline at end of file +ignore: + - "Tests" + - ".build" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1ffbf43..40c4cd3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,15 +9,15 @@ on: jobs: - "sanity-Tests": + "validity-Tests": runs-on: macOS-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Install swiftformat run: brew install swiftformat - - name: Run sanity - run: ./scripts/sanity.sh . + - name: Run validity + run: ./scripts/validity.sh . "tuxOS-Tests": runs-on: ubuntu-latest @@ -26,8 +26,8 @@ jobs: images: - swift:5.1 - swift:5.2 - - swiftlang/swift:nightly-5.3-bionic - - swiftlang/swift:nightly-amazonlinux2 + - swift:5.3 + - swiftlang/swift:nightly-master container: image: ${{ matrix.images }} steps: @@ -52,30 +52,28 @@ jobs: images: - swift:5.1 - swift:5.2 - - swiftlang/swift:nightly-5.3-bionic - - swiftlang/swift:nightly-amazonlinux2 + - swift:5.3 + - swiftlang/swift:nightly-master container: image: ${{ matrix.images }} steps: - name: Checkout uses: actions/checkout@v2 - - name: Build - run: swift build -c release - - name: Run test - run: .build/release/Base64KitPerformanceTest + - name: Build & run + run: swift run -c release "tuxOS-Integration-Tests": runs-on: ubuntu-latest strategy: matrix: images: - - swift:5.2 - - swiftlang/swift:nightly-5.3 + - swift:5.3 + - swiftlang/swift:nightly-master container: image: ${{ matrix.images }} env: MAX_ALLOCS_ALLOWED_base64_decoding: 1000 - MAX_ALLOCS_ALLOWED_base64_encoding: 2000 + MAX_ALLOCS_ALLOWED_base64_encoding: 1000 steps: - name: Checkout uses: actions/checkout@v2 @@ -108,7 +106,5 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Build - run: swift build -c release - - name: Run test - run: .build/release/Base64KitPerformanceTest + - name: Build & run + run: swift run -c release diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..e23bb5c --- /dev/null +++ b/.swiftformat @@ -0,0 +1,13 @@ +# file options + +--swiftversion 5.3 +--exclude .build + +# format options + +--self insert +--patternlet inline +--stripunusedargs unnamed-only +--ifdef no-indent + +# rules diff --git a/IntegrationTests/allocation-counter-tests-framework/template/scaffolding.swift b/IntegrationTests/allocation-counter-tests-framework/template/scaffolding.swift index 4c95a0f..44ed699 100644 --- a/IntegrationTests/allocation-counter-tests-framework/template/scaffolding.swift +++ b/IntegrationTests/allocation-counter-tests-framework/template/scaffolding.swift @@ -15,9 +15,9 @@ import AtomicCounter import Foundation #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - import Darwin +import Darwin #else - import Glibc +import Glibc #endif func measureAll(_ fn: () -> Int) -> [[String: Int]] { @@ -26,11 +26,11 @@ func measureAll(_ fn: () -> Int) -> [[String: Int]] { AtomicCounter.reset_malloc_counter() AtomicCounter.reset_malloc_bytes_counter() #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - autoreleasepool { - _ = fn() - } - #else + autoreleasepool { _ = fn() + } + #else + _ = fn() #endif usleep(100_000) // allocs/frees happen on multiple threads, allow some cool down time let frees = AtomicCounter.read_free_counter() diff --git a/IntegrationTests/tests_04_performance/test_01_resources/test_base64_decoding.swift b/IntegrationTests/tests_04_performance/test_01_resources/test_base64_decoding.swift index 766b4cc..90d737c 100644 --- a/IntegrationTests/tests_04_performance/test_01_resources/test_base64_decoding.swift +++ b/IntegrationTests/tests_04_performance/test_01_resources/test_base64_decoding.swift @@ -6,7 +6,7 @@ func run(identifier: String) { measure(identifier: identifier) { for _ in 0 ..< 1000 { - bytes = try! base64.base64decoded() + bytes = try! Base64.decode(string: base64) } return bytes?.count ?? 0 diff --git a/IntegrationTests/tests_04_performance/test_01_resources/test_base64_encoding.swift b/IntegrationTests/tests_04_performance/test_01_resources/test_base64_encoding.swift index 830edcc..fafeefb 100644 --- a/IntegrationTests/tests_04_performance/test_01_resources/test_base64_encoding.swift +++ b/IntegrationTests/tests_04_performance/test_01_resources/test_base64_encoding.swift @@ -6,7 +6,7 @@ func run(identifier: String) { measure(identifier: identifier) { for _ in 0 ..< 1000 { - base64 = String(base64Encoding: bytes) + base64 = Base64.encodeString(bytes: bytes) } return base64?.count ?? 0 diff --git a/Package.swift b/Package.swift index 0683b50..c59f4dc 100644 --- a/Package.swift +++ b/Package.swift @@ -6,24 +6,12 @@ import PackageDescription let package = Package( name: "swift-base64-kit", products: [ - .library( - name: "Base64Kit", - targets: ["Base64Kit"] - ), + .library(name: "Base64Kit", targets: ["Base64Kit"]), ], dependencies: [], targets: [ - .target( - name: "Base64KitPerformanceTest", - dependencies: ["Base64Kit"] - ), - .target( - name: "Base64Kit", - dependencies: [] - ), - .testTarget( - name: "Base64KitTests", - dependencies: ["Base64Kit"] - ), + .target(name: "Base64Kit", dependencies: []), + .target(name: "PerformanceTest", dependencies: ["Base64Kit"]), + .testTarget(name: "Base64KitTests", dependencies: ["Base64Kit"]), ] ) diff --git a/README.md b/README.md index eebfb55..87d4f8c 100644 --- a/README.md +++ b/README.md @@ -2,63 +2,56 @@ [![Swift 5.1](https://img.shields.io/badge/Swift-5.1-blue.svg)](https://swift.org/download/) [![github-actions](https://github.com/fabianfett/swift-base64-kit/workflows/CI/badge.svg)](https://github.com/fabianfett/swift-base64-kit/actions) -[![codecov](https://codecov.io/gh/fabianfett/swift-base64-kit/branch/master/graph/badge.svg)](https://codecov.io/gh/fabianfett/swift-base64) +[![codecov](https://codecov.io/gh/fabianfett/swift-base64-kit/branch/main/graph/badge.svg)](https://codecov.io/gh/fabianfett/swift-base64) ![macOS](https://img.shields.io/badge/os-macOS-green.svg?style=flat) ![tuxOS](https://img.shields.io/badge/os-tuxOS-green.svg?style=flat) -This package provides a base64 encoder and decoder in pure Swift (without the use of Foundation). The implementation is [RFC4648](https://tools.ietf.org/html/rfc4648) complient. +This package provides a base64 encoder and decoder in Swift without the use of Foundation. The implementation is [RFC4648](https://tools.ietf.org/html/rfc4648) complient and is faster than the Foundation base64 implementation. -Today the implementation is rather simple. No fancy precomputed lookup tables, no fancy SIMD instructions. Therefore, there is definitely room for improvement performance-wise. See also [Literature for a faster algorithm](#user-content-literature-for-a-faster-algorithm). - -Everything began with [an issue](https://github.com/apple/swift-nio/issues/1265) on [`swift-nio`](https://github.com/apple/swift-nio). +To achieve performance the implementation uses [Chromium precomputed lookup tables](https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c) and makes heavy use of unsafe swift API. When Swift has better support for SIMD instructions this might be an area worth exploring. ## Status - [x] support for base64 and base64url - [x] faster than Foundation +- [x] padding can be omitted - [ ] decoding can ignore line breaks - [ ] encoding can insert line breaks -This package's encoding implementation [is used in `swift-nio`'s websocket implementation](https://github.com/apple/swift-nio/blob/master/Sources/NIOWebSocket/Base64.swift). +A former implementation of this package [is used in `swift-nio`'s websocket implementation](https://github.com/apple/swift-nio/blob/main/Sources/NIOWebSocket/Base64.swift). ## Performance -Super [simple performance test](https://github.com/fabianfett/swift-base64-kit/blob/master/Sources/Base64KitPerformanceTest/main.swift) +Super [simple performance test](https://github.com/fabianfett/swift-base64-kit/blob/main/Sources/Base64KitPerformanceTest/main.swift) to ensure speediness of this implementation. Encoding and decoding 1m times the base64 string: ``` AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w== ``` -#### macOS +Tests were run on a MacBook Pro (16-inch, late 2019). Processor: 2.4 GHz 8-Core Intel Core i9. -MacBook Pro (15-inch, late 2016 - the first one with the butterfly keyboard). -Quad Core 2.7 GHz Intel Core i7 +#### macOS - swift 5.3 | | Encoding | Decoding | |:--|:--|:--| -| Foundation | 2.21s | 2.28s | -| swift-base64-kit | 1.01s | 1.06s | -| Speedup | 2.18x | 2.14x | - -#### linux +| Foundation | 2.08s | 2.15s | +| swift-base64-kit | 0.66s | 0.54s | +| Speedup | 3x | 4x | -Whatevar runs GitHub Actions 😉 +#### Linux - swift 5.3 | | Encoding | Decoding | |:--|:--|:--| -| Foundation | 33.64s | 3.49s | -| swift-base64-kit | 1.07s | 1.27s | -| Speedup | **31.18x** | 2.74x | - -I have no idea why Foundation base64 encoding is so slow on linux. 🤷‍♂️ +| Foundation | 1.01s | 5.5s | +| swift-base64-kit | 0.27s | 0.41s | +| Speedup | 3x | **~10x** | ## Literature for a faster algorithm I would really like to speed up this project further to be way faster than it is today. Some food for thought of how this could be tackled can be found here: -- [Chromium precomputed lookup tables](https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c) - [Wojciech Muła, Daniel Lemire: Faster Base64 Encoding and Decoding using AVX2 Instructions](https://arxiv.org/pdf/1704.00605.pdf). - [Daniel Lemire's blog - Ridiculously fast base64 encoding and decoding](https://lemire.me/blog/2018/01/17/ridiculously-fast-base64-encoding-and-decoding/) - [Swift SIMD support](https://github.com/apple/swift-evolution/blob/master/proposals/0229-simd.md) @@ -68,4 +61,3 @@ I would really like to speed up this project further to be way faster than it is As of today (2019-12-10), the author is aware of only one alternative that offers merely encoding. - [SwiftyBase64](https://github.com/drichardson/SwiftyBase64) - diff --git a/Sources/Base64Kit/Base64.swift b/Sources/Base64Kit/Base64.swift index 98b4ccf..3013c45 100644 --- a/Sources/Base64Kit/Base64.swift +++ b/Sources/Base64Kit/Base64.swift @@ -1,337 +1,33 @@ -public struct Base64 {} - -// MARK: Encoding - -extension Base64 { +public enum Base64 { public struct EncodingOptions: OptionSet { public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } public static let base64UrlAlphabet = EncodingOptions(rawValue: UInt(1 << 0)) + public static let omitPaddingCharacter = EncodingOptions(rawValue: UInt(1 << 1)) } - /// Base64 encode a collection of UInt8 to a string, without the use of Foundation. - /// - /// This function performs the world's most naive Base64 encoding: no attempts to use a larger - /// lookup table or anything intelligent like that, just shifts and masks. This works fine, for - /// now: the purpose of this encoding is to avoid round-tripping through Data, and the perf gain - /// from avoiding that is more than enough to outweigh the silliness of this code. - @inlinable - public static func encode(bytes: Buffer, options: EncodingOptions = []) - -> String where Buffer.Element == UInt8 { - // In Base64, 3 bytes become 4 output characters, and we pad to the - // nearest multiple of four. - let newCapacity = ((bytes.count + 2) / 3) * 4 - let alphabet = options.contains(.base64UrlAlphabet) - ? Base64.encodeBase64Url - : Base64.encodeBase64 - - var outputBytes = [UInt8]() - outputBytes.reserveCapacity(newCapacity) - - var input = bytes.makeIterator() - - while let firstByte = input.next() { - let secondByte = input.next() - let thirdByte = input.next() - - let firstChar = Base64.encode(alphabet: alphabet, firstByte: firstByte) - let secondChar = Base64.encode(alphabet: alphabet, firstByte: firstByte, secondByte: secondByte) - let thirdChar = Base64.encode(alphabet: alphabet, secondByte: secondByte, thirdByte: thirdByte) - let forthChar = Base64.encode(alphabet: alphabet, thirdByte: thirdByte) - - outputBytes.append(firstChar) - outputBytes.append(secondChar) - outputBytes.append(thirdChar) - outputBytes.append(forthChar) - } - - return String(decoding: outputBytes, as: Unicode.UTF8.self) - } - - // MARK: Internal - - // The base64 unicode table. - @usableFromInline - static let encodeBase64: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), - UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), - UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), - UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), - UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), - UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), - UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), - UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), - UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), - UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), - UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), - UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), - UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), - ] - - @usableFromInline - static let encodeBase64Url: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), - UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), - UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), - UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), - UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), - UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), - UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), - UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), - UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), - UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), - UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), - UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), - UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), - ] - - @usableFromInline - static let encodePaddingCharacter: UInt8 = 61 - - @inlinable - static func encode(alphabet: [UInt8], firstByte: UInt8) -> UInt8 { - let index = firstByte >> 2 - return alphabet[Int(index)] - } - - @inlinable - static func encode(alphabet: [UInt8], firstByte: UInt8, secondByte: UInt8?) -> UInt8 { - var index = (firstByte & 0b0000_0011) << 4 - if let secondByte = secondByte { - index += (secondByte & 0b1111_0000) >> 4 - } - return alphabet[Int(index)] - } - - @inlinable - static func encode(alphabet: [UInt8], secondByte: UInt8?, thirdByte: UInt8?) -> UInt8 { - guard let secondByte = secondByte else { - // No second byte means we are just emitting padding. - return Base64.encodePaddingCharacter - } - var index = (secondByte & 0b0000_1111) << 2 - if let thirdByte = thirdByte { - index += (thirdByte & 0b1100_0000) >> 6 - } - return alphabet[Int(index)] - } - - @inlinable - static func encode(alphabet: [UInt8], thirdByte: UInt8?) -> UInt8 { - guard let thirdByte = thirdByte else { - // No third byte means just padding. - return Base64.encodePaddingCharacter - } - let index = thirdByte & 0b0011_1111 - return alphabet[Int(index)] - } -} - -// MARK: - Decode - - -extension Base64 { public struct DecodingOptions: OptionSet { public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } public static let base64UrlAlphabet = DecodingOptions(rawValue: UInt(1 << 0)) - } - - @inlinable - public static func decode(encoded: Buffer, options: DecodingOptions = []) - throws -> [UInt8] where Buffer.Element == UInt8 { - let alphabet = options.contains(.base64UrlAlphabet) - ? Base64.decodeBase64Url - : Base64.decodeBase64 - - // In Base64 4 encoded bytes, become 3 decoded bytes. We pad to the - // nearest multiple of three. - let inputLength = encoded.count - guard inputLength > 0 else { return [] } - guard inputLength % 4 == 0 else { - throw DecodingError.invalidLength - } - - let inputBlocks = (inputLength + 3) / 4 - let fullQualified = inputBlocks - 1 - let outputLength = ((encoded.count + 3) / 4) * 3 - var iterator = encoded.makeIterator() - var outputBytes = [UInt8]() - outputBytes.reserveCapacity(outputLength) - - // fast loop. we don't expect any padding in here. - for _ in 0 ..< fullQualified { - let firstValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - let secondValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - let thirdValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - let forthValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - - outputBytes.append((firstValue << 2) | (secondValue >> 4)) - outputBytes.append((secondValue << 4) | (thirdValue >> 2)) - outputBytes.append((thirdValue << 6) | forthValue) - } - - // last 4 bytes. we expect padding characters in three and four - let firstValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - let secondValue: UInt8 = try iterator.nextValue(alphabet: alphabet) - let thirdValue: UInt8? = try iterator.nextValueOrEmpty(alphabet: alphabet) - let forthValue: UInt8? = try iterator.nextValueOrEmpty(alphabet: alphabet) - - outputBytes.append((firstValue << 2) | (secondValue >> 4)) - if let thirdValue = thirdValue { - outputBytes.append((secondValue << 4) | (thirdValue >> 2)) - - if let forthValue = forthValue { - outputBytes.append((thirdValue << 6) | forthValue) - } - } - - return outputBytes - } - - @inlinable - public static func decode(encoded: String, options: DecodingOptions = []) throws -> [UInt8] { - // A string can be backed by a contiguous storage (pure swift string) - // or a nsstring (bridged string from objc). We only get a pointer - // to the contiguous storage, if the input string is a swift string. - // Therefore to transform the nsstring backed input into a swift - // string we concat the input with nothing, causing a copy on write - // into a swift string. - let decoded = try encoded.utf8.withContiguousStorageIfAvailable { pointer in - try self.decode(encoded: pointer, options: options) - } - - if decoded != nil { - return decoded! - } - - return try decode(encoded: encoded + "", options: options) - } - - // MARK: Internal - - // swiftformat:disable consecutiveSpaces - @usableFromInline - static let decodeBase64: [UInt8] = [ - // 0 1 2 3 4 5 6 7 8 9 - /* 0 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 1 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 2 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 3 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 4 */ 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, - /* 5 */ 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - /* 6 */ 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, - /* 7 */ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 8 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - /* 9 */ 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, - /* 10 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - /* 11 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - /* 12 */ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, - /* 13 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 14 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 15 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 16 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 17 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 18 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 19 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 20 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 21 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 22 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 23 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 24 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 25 */ 255, 255, 255, 255, 255, - ] - - @usableFromInline - static let decodeBase64Url: [UInt8] = [ - // 0 1 2 3 4 5 6 7 8 9 - /* 0 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 1 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 2 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 3 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 4 */ 255, 255, 255, 255, 255, 62, 255, 255, 52, 53, - /* 5 */ 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - /* 6 */ 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, - /* 7 */ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 8 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - /* 9 */ 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, - /* 10 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - /* 11 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - /* 12 */ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, - /* 13 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 14 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 15 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 16 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 17 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 18 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 19 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 20 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 21 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 22 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 23 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 24 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* 25 */ 255, 255, 255, 255, 255, - ] - // swiftformat:enable consecutiveSpaces - - @usableFromInline - static let paddingCharacter: UInt8 = 254 -} - -extension IteratorProtocol where Self.Element == UInt8 { - @inlinable mutating func nextValue(alphabet: [UInt8]) throws -> UInt8 { - let ascii = next()! - - let value = alphabet[Int(ascii)] - - if value < 64 { - return value - } - - if value == Base64.paddingCharacter { - throw DecodingError.unexpectedPaddingCharacter - } - - throw DecodingError.invalidCharacter(ascii) - } - - @inlinable mutating func nextValueOrEmpty(alphabet: [UInt8]) throws -> UInt8? { - let ascii = next()! - - let value = alphabet[Int(ascii)] - - if value < 64 { - return value - } - - if value == Base64.paddingCharacter { - return nil - } - - throw DecodingError.invalidCharacter(ascii) + public static let omitPaddingCharacter = DecodingOptions(rawValue: UInt(1 << 1)) } } -// MARK: - Extensions - +//// MARK: - Extensions - -extension String { +public extension String { @inlinable - public init(base64Encoding bytes: Buffer, options: Base64.EncodingOptions = []) - where Buffer.Element == UInt8 { - self = Base64.encode(bytes: bytes, options: options) + init(base64Encoding bytes: Buffer, options: Base64.EncodingOptions = []) + where Buffer.Element == UInt8 + { + self = Base64.encodeString(bytes: bytes, options: options) } - public func base64decoded(options: Base64.DecodingOptions = []) throws -> [UInt8] { - // In Base64, 3 bytes become 4 output characters, and we pad to the nearest multiple - // of four. - return try Base64.decode(encoded: self, options: options) + func base64decoded(options: Base64.DecodingOptions = []) throws -> [UInt8] { + try Base64.decode(string: self, options: options) } } diff --git a/Sources/Base64Kit/Chromium.swift b/Sources/Base64Kit/Chromium.swift new file mode 100644 index 0000000..e099285 --- /dev/null +++ b/Sources/Base64Kit/Chromium.swift @@ -0,0 +1,782 @@ + +// https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c + +// MARK: - Encoding - + +extension Base64 { + @usableFromInline + internal static let encodePaddingCharacter: UInt8 = 61 + + @usableFromInline + static let encoding0: [UInt8] = [ + UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), + UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), + UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), + UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), + UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), + UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), + UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), + UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), + UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), + UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), + UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), + UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), + UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "+"), + UInt8(ascii: "+"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "/"), UInt8(ascii: "/"), UInt8(ascii: "/"), + ] + + @usableFromInline + static let encoding1: [UInt8] = [ + UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), + UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), + UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), + UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), + UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), + UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), + ] + + @usableFromInline + static let encoding0url: [UInt8] = [ + UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), + UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), + UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), + UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), + UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), + UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), + UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), + UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), + UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), + UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), + UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), + UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), + UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "-"), + UInt8(ascii: "-"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "_"), UInt8(ascii: "_"), UInt8(ascii: "_"), + ] + + @usableFromInline + static let encoding1url: [UInt8] = [ + UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), + UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), + UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), + UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), + UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), + UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), + ] + + @inlinable + public static func encodeBytes(bytes: Buffer, options: EncodingOptions = []) + -> [UInt8] where Buffer.Element == UInt8 + { + let newCapacity = ((bytes.count + 2) / 3) * 4 + + if let result = bytes.withContiguousStorageIfAvailable({ (input) -> [UInt8] in + [UInt8](unsafeUninitializedCapacity: newCapacity) { buffer, length in + Self._encodeChromium(input: input, buffer: buffer, length: &length, options: options) + } + }) { + return result + } + + return self.encodeBytes(bytes: Array(bytes), options: options) + } + + @inlinable + public static func encodeString(bytes: Buffer, options: EncodingOptions = []) + -> String where Buffer.Element == UInt8 + { + let newCapacity = ((bytes.count + 2) / 3) * 4 + + #if swift(>=5.3) + if #available(OSX 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) { + if let result = bytes.withContiguousStorageIfAvailable({ (input) -> String in + String(unsafeUninitializedCapacity: newCapacity) { (buffer) -> Int in + var length = newCapacity + Self._encodeChromium(input: input, buffer: buffer, length: &length, options: options) + return length + } + }) { + return result + } + + return self.encodeString(bytes: Array(bytes), options: options) + } else { + let bytes: [UInt8] = self.encodeBytes(bytes: bytes, options: options) + return String(decoding: bytes, as: Unicode.UTF8.self) + } + #else + let bytes: [UInt8] = self.encodeBytes(bytes: bytes, options: options) + return String(decoding: bytes, as: Unicode.UTF8.self) + #endif + } + + @inlinable + static func _encodeChromium(input: UnsafeBufferPointer, buffer: UnsafeMutableBufferPointer, length: inout Int, options: EncodingOptions) { + let omitPaddingCharacter = options.contains(.omitPaddingCharacter) + + Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { e0, e1 in + let to = input.count / 3 * 3 + var outIndex = 0 + for index in stride(from: 0, to: to, by: 3) { + let i1 = input[index] + let i2 = input[index + 1] + let i3 = input[index + 2] + buffer[outIndex] = e0[Int(i1)] + buffer[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))] + buffer[outIndex + 2] = e1[Int(((i2 & 0x0F) << 2) | ((i3 >> 6) & 0x03))] + buffer[outIndex + 3] = e1[Int(i3)] + outIndex += 4 + } + + if to < input.count { + let index = to + + let i1 = input[index] + let i2 = index + 1 < input.count ? input[index + 1] : nil + let i3 = index + 2 < input.count ? input[index + 2] : nil + + buffer[outIndex] = e0[Int(i1)] + + if let i2 = i2, let i3 = i3 { + buffer[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))] + buffer[outIndex + 2] = e1[Int(((i2 & 0x0F) << 2) | ((i3 >> 6) & 0x03))] + buffer[outIndex + 3] = e1[Int(i3)] + outIndex += 4 + } else if let i2 = i2 { + buffer[outIndex + 1] = e1[Int(((i1 & 0x03) << 4) | ((i2 >> 4) & 0x0F))] + buffer[outIndex + 2] = e1[Int((i2 & 0x0F) << 2)] + outIndex += 3 + if !omitPaddingCharacter { + buffer[outIndex] = Self.encodePaddingCharacter + outIndex += 1 + } + } else { + buffer[outIndex + 1] = e1[Int((i1 & 0x03) << 4)] + outIndex += 2 + if !omitPaddingCharacter { + buffer[outIndex] = Self.encodePaddingCharacter + buffer[outIndex + 1] = Self.encodePaddingCharacter + outIndex += 2 + } + } + } + + length = outIndex + } + } + + @inlinable + static func withUnsafeEncodingTablesAsBufferPointers(options: Base64.EncodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer) throws -> R) rethrows -> R { + let encoding0 = options.contains(.base64UrlAlphabet) ? Self.encoding0url : Self.encoding0 + let encoding1 = options.contains(.base64UrlAlphabet) ? Self.encoding1url : Self.encoding1 + + assert(encoding0.count == 256) + assert(encoding1.count == 256) + + return try encoding0.withUnsafeBufferPointer { (e0) -> R in + try encoding1.withUnsafeBufferPointer { (e1) -> R in + try body(e0, e1) + } + } + } +} + +// MARK: - Decoding - + +extension Base64 { + @inlinable + public static func decode(string encoded: String, options: DecodingOptions = []) throws -> [UInt8] { + let decoded = try encoded.utf8.withContiguousStorageIfAvailable { (characterPointer) -> [UInt8] in + guard characterPointer.count > 0 else { + return [] + } + + let outputLength = ((characterPointer.count + 3) / 4) * 3 + + return try characterPointer.withMemoryRebound(to: UInt8.self) { (input) -> [UInt8] in + try [UInt8](unsafeUninitializedCapacity: outputLength) { output, length in + try Self._decodeChromium(from: input, into: output, length: &length, options: options) + } + } + } + + if decoded != nil { + return decoded! + } + + var encoded = encoded + encoded.makeContiguousUTF8() + return try Self.decode(string: encoded, options: options) + } + + @inlinable + public static func decode(bytes: Buffer, options: DecodingOptions = []) throws -> [UInt8] where Buffer.Element == UInt8 { + let decoded = try bytes.withContiguousStorageIfAvailable { (input) -> [UInt8] in + let outputLength = ((input.count + 3) / 4) * 3 + + return try [UInt8](unsafeUninitializedCapacity: outputLength) { output, length in + try Self._decodeChromium(from: input, into: output, length: &length, options: options) + } + } + + if decoded != nil { + return decoded! + } + + return try self.decode(bytes: Array(bytes), options: options) + } + + @inlinable + static func _decodeChromium( + from inBuffer: UnsafeBufferPointer, + into outBuffer: UnsafeMutableBufferPointer, + length: inout Int, + options: DecodingOptions = [] + ) throws { + let remaining = inBuffer.count % 4 + switch (options.contains(.omitPaddingCharacter), remaining) { + case (false, 1...): + throw DecodingError.invalidLength + case (true, 1): + throw DecodingError.invalidLength + default: + // everythin alright so far + break + } + + let outputLength = ((inBuffer.count + 3) / 4) * 3 + let fullchunks = remaining == 0 ? inBuffer.count / 4 - 1 : inBuffer.count / 4 + guard outBuffer.count >= outputLength else { + preconditionFailure("Expected the out buffer to be at least as long as outputLength") + } + + try Self.withUnsafeDecodingTablesAsBufferPointers(options: options) { d0, d1, d2, d3 in + var outIndex = 0 + if fullchunks > 0 { + for chunk in 0 ..< fullchunks { + let inIndex = chunk * 4 + let a0 = inBuffer[inIndex] + let a1 = inBuffer[inIndex + 1] + let a2 = inBuffer[inIndex + 2] + let a3 = inBuffer[inIndex + 3] + var x: UInt32 = d0[Int(a0)] | d1[Int(a1)] | d2[Int(a2)] | d3[Int(a3)] + + if x >= Self.badCharacter { + // TODO: Inspect characters here better + throw DecodingError.invalidCharacter(inBuffer[inIndex]) + } + + withUnsafePointer(to: &x) { ptr in + ptr.withMemoryRebound(to: UInt8.self, capacity: 4) { newPtr in + outBuffer[outIndex] = newPtr[0] + outBuffer[outIndex + 1] = newPtr[1] + outBuffer[outIndex + 2] = newPtr[2] + outIndex += 3 + } + } + } + } + + // inIndex is the first index in the last chunk + let inIndex = fullchunks > 1 ? fullchunks * 4 : 0 + let a0 = inBuffer[inIndex] + let a1 = inBuffer[inIndex + 1] + var a2: UInt8? + var a3: UInt8? + if inIndex + 2 < inBuffer.count, inBuffer[inIndex + 2] != Self.encodePaddingCharacter { + a2 = inBuffer[inIndex + 2] + } + if inIndex + 3 < inBuffer.count, inBuffer[inIndex + 3] != Self.encodePaddingCharacter { + a3 = inBuffer[inIndex + 3] + } + + var x: UInt32 = d0[Int(a0)] | d1[Int(a1)] | d2[Int(a2 ?? 65)] | d3[Int(a3 ?? 65)] + if x >= Self.badCharacter { + // TODO: Inspect characters here better + throw DecodingError.invalidCharacter(inBuffer[inIndex]) + } + + withUnsafePointer(to: &x) { ptr in + ptr.withMemoryRebound(to: UInt8.self, capacity: 4) { newPtr in + outBuffer[outIndex] = newPtr[0] + outIndex += 1 + if a2 != nil { + outBuffer[outIndex] = newPtr[1] + outIndex += 1 + } + if a3 != nil { + outBuffer[outIndex] = newPtr[2] + outIndex += 1 + } + } + } + + length = outIndex + } + } + + @usableFromInline + static func withUnsafeDecodingTablesAsBufferPointers(options: Base64.DecodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer) throws -> R) rethrows -> R { + let decoding0 = options.contains(.base64UrlAlphabet) ? Self.decoding0url : Self.decoding0 + let decoding1 = options.contains(.base64UrlAlphabet) ? Self.decoding1url : Self.decoding1 + let decoding2 = options.contains(.base64UrlAlphabet) ? Self.decoding2url : Self.decoding2 + let decoding3 = options.contains(.base64UrlAlphabet) ? Self.decoding3url : Self.decoding3 + + assert(decoding0.count == 256) + assert(decoding1.count == 256) + assert(decoding2.count == 256) + assert(decoding3.count == 256) + + return try decoding0.withUnsafeBufferPointer { (d0) -> R in + try decoding1.withUnsafeBufferPointer { (d1) -> R in + try decoding2.withUnsafeBufferPointer { (d2) -> R in + try decoding3.withUnsafeBufferPointer { (d3) -> R in + try body(d0, d1, d2, d3) + } + } + } + } + } + + @usableFromInline + static let badCharacter: UInt32 = 0x01FF_FFFF + + @usableFromInline + static let decoding0: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0000_00F8, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00FC, + 0x0000_00D0, 0x0000_00D4, 0x0000_00D8, 0x0000_00DC, 0x0000_00E0, 0x0000_00E4, + 0x0000_00E8, 0x0000_00EC, 0x0000_00F0, 0x0000_00F4, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, + 0x0000_0004, 0x0000_0008, 0x0000_000C, 0x0000_0010, 0x0000_0014, 0x0000_0018, + 0x0000_001C, 0x0000_0020, 0x0000_0024, 0x0000_0028, 0x0000_002C, 0x0000_0030, + 0x0000_0034, 0x0000_0038, 0x0000_003C, 0x0000_0040, 0x0000_0044, 0x0000_0048, + 0x0000_004C, 0x0000_0050, 0x0000_0054, 0x0000_0058, 0x0000_005C, 0x0000_0060, + 0x0000_0064, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0000_0068, 0x0000_006C, 0x0000_0070, 0x0000_0074, 0x0000_0078, + 0x0000_007C, 0x0000_0080, 0x0000_0084, 0x0000_0088, 0x0000_008C, 0x0000_0090, + 0x0000_0094, 0x0000_0098, 0x0000_009C, 0x0000_00A0, 0x0000_00A4, 0x0000_00A8, + 0x0000_00AC, 0x0000_00B0, 0x0000_00B4, 0x0000_00B8, 0x0000_00BC, 0x0000_00C0, + 0x0000_00C4, 0x0000_00C8, 0x0000_00CC, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding1: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0000_E003, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_F003, + 0x0000_4003, 0x0000_5003, 0x0000_6003, 0x0000_7003, 0x0000_8003, 0x0000_9003, + 0x0000_A003, 0x0000_B003, 0x0000_C003, 0x0000_D003, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, + 0x0000_1000, 0x0000_2000, 0x0000_3000, 0x0000_4000, 0x0000_5000, 0x0000_6000, + 0x0000_7000, 0x0000_8000, 0x0000_9000, 0x0000_A000, 0x0000_B000, 0x0000_C000, + 0x0000_D000, 0x0000_E000, 0x0000_F000, 0x0000_0001, 0x0000_1001, 0x0000_2001, + 0x0000_3001, 0x0000_4001, 0x0000_5001, 0x0000_6001, 0x0000_7001, 0x0000_8001, + 0x0000_9001, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0000_A001, 0x0000_B001, 0x0000_C001, 0x0000_D001, 0x0000_E001, + 0x0000_F001, 0x0000_0002, 0x0000_1002, 0x0000_2002, 0x0000_3002, 0x0000_4002, + 0x0000_5002, 0x0000_6002, 0x0000_7002, 0x0000_8002, 0x0000_9002, 0x0000_A002, + 0x0000_B002, 0x0000_C002, 0x0000_D002, 0x0000_E002, 0x0000_F002, 0x0000_0003, + 0x0000_1003, 0x0000_2003, 0x0000_3003, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding2: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0080_0F00, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x00C0_0F00, + 0x0000_0D00, 0x0040_0D00, 0x0080_0D00, 0x00C0_0D00, 0x0000_0E00, 0x0040_0E00, + 0x0080_0E00, 0x00C0_0E00, 0x0000_0F00, 0x0040_0F00, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, + 0x0040_0000, 0x0080_0000, 0x00C0_0000, 0x0000_0100, 0x0040_0100, 0x0080_0100, + 0x00C0_0100, 0x0000_0200, 0x0040_0200, 0x0080_0200, 0x00C0_0200, 0x0000_0300, + 0x0040_0300, 0x0080_0300, 0x00C0_0300, 0x0000_0400, 0x0040_0400, 0x0080_0400, + 0x00C0_0400, 0x0000_0500, 0x0040_0500, 0x0080_0500, 0x00C0_0500, 0x0000_0600, + 0x0040_0600, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x0080_0600, 0x00C0_0600, 0x0000_0700, 0x0040_0700, 0x0080_0700, + 0x00C0_0700, 0x0000_0800, 0x0040_0800, 0x0080_0800, 0x00C0_0800, 0x0000_0900, + 0x0040_0900, 0x0080_0900, 0x00C0_0900, 0x0000_0A00, 0x0040_0A00, 0x0080_0A00, + 0x00C0_0A00, 0x0000_0B00, 0x0040_0B00, 0x0080_0B00, 0x00C0_0B00, 0x0000_0C00, + 0x0040_0C00, 0x0080_0C00, 0x00C0_0C00, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding3: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x003E_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003F_0000, + 0x0034_0000, 0x0035_0000, 0x0036_0000, 0x0037_0000, 0x0038_0000, 0x0039_0000, + 0x003A_0000, 0x003B_0000, 0x003C_0000, 0x003D_0000, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, + 0x0001_0000, 0x0002_0000, 0x0003_0000, 0x0004_0000, 0x0005_0000, 0x0006_0000, + 0x0007_0000, 0x0008_0000, 0x0009_0000, 0x000A_0000, 0x000B_0000, 0x000C_0000, + 0x000D_0000, 0x000E_0000, 0x000F_0000, 0x0010_0000, 0x0011_0000, 0x0012_0000, + 0x0013_0000, 0x0014_0000, 0x0015_0000, 0x0016_0000, 0x0017_0000, 0x0018_0000, + 0x0019_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x001A_0000, 0x001B_0000, 0x001C_0000, 0x001D_0000, 0x001E_0000, + 0x001F_0000, 0x0020_0000, 0x0021_0000, 0x0022_0000, 0x0023_0000, 0x0024_0000, + 0x0025_0000, 0x0026_0000, 0x0027_0000, 0x0028_0000, 0x0029_0000, 0x002A_0000, + 0x002B_0000, 0x002C_0000, 0x002D_0000, 0x002E_0000, 0x002F_0000, 0x0030_0000, + 0x0031_0000, 0x0032_0000, 0x0033_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding0url: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00F8, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_00D0, 0x0000_00D4, 0x0000_00D8, 0x0000_00DC, 0x0000_00E0, 0x0000_00E4, // 48 + 0x0000_00E8, 0x0000_00EC, 0x0000_00F0, 0x0000_00F4, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0000_0004, 0x0000_0008, 0x0000_000C, 0x0000_0010, 0x0000_0014, 0x0000_0018, // 66 + 0x0000_001C, 0x0000_0020, 0x0000_0024, 0x0000_0028, 0x0000_002C, 0x0000_0030, // 72 + 0x0000_0034, 0x0000_0038, 0x0000_003C, 0x0000_0040, 0x0000_0044, 0x0000_0048, // 78 + 0x0000_004C, 0x0000_0050, 0x0000_0054, 0x0000_0058, 0x0000_005C, 0x0000_0060, // 84 + 0x0000_0064, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00FC, // 90 + 0x01FF_FFFF, 0x0000_0068, 0x0000_006C, 0x0000_0070, 0x0000_0074, 0x0000_0078, + 0x0000_007C, 0x0000_0080, 0x0000_0084, 0x0000_0088, 0x0000_008C, 0x0000_0090, + 0x0000_0094, 0x0000_0098, 0x0000_009C, 0x0000_00A0, 0x0000_00A4, 0x0000_00A8, + 0x0000_00AC, 0x0000_00B0, 0x0000_00B4, 0x0000_00B8, 0x0000_00BC, 0x0000_00C0, + 0x0000_00C4, 0x0000_00C8, 0x0000_00CC, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding1url: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_E003, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_4003, 0x0000_5003, 0x0000_6003, 0x0000_7003, 0x0000_8003, 0x0000_9003, // 48 + 0x0000_A003, 0x0000_B003, 0x0000_C003, 0x0000_D003, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0000_1000, 0x0000_2000, 0x0000_3000, 0x0000_4000, 0x0000_5000, 0x0000_6000, // 66 + 0x0000_7000, 0x0000_8000, 0x0000_9000, 0x0000_A000, 0x0000_B000, 0x0000_C000, // 72 + 0x0000_D000, 0x0000_E000, 0x0000_F000, 0x0000_0001, 0x0000_1001, 0x0000_2001, // 78 + 0x0000_3001, 0x0000_4001, 0x0000_5001, 0x0000_6001, 0x0000_7001, 0x0000_8001, // 84 + 0x0000_9001, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_F003, // 90 + 0x01FF_FFFF, 0x0000_A001, 0x0000_B001, 0x0000_C001, 0x0000_D001, 0x0000_E001, + 0x0000_F001, 0x0000_0002, 0x0000_1002, 0x0000_2002, 0x0000_3002, 0x0000_4002, + 0x0000_5002, 0x0000_6002, 0x0000_7002, 0x0000_8002, 0x0000_9002, 0x0000_A002, + 0x0000_B002, 0x0000_C002, 0x0000_D002, 0x0000_E002, 0x0000_F002, 0x0000_0003, + 0x0000_1003, 0x0000_2003, 0x0000_3003, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding2url: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0080_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_0D00, 0x0040_0D00, 0x0080_0D00, 0x00C0_0D00, 0x0000_0E00, 0x0040_0E00, // 48 + 0x0080_0E00, 0x00C0_0E00, 0x0000_0F00, 0x0040_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0040_0000, 0x0080_0000, 0x00C0_0000, 0x0000_0100, 0x0040_0100, 0x0080_0100, // 66 + 0x00C0_0100, 0x0000_0200, 0x0040_0200, 0x0080_0200, 0x00C0_0200, 0x0000_0300, // 72 + 0x0040_0300, 0x0080_0300, 0x00C0_0300, 0x0000_0400, 0x0040_0400, 0x0080_0400, // 78 + 0x00C0_0400, 0x0000_0500, 0x0040_0500, 0x0080_0500, 0x00C0_0500, 0x0000_0600, // 84 + 0x0040_0600, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x00C0_0F00, // 90 + 0x01FF_FFFF, 0x0080_0600, 0x00C0_0600, 0x0000_0700, 0x0040_0700, 0x0080_0700, + 0x00C0_0700, 0x0000_0800, 0x0040_0800, 0x0080_0800, 0x00C0_0800, 0x0000_0900, + 0x0040_0900, 0x0080_0900, 0x00C0_0900, 0x0000_0A00, 0x0040_0A00, 0x0080_0A00, + 0x00C0_0A00, 0x0000_0B00, 0x0040_0B00, 0x0080_0B00, 0x00C0_0B00, 0x0000_0C00, + 0x0040_0C00, 0x0080_0C00, 0x00C0_0C00, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] + + @usableFromInline + static let decoding3url: [UInt32] = [ + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003E_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0034_0000, 0x0035_0000, 0x0036_0000, 0x0037_0000, 0x0038_0000, 0x0039_0000, // 48 + 0x003A_0000, 0x003B_0000, 0x003C_0000, 0x003D_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0001_0000, 0x0002_0000, 0x0003_0000, 0x0004_0000, 0x0005_0000, 0x0006_0000, // 66 + 0x0007_0000, 0x0008_0000, 0x0009_0000, 0x000A_0000, 0x000B_0000, 0x000C_0000, // 72 + 0x000D_0000, 0x000E_0000, 0x000F_0000, 0x0010_0000, 0x0011_0000, 0x0012_0000, // 78 + 0x0013_0000, 0x0014_0000, 0x0015_0000, 0x0016_0000, 0x0017_0000, 0x0018_0000, // 84 + 0x0019_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003F_0000, // 90 + 0x01FF_FFFF, 0x001A_0000, 0x001B_0000, 0x001C_0000, 0x001D_0000, 0x001E_0000, + 0x001F_0000, 0x0020_0000, 0x0021_0000, 0x0022_0000, 0x0023_0000, 0x0024_0000, + 0x0025_0000, 0x0026_0000, 0x0027_0000, 0x0028_0000, 0x0029_0000, 0x002A_0000, + 0x002B_0000, 0x002C_0000, 0x002D_0000, 0x002E_0000, 0x002F_0000, 0x0030_0000, + 0x0031_0000, 0x0032_0000, 0x0033_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, + ] +} diff --git a/Sources/Base64KitPerformanceTest/main.swift b/Sources/Base64KitPerformanceTest/main.swift deleted file mode 100644 index ffffb20..0000000 --- a/Sources/Base64KitPerformanceTest/main.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Base64Kit -import Foundation - -let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==" - -let runs = 1_000_000 -let bytes = Array(UInt8(0) ... UInt8(255)) - -@discardableResult -func timing(name: String, execute: () -> Void) -> TimeInterval { - let start = Date() - execute() - let time = -start.timeIntervalSinceNow - print("\(name) | took: \(time)s") - return time -} - -print("Number of invocations: \(runs)") - -print("------------------------------------------") -print("Encoding") - -let data = Data(bytes) -let foundationEncoding = timing(name: "Foundation") { - for _ in 1 ... runs { - _ = data.base64EncodedString() - } -} - -let base64Encoding = timing(name: "Base64 ") { - for _ in 1 ... runs { - _ = String(base64Encoding: bytes) - } -} - -print("------------------------------------------") -print("Decoding") - -let foundationDecoding = timing(name: "Foundation") { - for _ in 1 ... runs { - _ = Data(base64Encoded: base64) - } -} - -let base64Decoding = timing(name: "Base64 ") { - for _ in 1 ... runs { - _ = try! base64.base64decoded() - } -} - -print("------------------------------------------") -print("Results") - -var result: Int32 = 0 -if foundationEncoding < base64Encoding { - print("Base64 encoding must be at least as fast as Foundation encoding") - result = 1 -} - -if foundationDecoding < base64Decoding { - print("Base64 decoding must be at least as fast as Foundation decoding") - result = 1 -} - -if result == 0 { - let encodingGain = round(foundationEncoding / base64Encoding * 1000) / 1000 - let decodingGain = round(foundationDecoding / base64Decoding * 1000) / 1000 - print("Encoding: \(encodingGain)x") - print("Decoding: \(decodingGain)x") -} - -exit(result) diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift new file mode 100644 index 0000000..7dc7b64 --- /dev/null +++ b/Sources/PerformanceTest/main.swift @@ -0,0 +1,110 @@ +import Base64Kit +import Foundation + +let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==" + +let runs = 1_000_000 +let bytes = Array(UInt8(0) ... UInt8(255)) + +@discardableResult +func timing(name: String, execute: () -> Void) -> TimeInterval { + let start = Date() + execute() + let time = -start.timeIntervalSinceNow + print("\(name) | took: \(time)s") + return time +} + +print("Number of invocations: \(runs)") + +print("------------------------------------------") +print("Encoding") + +let data = Data(bytes) +let foundationEncodingString = timing(name: "Foundation: Data to String ") { + for _ in 1 ... runs { + _ = data.base64EncodedString() + } +} + +let foundationEncodingData = timing(name: "Foundation: Data to Data ") { + for _ in 1 ... runs { + _ = data.base64EncodedData() + } +} + +let chromeEncodingBytes = timing(name: "Chromium: [UInt8] to [UInt8]") { + for _ in 1 ... runs { + let _: [UInt8] = Base64.encodeBytes(bytes: bytes) + } +} + +let chromeEncodingString = timing(name: "Chromium: [UInt8] to String ") { + for _ in 1 ... runs { + let _: String = Base64.encodeString(bytes: bytes) + } +} + +let chromeEncodingData = timing(name: "Chromium: Data to [UInt8]") { + for _ in 1 ... runs { + let _: String = Base64.encodeString(bytes: data) + } +} + +print("------------------------------------------") +print("Decoding") + +let foundationDecodingFromString = timing(name: "Foundation: String to Data ") { + for _ in 1 ... runs { + _ = Data(base64Encoded: base64) + } +} + +let encodedData = Data(base64.utf8) +let foundationDecodingFromData = timing(name: "Foundation: Data to Data ") { + for _ in 1 ... runs { + _ = Data(base64Encoded: encodedData) + } +} + +let encodedUInt8Array = Array(base64.utf8) +let chromeDecodingFromBytes = timing(name: "Chromium: [UInt8] to [UInt8]") { + for _ in 1 ... runs { + _ = try! Base64.decode(bytes: encodedUInt8Array) + } +} + +let chromeDecodingFromString = timing(name: "Chromium: String to [UInt8]") { + for _ in 1 ... runs { + _ = try! Base64.decode(string: base64) + } +} + +let chromeDecodingFromData = timing(name: "Chromium: Data to [UInt8]") { + for _ in 1 ... runs { + _ = try! Base64.decode(bytes: encodedData) + } +} + +// print("------------------------------------------") +// print("Results") + +var result: Int32 = 0 +// if foundationEncoding < base64Encoding { +// print("Base64 encoding must be at least as fast as Foundation encoding") +// result = 1 +// } +// +// if foundationDecoding < base64Decoding { +// print("Base64 decoding must be at least as fast as Foundation decoding") +// result = 1 +// } +// +// if result == 0 { +// let encodingGain = round(foundationEncoding / base64Encoding * 1000) / 1000 +// let decodingGain = round(foundationDecoding / base64Decoding * 1000) / 1000 +// print("Encoding: \(encodingGain)x") +// print("Decoding: \(decodingGain)x") +// } + +exit(result) diff --git a/Tests/Base64KitTests/Base64/DecodingTests.swift b/Tests/Base64KitTests/Base64/DecodingTests.swift deleted file mode 100644 index a7e6d53..0000000 --- a/Tests/Base64KitTests/Base64/DecodingTests.swift +++ /dev/null @@ -1,56 +0,0 @@ -@testable import Base64Kit -import XCTest - -class DecodingTests: XCTestCase { - func testDecodeEmptyString() throws { - var decoded: [UInt8]? - XCTAssertNoThrow(decoded = try "".base64decoded()) - XCTAssertEqual(decoded?.count, 0) - } - - func testBase64DecodingArrayOfNulls() throws { - let expected = Array(repeating: UInt8(0), count: 10) - var decoded: [UInt8]? - XCTAssertNoThrow(decoded = try "AAAAAAAAAAAAAA==".base64decoded()) - XCTAssertEqual(decoded, expected) - } - - func testBase64DecodingAllTheBytesSequentially() { - let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==" - - let expected = Array(UInt8(0) ... UInt8(255)) - var decoded: [UInt8]? - XCTAssertNoThrow(decoded = try base64.base64decoded()) - - XCTAssertEqual(decoded, expected) - } - - func testBase64UrlDecodingAllTheBytesSequentially() { - let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==" - - let expected = Array(UInt8(0) ... UInt8(255)) - var decoded: [UInt8]? - XCTAssertNoThrow(decoded = try base64.base64decoded(options: .base64UrlAlphabet)) - - XCTAssertEqual(decoded, expected) - } - - func testBase64DecodingWithPoop() { - XCTAssertThrowsError(_ = try "💩".base64decoded()) { error in - XCTAssertEqual(error as? DecodingError, .invalidCharacter(240)) - } - } - - func testBase64DecodingWithInvalidLength() { - XCTAssertThrowsError(_ = try "AAAAA".base64decoded()) { error in - XCTAssertEqual(error as? DecodingError, .invalidLength) - } - } - - func testNSStringToDecode() { - let test = "1234567" - let nsstring = test.data(using: .utf8)!.base64EncodedString() - - XCTAssertNoThrow(try nsstring.base64decoded()) - } -} diff --git a/Tests/Base64KitTests/Base64/EncodingTests.swift b/Tests/Base64KitTests/Base64/EncodingTests.swift deleted file mode 100644 index 8d35f06..0000000 --- a/Tests/Base64KitTests/Base64/EncodingTests.swift +++ /dev/null @@ -1,28 +0,0 @@ -@testable import Base64Kit -import XCTest - -class EncodingTests: XCTestCase { - func testEncodeEmptyData() { - let data = [UInt8]() - let encodedData = String(base64Encoding: data) - XCTAssertEqual(encodedData.count, 0) - } - - func testBase64EncodingArrayOfNulls() { - let data = Array(repeating: UInt8(0), count: 10) - let encodedData = String(base64Encoding: data) - XCTAssertEqual(encodedData, "AAAAAAAAAAAAAA==") - } - - func testBase64EncodingAllTheBytesSequentially() { - let data = Array(UInt8(0) ... UInt8(255)) - let encodedData = String(base64Encoding: data) - XCTAssertEqual(encodedData, "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==") - } - - func testBase64UrlEncodingAllTheBytesSequentially() { - let data = Array(UInt8(0) ... UInt8(255)) - let encodedData = String(base64Encoding: data, options: .base64UrlAlphabet) - XCTAssertEqual(encodedData, "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==") - } -} diff --git a/Tests/Base64KitTests/Base64/IntegrationTests.swift b/Tests/Base64KitTests/Base64/IntegrationTests.swift deleted file mode 100644 index 855056c..0000000 --- a/Tests/Base64KitTests/Base64/IntegrationTests.swift +++ /dev/null @@ -1,16 +0,0 @@ -@testable import Base64Kit -import XCTest - -class IntegrationTests: XCTestCase { - func testEncodeAndDecodingĨ() throws { - var input = "Ĩ" - let output = try input.withUTF8 { (ptr) -> String in - let bytes = String(base64Encoding: ptr) - let decoded = try bytes.base64decoded() - - return String(bytes: decoded, encoding: .utf8)! - } - - XCTAssertEqual(input, output) - } -} diff --git a/Tests/Base64KitTests/ChromiumTests.swift b/Tests/Base64KitTests/ChromiumTests.swift new file mode 100644 index 0000000..cab9694 --- /dev/null +++ b/Tests/Base64KitTests/ChromiumTests.swift @@ -0,0 +1,101 @@ +import Base64Kit +import XCTest + +class ChromiumTests: XCTestCase { + // MARK: Encoding + + func testEncodeEmptyData() { + let data = [UInt8]() + let encodedData: [UInt8] = Base64.encodeBytes(bytes: data) + XCTAssertEqual(encodedData.count, 0) + } + + func testBase64EncodingArrayOfNulls() { + let data = Array(repeating: UInt8(0), count: 10) + let encodedData: [UInt8] = Base64.encodeBytes(bytes: data) + XCTAssertEqual(encodedData, [UInt8]("AAAAAAAAAAAAAA==".utf8)) + } + + func testBase64EncodingAllTheBytesSequentially() { + let data = Array(UInt8(0) ... UInt8(255)) + let encodedData: [UInt8] = Base64.encodeBytes(bytes: data) + XCTAssertEqual(encodedData, [UInt8]("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==".utf8)) + } + + func testBase64UrlEncodingAllTheBytesSequentially() { + let data = Array(UInt8(0) ... UInt8(255)) + let encodedData: [UInt8] = Base64.encodeBytes(bytes: data, options: .base64UrlAlphabet) + XCTAssertEqual(encodedData, [UInt8]("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==".utf8)) + } + + func testBase64UrlEncodingAllTheBytesSequentiallyOmitPadding() { + let data = Array(UInt8(0) ... UInt8(255)) + let encodedData: [UInt8] = Base64.encodeBytes(bytes: data, options: [.base64UrlAlphabet, .omitPaddingCharacter]) + XCTAssertEqual(encodedData, [UInt8]("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w".utf8)) + } + + // MARK: Decoding + + func testDecodeEmptyString() throws { + var decoded: [UInt8]? + XCTAssertNoThrow(decoded = try Base64.decode(string: "")) + XCTAssertEqual(decoded?.count, 0) + } + + func testBase64DecodingArrayOfNulls() throws { + let expected = Array(repeating: UInt8(0), count: 10) + var decoded: [UInt8]? + var string = "AAAAAAAAAAAAAA==" + string.makeContiguousUTF8() + XCTAssertNoThrow(decoded = try Base64.decode(string: string)) + XCTAssertEqual(decoded, expected) + } + + func testBase64DecodingAllTheBytesSequentially() { + let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==" + + let expected = Array(UInt8(0) ... UInt8(255)) + var decoded: [UInt8]? + XCTAssertNoThrow(decoded = try Base64.decode(bytes: base64.utf8)) + XCTAssertEqual(decoded, expected) + } + + func testBase64UrlDecodingAllTheBytesSequentially() { + let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==" + + let expected = Array(UInt8(0) ... UInt8(255)) + var decoded: [UInt8]? + XCTAssertNoThrow(decoded = try Base64.decode(string: base64, options: .base64UrlAlphabet)) + + XCTAssertEqual(decoded, expected) + } + + func testBase64UrlDecodingAllTheBytesSequentiallyOmitPadding() { + let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w" + + let expected = Array(UInt8(0) ... UInt8(255)) + var decoded: [UInt8]? + XCTAssertNoThrow(decoded = try Base64.decode(string: base64, options: [.base64UrlAlphabet, .omitPaddingCharacter])) + + XCTAssertEqual(decoded, expected) + } + + func testBase64DecodingWithPoop() { + XCTAssertThrowsError(_ = try Base64.decode(bytes: "💩".utf8)) { error in + XCTAssertEqual(error as? DecodingError, .invalidCharacter(240)) + } + } + + func testBase64DecodingWithInvalidLength() { + XCTAssertThrowsError(_ = try Base64.decode(bytes: "AAAAA".utf8)) { error in + XCTAssertEqual(error as? DecodingError, .invalidLength) + } + } + + func testNSStringToDecode() { + let test = "1234567" + let nsstring = test.data(using: .utf8)!.base64EncodedString() + + XCTAssertNoThrow(try Base64.decode(string: nsstring)) + } +} diff --git a/Tests/Base64KitTests/IntegrationTests.swift b/Tests/Base64KitTests/IntegrationTests.swift new file mode 100644 index 0000000..f8f863e --- /dev/null +++ b/Tests/Base64KitTests/IntegrationTests.swift @@ -0,0 +1,16 @@ +@testable import Base64Kit +import XCTest + +class IntegrationTests: XCTestCase { + func testEncodeAndDecodingĨ() throws { + var input = "Ĩ" + let encoded = input.withUTF8 { (ptr) -> String in + Base64.encodeString(bytes: ptr) + } + + let decoded = try Base64.decode(string: encoded) + let output = String(decoding: decoded, as: Unicode.UTF8.self) + + XCTAssertEqual(input, output) + } +} diff --git a/scripts/sanity.sh b/scripts/validity.sh similarity index 100% rename from scripts/sanity.sh rename to scripts/validity.sh