diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 000000000..b8568b2f7 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,279 @@ + The Foundation Project + ====================== + +This product contains a derivation of Fabian Fett's 'Base64.swift'. + + * LICENSE (Apache License 2.0): + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + * HOMEPAGE: + * https://github.com/swift-extras/swift-extras-base64 + +--- + +This product contains a derivation of Daniel Lemire's 'chromiumbase64' +implementation in 'fastbase64'. + + * LICENSE: + + Copyright (c) 2015-2016, Wojciech Muła, Alfred Klomp, Daniel Lemire + (Unless otherwise stated in the source code) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * HOMEPAGE: + * https://github.com/lemire/fastbase64 + +--- + +This product contains a derivation of Nick Galbreath's 'base64' +implementation in 'stringencoders'. + + * LICENSE (The MIT License): + + The MIT License (MIT) + + Copyright (c) 2016 Nick Galbreath + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + * HOMEPAGE: + * https://github.com/client9/stringencoders diff --git a/Sources/FoundationEssentials/Data/Data+Base64.swift b/Sources/FoundationEssentials/Data/Data+Base64.swift index f73d15049..e1b373f57 100644 --- a/Sources/FoundationEssentials/Data/Data+Base64.swift +++ b/Sources/FoundationEssentials/Data/Data+Base64.swift @@ -92,23 +92,7 @@ extension Data { /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded string. public func base64EncodedString(options: Base64EncodingOptions = []) -> String { -#if FOUNDATION_FRAMEWORK - // Previously inlinable - return _representation.withInteriorPointerReference { - return $0.base64EncodedString( - options: NSData.Base64EncodingOptions( - rawValue: options.rawValue)) - } -#else - if self.isEmpty { return "" } - - return self.withBufferView { inputBuffer in - let capacity = Self.estimateBase64Size(length: self.count) - return String(utf8Capacity: capacity) { - Self.base64EncodeBytes(inputBuffer, &$0, options: options) - } - } -#endif + Base64.encodeToString(bytes: self, options: options) } /// Returns a Base-64 encoded `Data`. @@ -116,38 +100,11 @@ extension Data { /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded data. public func base64EncodedData(options: Base64EncodingOptions = []) -> Data { -#if FOUNDATION_FRAMEWORK - // Previously inlinable - return _representation.withInteriorPointerReference { - return $0.base64EncodedData( - options: NSData.Base64EncodingOptions( - rawValue: options.rawValue)) - } -#else - let dataLength = self.count - if dataLength == 0 { return Data() } - - return self.withBufferView { inputBuffer in - let capacity = Self.estimateBase64Size(length: dataLength) - return Data(capacity: capacity) { - Self.base64EncodeBytes(inputBuffer, &$0, options: options) - } - } -#endif + Base64.encodeToData(bytes: self, options: options) } // MARK: - Internal Helpers - static func estimateBase64Size(length: Int) -> Int { - // Worst case allow for 64bytes + \r\n per line 48 input bytes => 66 output bytes - return ((length + 47) * 66) / 48 - } - - /** - Padding character used when the number of bytes to encode is not divisible by 3 - */ - static let base64Padding : UInt8 = 61 // = - /** This method decodes Base64-encoded data. @@ -249,123 +206,333 @@ extension Data { throw Base64Error.cannotDecode } } +} - /** - This method encodes data in Base64. +// This base64 implementation is heavily inspired by: +// +// https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c +// https://github.com/client9/stringencoders/blob/master/src/modp_b64.c +// +// See NOTICE.txt for Licenses + +enum Base64 {} + +// MARK: - Encoding - + +extension Base64 { + static let encodePaddingCharacter: UInt8 = 61 + + 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: "/"), + ] + + 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: "/"), + ] + + static func encodeToBytes(bytes: Buffer, options: Data.Base64EncodingOptions) + -> [UInt8] where Buffer.Element == UInt8 + { + let newCapacity = self.encodeComputeCapacity(bytes: bytes.count, options: options) + + if let result = bytes.withContiguousStorageIfAvailable({ input -> [UInt8] in + [UInt8](unsafeUninitializedCapacity: newCapacity) { buffer, length in + Self._encode(input: input, buffer: buffer, length: &length, options: options) + } + }) { + return result + } - - parameter dataBuffer: A BufferView of the bytes to encode - - parameter options: Options for formatting the result - - parameter buffer: The buffer to write the bytes into - */ - static func base64EncodeBytes( - _ dataBuffer: BufferView, _ buffer: inout OutputBuffer, options: Base64EncodingOptions = [] - ) { - // Use a StaticString for lookup of values 0-63 -> ASCII values - let base64Chars = StaticString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") - assert(base64Chars.utf8CodeUnitCount == 64) - assert(base64Chars.hasPointerRepresentation) - assert(base64Chars.isASCII) - let base64CharsPtr = base64Chars.utf8Start - - let lineLength: Int - var currentLineCount = 0 - let separatorByte1: UInt8 - var separatorByte2: UInt8? + return self.encodeToBytes(bytes: Array(bytes), options: options) + } - if options.isEmpty { - lineLength = 0 - separatorByte1 = 0 - } else { - if options.contains(.lineLength64Characters) { - lineLength = 64 - } else if options.contains(.lineLength76Characters) { - lineLength = 76 - } else { - lineLength = 0 + static func encodeToString(bytes: Buffer, options: Data.Base64EncodingOptions = []) + -> String where Buffer.Element == UInt8 + { + let newCapacity = self.encodeComputeCapacity(bytes: bytes.count, options: options) + + 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._encode(input: input, buffer: buffer, length: &length, options: options) + return length + } + }) { + return result } - switch (options.contains(.endLineWithCarriageReturn), options.contains(.endLineWithLineFeed)) { - case (true, true), (false, false): - separatorByte1 = UInt8(ascii: "\r") - separatorByte2 = UInt8(ascii: "\n") - case (true, false): - separatorByte1 = UInt8(ascii: "\r") - case (false, true): - separatorByte1 = UInt8(ascii: "\n") - } + return self.encodeToString(bytes: Array(bytes), options: options) + } else { + let bytes: [UInt8] = self.encodeToBytes(bytes: bytes, options: options) + return String(decoding: bytes, as: Unicode.UTF8.self) } + } - func lookupBase64Value(_ value: UInt16) -> UInt32 { - let byte = base64CharsPtr[Int(value & 63)] - return UInt32(byte) + static func encodeToData(bytes: Buffer, options: Data.Base64EncodingOptions = []) + -> Data where Buffer.Element == UInt8 + { + let newCapacity = self.encodeComputeCapacity(bytes: bytes.count, options: options) + + if let result = bytes.withContiguousStorageIfAvailable({ input -> Data in + var data = Data(count: newCapacity) // initialized with zeroed buffer + _ = data.withUnsafeMutableBytes { rawBuffer in + rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in + var length = newCapacity + Self._encode(input: input, buffer: buffer, length: &length, options: options) + return length + } + } + return data + }) { + return result } - // Read three bytes at a time, which convert to 4 ASCII characters, allowing for byte2 and byte3 being nil + return self.encodeToData(bytes: Array(bytes), options: options) + } + + static func _encode(input: UnsafeBufferPointer, buffer: UnsafeMutableBufferPointer, length: inout Int, options: Data.Base64EncodingOptions) { + if options.contains(.lineLength64Characters) || options.contains(.lineLength76Characters) { + return self._encodeWithLineBreaks(input: input, buffer: buffer, length: &length, options: options) + } - var inputIndex = 0 - var bytesLeft = dataBuffer.count + let omitPaddingCharacter = false // options.contains(.omitPaddingCharacter) + + Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { (e0, e1) throws(Never) -> Void 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 + } - while bytesLeft > 0 { + 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 { + buffer[outIndex &+ 1] = e1[Int(((i1 & 0x03) &<< 4) | ((i2 &>> 4) & 0x0F))] + if let i3 = i3 { + buffer[outIndex &+ 2] = e1[Int(((i2 & 0x0F) &<< 2) | ((i3 &>> 6) & 0x03))] + buffer[outIndex &+ 3] = e1[Int(i3)] + outIndex += 4 + } else { + 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 + } + } + } - let byte1 = dataBuffer[offset: inputIndex] + length = outIndex + } + } - // outputBytes is a UInt32 to allow 4 bytes to be written out at once. - var outputBytes = lookupBase64Value(UInt16(byte1 >> 2)) + static func _encodeWithLineBreaks( + input: UnsafeBufferPointer, + buffer: UnsafeMutableBufferPointer, + length: inout Int, + options: Data.Base64EncodingOptions + ) { + let omitPaddingCharacter = false - if bytesLeft > 2 { - // This is the main loop converting 3 bytes at a time. - let byte2 = dataBuffer[offset: inputIndex + 1] - let byte3 = dataBuffer[offset: inputIndex + 2] - var value = UInt16(byte1 & 0x3) << 8 - value |= UInt16(byte2) + assert(options.contains(.lineLength64Characters) || options.contains(.lineLength76Characters)) - let outputByte2 = lookupBase64Value(value >> 4) - outputBytes |= (outputByte2 << 8) - value = (value << 8) | UInt16(byte3) + let lineLength = if options.contains(.lineLength64Characters) { + 48 + } else { + 57 + } - let outputByte3 = lookupBase64Value(value >> 6) - outputBytes |= (outputByte3 << 16) + let lines = input.count / lineLength - let outputByte4 = lookupBase64Value(value) - outputBytes |= (outputByte4 << 24) - inputIndex += 3 - } else { - // This runs once at the end of there were 1 or 2 bytes left, byte1 having already been read. - // Read byte2 or 0 if there isnt another byte - let byte2 = bytesLeft == 1 ? 0 : dataBuffer[offset: inputIndex + 1] - var value = UInt16(byte1 & 0x3) << 8 - value |= UInt16(byte2) - - let outputByte2 = lookupBase64Value(value >> 4) - outputBytes |= (outputByte2 << 8) - - let outputByte3 = bytesLeft == 1 ? UInt32(self.base64Padding) : lookupBase64Value(value << 2) - outputBytes |= (outputByte3 << 16) - outputBytes |= (UInt32(self.base64Padding) << 24) - inputIndex += bytesLeft - assert(inputIndex == dataBuffer.count) - } + let separatorByte1: UInt8 + let separatorByte2: UInt8? + + switch (options.contains(.endLineWithCarriageReturn), options.contains(.endLineWithLineFeed)) { + case (true, true), (false, false): + separatorByte1 = UInt8(ascii: "\r") + separatorByte2 = UInt8(ascii: "\n") + case (true, false): + separatorByte1 = UInt8(ascii: "\r") + separatorByte2 = nil + case (false, true): + separatorByte1 = UInt8(ascii: "\n") + separatorByte2 = nil + } - // The lowest byte of outputBytes needs to be stored at the lowest address, so make sure - // the bytes are in the correct order on big endian CPUs. - buffer.appendBytes(of: outputBytes.littleEndian, as: UInt32.self) - bytesLeft = dataBuffer.count - inputIndex + Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { e0, e1 in + var outIndex = 0 + for lineInputIndex in stride(from: 0, to: lines * lineLength, by: lineLength) { + for index in stride(from: lineInputIndex, to: lineInputIndex + lineLength, 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 lineLength != 0 { - // Add required EOL markers. - currentLineCount += 4 - assert(currentLineCount <= lineLength) + buffer[outIndex] = separatorByte1 + outIndex += 1 + if let separatorByte2 { + buffer[outIndex] = separatorByte2 + outIndex += 1 + } + } - if currentLineCount == lineLength && bytesLeft > 0 { - buffer.appendElement(separatorByte1) + let to = input.count / 3 * 3 + for index in stride(from: lines * lineLength, 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 let separatorByte2 { - buffer.appendElement(separatorByte2) + 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 } - currentLineCount = 0 } } + + length = outIndex } } -} + static func encodeComputeCapacity(bytes: Int, options: Data.Base64EncodingOptions) -> Int { + let capacityWithoutBreaks = ((bytes + 2) / 3) * 4 + + guard options.contains(.lineLength64Characters) || options.contains(.lineLength76Characters) else { + return capacityWithoutBreaks + } + + let seperatorBytes = switch (options.contains(.endLineWithCarriageReturn), options.contains(.endLineWithLineFeed)) { + case (true, true), (false, false): 2 + case (true, false), (false, true): 1 + } + + let lineLength = options.contains(.lineLength64Characters) ? 64 : 76 + let lineBreaks = capacityWithoutBreaks / lineLength + let lineBreakCapacity = lineBreaks * seperatorBytes + return capacityWithoutBreaks + lineBreakCapacity + } + + static func withUnsafeEncodingTablesAsBufferPointers(options: Data.Base64EncodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer) -> R) -> R { + let encoding0 = Self.encoding0 + let encoding1 = Self.encoding1 + + assert(encoding0.count == 256) + assert(encoding1.count == 256) + + return encoding0.withUnsafeBufferPointer { e0 in + encoding1.withUnsafeBufferPointer { e1 in + body(e0, e1) + } + } + } +}