diff --git a/Sources/FoundationEssentials/Data/Data+Base64.swift b/Sources/FoundationEssentials/Data/Data+Base64.swift index ac23101e0..e497426ca 100644 --- a/Sources/FoundationEssentials/Data/Data+Base64.swift +++ b/Sources/FoundationEssentials/Data/Data+Base64.swift @@ -10,10 +10,20 @@ // //===----------------------------------------------------------------------===// -private enum Base64Error: Error { - case invalidElementCount - case cannotDecode -} +#if canImport(Darwin) +import Darwin +#elseif canImport(Bionic) +import Bionic +#elseif canImport(Glibc) +import Glibc +#elseif canImport(Musl) +import Musl +#elseif os(Windows) +import CRT +import WinSDK +#elseif os(WASI) +import WASILibc +#endif @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { @@ -26,21 +36,10 @@ extension Data { /// - parameter base64String: The string to parse. /// - parameter options: Encoding options. Default value is `[]`. public init?(base64Encoded base64String: __shared String, options: Base64DecodingOptions = []) { -#if FOUNDATION_FRAMEWORK - if let d = NSData(base64Encoded: base64String, options: NSData.Base64DecodingOptions(rawValue: options.rawValue)) { - self.init(referencing: d) - } else { + guard let result = try? Base64.decode(string: base64String, options: options) else { return nil } -#else - var encoded = base64String - let decoded = encoded.withUTF8 { - // String won't pass an empty buffer with a `nil` `baseAddress`. - Data(decodingBase64: BufferView(unsafeBufferPointer: $0)!, options: options) - } - guard let decoded else { return nil } - self = decoded -#endif + self = result } /// Initialize a `Data` from a Base-64, UTF-8 encoded `Data`. @@ -50,39 +49,10 @@ extension Data { /// - parameter base64Data: Base-64, UTF-8 encoded input data. /// - parameter options: Decoding options. Default value is `[]`. public init?(base64Encoded base64Data: __shared Data, options: Base64DecodingOptions = []) { -#if FOUNDATION_FRAMEWORK - if let d = NSData(base64Encoded: base64Data, options: NSData.Base64DecodingOptions(rawValue: options.rawValue)) { - self.init(referencing: d) - } else { + guard let result = try? Base64.decode(data: base64Data, options: options) else { return nil } -#else - let decoded = base64Data.withBufferView { - Data(decodingBase64: $0, options: options) - } - guard let decoded else { return nil } - self = decoded -#endif - } - - init?(decodingBase64 bytes: borrowing BufferView, options: Base64DecodingOptions = []) { - guard bytes.count.isMultiple(of: 4) || options.contains(.ignoreUnknownCharacters) - else { return nil } - - // Every 4 valid ASCII bytes maps to 3 output bytes: (bytes.count * 3)/4 - let capacity = (bytes.count * 3) >> 2 - // A non-trapping version of the calculation goes like this: - // let (q, r) = bytes.count.quotientAndRemainder(dividingBy: 4) - // let capacity = (q * 3) + (r==0 ? 0 : r-1) - let decoded = try? Data( - capacity: capacity, - initializingWith: { //FIXME: should work with borrowed `bytes` - [bytes = copy bytes] in - try Data.base64DecodeBytes(bytes, &$0, options: options) - } - ) - guard let decoded else { return nil } - self = decoded + self = result } // MARK: - Create base64 @@ -102,110 +72,6 @@ extension Data { public func base64EncodedData(options: Base64EncodingOptions = []) -> Data { Base64.encodeToData(bytes: self, options: options) } - - // MARK: - Internal Helpers - - /** - This method decodes Base64-encoded data. - - If the input contains any bytes that are not valid Base64 characters, - this will throw a `Base64Error`. - - - parameter bytes: The Base64 bytes - - parameter output: An OutputBuffer to be filled with decoded bytes - - parameter options: Options for handling invalid input - - throws: When decoding fails - */ - static func base64DecodeBytes( - _ bytes: borrowing BufferView, _ output: inout OutputBuffer, options: Base64DecodingOptions = [] - ) throws { - guard bytes.count.isMultiple(of: 4) || options.contains(.ignoreUnknownCharacters) - else { throw Base64Error.invalidElementCount } - - // This table maps byte values 0-127, input bytes >127 are always invalid. - // Map the ASCII characters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -> 0...63 - // Map '=' (ASCII 61) to 0x40. - // All other values map to 0x7f. This allows '=' and invalid bytes to be checked together by testing bit 6 (0x40). - let base64Decode: StaticString = """ -\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\ -\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\ -\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{3e}\u{7f}\u{7f}\u{7f}\u{3f}\ -\u{34}\u{35}\u{36}\u{37}\u{38}\u{39}\u{3a}\u{3b}\u{3c}\u{3d}\u{7f}\u{7f}\u{7f}\u{40}\u{7f}\u{7f}\ -\u{7f}\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0a}\u{0b}\u{0c}\u{0d}\u{0e}\ -\u{0f}\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\ -\u{7f}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}\u{20}\u{21}\u{22}\u{23}\u{24}\u{25}\u{26}\u{27}\u{28}\ -\u{29}\u{2a}\u{2b}\u{2c}\u{2d}\u{2e}\u{2f}\u{30}\u{31}\u{32}\u{33}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f} -""" - assert(base64Decode.isASCII) - assert(base64Decode.utf8CodeUnitCount == 128) - assert(base64Decode.hasPointerRepresentation) - - let ignoreUnknown = options.contains(.ignoreUnknownCharacters) - - var currentByte: UInt8 = 0 - var validCharacterCount = 0 - var paddingCount = 0 - var index = 0 - - for base64Char in bytes { - var value: UInt8 = 0 - - var invalid = false - if base64Char >= base64Decode.utf8CodeUnitCount { - invalid = true - } else { - value = base64Decode.utf8Start[Int(base64Char)] - if value & 0x40 == 0x40 { // Input byte is either '=' or an invalid value. - if value == 0x7f { - invalid = true - } else if value == 0x40 { // '=' padding at end of input. - paddingCount += 1 - continue - } - } - } - - if invalid { - if ignoreUnknown { - continue - } else { - throw Base64Error.cannotDecode - } - } - validCharacterCount += 1 - - // Padding found in the middle of the sequence is invalid. - if paddingCount > 0 { - throw Base64Error.cannotDecode - } - - switch index { - case 0: - currentByte = (value << 2) - case 1: - currentByte |= (value >> 4) - output.appendElement(currentByte) - currentByte = (value << 4) - case 2: - currentByte |= (value >> 2) - output.appendElement(currentByte) - currentByte = (value << 6) - case 3: - currentByte |= value - output.appendElement(currentByte) - index = -1 - default: - fatalError("Invalid state") - } - - index += 1 - } - - guard (validCharacterCount + paddingCount) % 4 == 0 else { - // Invalid character count of valid input characters. - throw Base64Error.cannotDecode - } - } } // This base64 implementation is heavily inspired by: @@ -346,7 +212,7 @@ extension Base64 { return self._encodeWithLineBreaks(input: input, buffer: buffer, length: &length, options: options) } - let omitPaddingCharacter = false // options.contains(.omitPaddingCharacter) + let omitPaddingCharacter = false Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { (e0, e1) throws(Never) -> Void in let to = input.count / 3 * 3 @@ -547,3 +413,613 @@ extension Base64 { } } } + +// MARK: - Decoding - + +extension Base64 { + + enum DecodingError: Error, Equatable { + case invalidLength + case invalidCharacter(UInt8) + case unexpectedPaddingCharacter + case unexpectedEnd + } + + static func decode(string encoded: String, options: Data.Base64DecodingOptions = []) throws(DecodingError) -> Data { + let result = encoded.utf8.withContiguousStorageIfAvailable { bufferPointer in + // `withContiguousStorageIfAvailable` sadly does not support typed throws, so we need + // to use Result to get the error out without allocation for the error. + Result(catching: { () throws(DecodingError) -> Data in + try Self._decodeToData(from: bufferPointer, options: options) + }) + } + + if let result { + return try result.get() + } + + var encoded = encoded + encoded.makeContiguousUTF8() + return try Self.decode(string: encoded, options: options) + } + + static func decode(data encoded: Data, options: Data.Base64DecodingOptions = []) throws(DecodingError) -> Data? { + let result = encoded.withContiguousStorageIfAvailable { bufferPointer in + // `withContiguousStorageIfAvailable` sadly does not support typed throws, so we need + // to use Result to get the error out without allocation for the error. + Result(catching: { () throws(DecodingError) -> Data in + try Self._decodeToData(from: bufferPointer, options: options) + }) + } + + if let result { + return try result.get() + } + + return try Self.decode(bytes: Array(encoded), options: options) + } + + static func decode(bytes: Buffer, options: Data.Base64DecodingOptions = []) throws(DecodingError) -> Data where Buffer.Element == UInt8 { + guard bytes.count > 0 else { + return Data() + } + + let result = bytes.withContiguousStorageIfAvailable { bufferPointer in + // `withContiguousStorageIfAvailable` sadly does not support typed throws, so we need + // to use Result to get the error out without allocation for the error. + Result(catching: { () throws(DecodingError) -> Data in + try Self._decodeToData(from: bufferPointer, options: options) + }) + } + + if let result { + return try result.get() + } + + return try self.decode(bytes: Array(bytes), options: options) + } + + static func _decodeToData(from inBuffer: UnsafeBufferPointer, options: Data.Base64DecodingOptions) throws(DecodingError) -> Data { + guard inBuffer.count > 0 else { + return Data() + } + + let outputLength = ((inBuffer.count + 3) / 4) * 3 + + let pointer = malloc(outputLength) + let other = pointer?.bindMemory(to: UInt8.self, capacity: outputLength) + let target = UnsafeMutableBufferPointer(start: other, count: outputLength) + var length = outputLength + if options.contains(.ignoreUnknownCharacters) { + try Self._decodeIgnoringErrors(from: inBuffer, into: target, length: &length, options: options) + } else { + // for whatever reason I can see this being 10% faster for larger payloads. Maybe better + // branch prediction? + try self._decode(from: inBuffer, into: target, length: &length, options: options) + } + + return Data(bytesNoCopy: pointer!, count: length, deallocator: .free) + } + + static func _decode( + from inBuffer: UnsafeBufferPointer, + into outBuffer: UnsafeMutableBufferPointer, + length: inout Int, + options: Data.Base64DecodingOptions + ) throws(DecodingError) { + let remaining = inBuffer.count % 4 + guard remaining == 0 else { throw DecodingError.invalidLength } + + 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) throws(DecodingError) 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 * 4 + 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 + } + } + + static func _decodeIgnoringErrors( + from inBuffer: UnsafeBufferPointer, + into outBuffer: UnsafeMutableBufferPointer, + length: inout Int, + options: Data.Base64DecodingOptions + ) throws(DecodingError) { + assert(options.contains(.ignoreUnknownCharacters)) + + let outputLength = ((inBuffer.count + 3) / 4) * 3 + 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) throws(DecodingError) in + var outIndex = 0 + var inIndex = 0 + + fastLoop: while inIndex + 3 < inBuffer.count { + 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 { + if a3 == Self.encodePaddingCharacter { + break // the loop + } + + // error fast path. we assume that illeagal errors are at the boundary. + // lets skip them and then return to fast mode! + if !self.isValidBase64Byte(a0, options: options) { + if !self.isValidBase64Byte(a1, options: options) { + if !self.isValidBase64Byte(a2, options: options) { + if !self.isValidBase64Byte(a3, options: options) { + inIndex &+= 4 + continue + } else { + inIndex &+= 3 + continue + } + } else { + inIndex &+= 2 + continue + } + } else { + inIndex &+= 1 + continue + } + } + + // error slow path... the first character is valid base64 + let b0 = a0 + var b1: UInt8? = nil + var b2: UInt8? = nil + var b3: UInt8? = nil + let startIndex = inIndex + inIndex &+= 1 + scanForValidCharacters: while inIndex < inBuffer.count { + guard self.isValidBase64Byte(inBuffer[inIndex], options: options) else { + if inBuffer[inIndex] == Self.encodePaddingCharacter { + inIndex = startIndex + break fastLoop + } + inIndex &+= 1 + continue scanForValidCharacters + } + + defer { inIndex &+= 1 } + + if b1 == nil { + b1 = inBuffer[inIndex] + } else if b2 == nil { + b2 = inBuffer[inIndex] + } else if b3 == nil { + b3 = inBuffer[inIndex] + break scanForValidCharacters + } + } + + guard let b1, let b2, let b3 else { + throw DecodingError.invalidLength + } + + x = d0[Int(b0)] | d1[Int(b1)] | d2[Int(b2)] | d3[Int(b3)] + + } else { + inIndex &+= 4 + } + + 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 + } + } + } + + if inIndex == inBuffer.count { + // all done! + length = outIndex + return + } + + guard inIndex + 3 < inBuffer.count else { + if options.contains(.ignoreUnknownCharacters) { + // ensure that all remaining characters are unknown + while inIndex < inBuffer.count { + let value = inBuffer[inIndex] + if self.isValidBase64Byte(value, options: options) || value == Self.encodePaddingCharacter { + throw DecodingError.invalidCharacter(inBuffer[inIndex]) + } + inIndex &+= 1 + } + length = outIndex + return + } + throw DecodingError.invalidLength + } + + let a0 = inBuffer[inIndex] + let a1 = inBuffer[inIndex + 1] + var a2: UInt8 = 65 + var a3: UInt8 = 65 + var padding2 = false + var padding3 = false + + if inBuffer[inIndex + 2] == Self.encodePaddingCharacter { + padding2 = true + } else { + a2 = inBuffer[inIndex + 2] + } + if inBuffer[inIndex + 3] == Self.encodePaddingCharacter { + padding3 = true + } else { + if padding2 && self.isValidBase64Byte(inBuffer[inIndex + 3], options: options) { + throw DecodingError.unexpectedPaddingCharacter + } + a3 = inBuffer[inIndex + 3] + } + + var x: UInt32 = d0[Int(a0)] | d1[Int(a1)] | d2[Int(a2)] | d3[Int(a3)] + if x >= Self.badCharacter { + var b0: UInt8? = nil + var b1: UInt8? = nil + var b2: UInt8? = nil + var b3: UInt8? = nil + + scanForValidCharacters: while inIndex < inBuffer.count { + defer { inIndex &+= 1 } + let value = inBuffer[inIndex] + if self.isValidBase64Byte(value, options: options) { + if b0 == nil { + b0 = value + } else if b1 == nil { + b1 = value + } else if b2 == nil { + b2 = value + } else if b3 == nil { + if padding2 { throw DecodingError.unexpectedPaddingCharacter } + b3 = value + break scanForValidCharacters + } + } else if value == Self.encodePaddingCharacter { + guard b0 != nil, b1 != nil else { + throw DecodingError.invalidLength + } + if b2 == nil { + padding2 = true + b2 = 65 + } else if b3 == nil { + padding3 = true + b3 = 65 + break scanForValidCharacters + } + } + } + + guard let b0, let b1, let b2, let b3 else { + if b0 == nil { + length = outIndex + return + } + throw DecodingError.invalidLength + } + + x = d0[Int(b0)] | d1[Int(b1)] | d2[Int(b2)] | d3[Int(b3)] + assert(x < Self.badCharacter) + } + + withUnsafePointer(to: &x) { ptr in + ptr.withMemoryRebound(to: UInt8.self, capacity: 4) { newPtr in + outBuffer[outIndex] = newPtr[0] + outIndex += 1 + if !padding2 { + outBuffer[outIndex] = newPtr[1] + outIndex += 1 + } + if !padding3 { + outBuffer[outIndex] = newPtr[2] + outIndex += 1 + } + } + } + + length = outIndex + } + } + + static func withUnsafeDecodingTablesAsBufferPointers(options: Data.Base64DecodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer) throws(E) -> R) throws(E) -> R { + let decoding0 = Self.decoding0 + let decoding1 = Self.decoding1 + let decoding2 = Self.decoding2 + let decoding3 = Self.decoding3 + + assert(decoding0.count == 256) + assert(decoding1.count == 256) + assert(decoding2.count == 256) + assert(decoding3.count == 256) + + // Workaround that `withUnsafeBufferPointer` started to support typed throws in Swift 6.1 + let result = decoding0.withUnsafeBufferPointer { d0 -> Result in + decoding1.withUnsafeBufferPointer { d1 -> Result in + decoding2.withUnsafeBufferPointer { d2 -> Result in + decoding3.withUnsafeBufferPointer { d3 -> Result in + Result { () throws(E) -> R in + try body(d0, d1, d2, d3) + } + } + } + } + } + + return try result.get() + } + + static func isValidBase64Byte(_ byte: UInt8, options: Data.Base64DecodingOptions) -> Bool { + switch byte { + case UInt8(ascii: "A")...UInt8(ascii: "Z"), + UInt8(ascii: "a")...UInt8(ascii: "z"), + UInt8(ascii: "0")...UInt8(ascii: "9"): + true + + case UInt8(ascii: "-"), UInt8(ascii: "_"): + false // options.contains(.base64UrlAlphabet) + + case UInt8(ascii: "/"), UInt8(ascii: "+"): + true // !options.contains(.base64UrlAlphabet) + + default: + false + } + } + + static let badCharacter: UInt32 = 0x01FF_FFFF + + 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, + ] + + 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, + ] + + 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, + ] + + 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, + ] +} diff --git a/Sources/FoundationEssentials/JSON/JSONDecoder.swift b/Sources/FoundationEssentials/JSON/JSONDecoder.swift index 8501e8568..35ca69078 100644 --- a/Sources/FoundationEssentials/JSON/JSONDecoder.swift +++ b/Sources/FoundationEssentials/JSON/JSONDecoder.swift @@ -679,7 +679,7 @@ extension JSONDecoderImpl: Decoder { var data: Data? if isSimple { data = withBuffer(for: region) { buffer, _ in - Data(decodingBase64: buffer) + try? Base64.decode(bytes: buffer) } } if data == nil { diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index 30f8bc62e..4f6d02036 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -1986,6 +1986,36 @@ extension DataTests { XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA= =", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA== ", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: " AQIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "A QIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQ IDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQI DBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQID BA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDB A==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA ==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA= =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA== ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: " AQIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "A QIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQ IDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQI DBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQID BA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDB A==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA ==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA= =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA== ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: " AQIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "A QIDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQ IDBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQI DBA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQID BA==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDB A==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA ==", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA= =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4]), Data(base64Encoded: "AQIDBA== ", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: " AQIDBAU=", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "A QIDBAU=", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQ IDBAU=", options: .ignoreUnknownCharacters)) @@ -1996,6 +2026,36 @@ extension DataTests { XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU =", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU= ", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: " AQIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "A QIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQ IDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQI DBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQID BAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDB AU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBA U=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU= ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: " AQIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "A QIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQ IDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQI DBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQID BAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDB AU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBA U=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU= ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: " AQIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "A QIDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQ IDBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQI DBAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQID BAU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDB AU=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBA U=", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU =", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5]), Data(base64Encoded: "AQIDBAU= ", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: " AQIDBAUG", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "A QIDBAUG", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQ IDBAUG", options: .ignoreUnknownCharacters)) @@ -2005,6 +2065,36 @@ extension DataTests { XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBA UG", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAU G", options: .ignoreUnknownCharacters)) XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAUG ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: " AQIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "A QIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQ IDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQI DBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQID BAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDB AUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBA UG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAU G", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAUG ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: " AQIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "A QIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQ IDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQI DBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQID BAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDB AUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBA UG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAU G", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAUG ", options: .ignoreUnknownCharacters)) + + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: " AQIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "A QIDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQ IDBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQI DBAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQID BAUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDB AUG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBA UG", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAU G", options: .ignoreUnknownCharacters)) + XCTAssertEqual(Data([1, 2, 3, 4, 5, 6]), Data(base64Encoded: "AQIDBAUG ", options: .ignoreUnknownCharacters)) } func test_base64Decode_test1MBDataGoing0to255OverAndOver() {