// // Base64FS.swift // // Created by Jack Chorley on 27/06/2017. // import Foundation public class Base64FS { private static let padding: UInt8 = 61 // Padding = "=" private static let filenameSafeAlphabet: [UInt8] = [ 65, // 0 = "A" 66, // 1 = "B" 67, // 2 = "C" 68, // 3 = "D" 69, // 4 = "E" 70, // 5 = "F" 71, // 6 = "G" 72, // 7 = "H" 73, // 8 = "I" 74, // 9 = "J" 75, // 10 = "K" 76, // 11 = "L" 77, // 12 = "M" 78, // 13 = "N" 79, // 14 = "O" 80, // 15 = "P" 81, // 16 = "Q" 82, // 17 = "R" 83, // 18 = "S" 84, // 19 = "T" 85, // 20 = "U" 86, // 21 = "V" 87, // 22 = "W" 88, // 23 = "X" 89, // 24 = "Y" 90, // 25 = "Z" 97, // 26 = "a" 98, // 27 = "b" 99, // 28 = "c" 100, // 29 = "d" 101, // 30 = "e" 102, // 31 = "f" 103, // 32 = "g" 104, // 33 = "h" 105, // 34 = "i" 106, // 35 = "j" 107, // 36 = "k" 108, // 37 = "l" 109, // 38 = "m" 110, // 39 = "n" 111, // 40 = "o" 112, // 41 = "p" 113, // 42 = "q" 114, // 43 = "r" 115, // 44 = "s" 116, // 45 = "t" 117, // 46 = "u" 118, // 47 = "v" 119, // 48 = "w" 120, // 49 = "x" 121, // 50 = "y" 122, // 51 = "z" 48, // 52 = "0" 49, // 53 = "1" 50, // 54 = "2" 51, // 55 = "3" 52, // 56 = "4" 53, // 57 = "5" 54, // 58 = "6" 55, // 59 = "7" 56, // 60 = "8" 57, // 61 = "9" 45, // 62 = "-" 95, // 63 = "_" ] private static let safeAlphabetToIndex: [UInt8 : UInt8] = [ 61 : 0, // Padding = 0 65 : 0, // 0 = "A" 66 : 1, // 1 = "B" 67 : 2, // 2 = "C" 68 : 3, // 3 = "D" 69 : 4, // 4 = "E" 70 : 5, // 5 = "F" 71 : 6, // 6 = "G" 72 : 7, // 7 = "H" 73 : 8, // 8 = "I" 74 : 9, // 9 = "J" 75 : 10, // 10 = "K" 76 : 11, // 11 = "L" 77 : 12, // 12 = "M" 78 : 13, // 13 = "N" 79 : 14, // 14 = "O" 80 : 15, // 15 = "P" 81 : 16, // 16 = "Q" 82 : 17, // 17 = "R" 83 : 18, // 18 = "S" 84 : 19, // 19 = "T" 85 : 20, // 20 = "U" 86 : 21, // 21 = "V" 87 : 22, // 22 = "W" 88 : 23, // 23 = "X" 89 : 24, // 24 = "Y" 90 : 25, // 25 = "Z" 97 : 26, // 26 = "a" 98 : 27, // 27 = "b" 99 : 28, // 28 = "c" 100 : 29, // 29 = "d" 101 : 30, // 30 = "e" 102 : 31, // 31 = "f" 103 : 32, // 32 = "g" 104 : 33, // 33 = "h" 105 : 34, // 34 = "i" 106 : 35, // 35 = "j" 107 : 36, // 36 = "k" 108 : 37, // 37 = "l" 109 : 38, // 38 = "m" 110 : 39, // 39 = "n" 111 : 40, // 40 = "o" 112 : 41, // 41 = "p" 113 : 42, // 42 = "q" 114 : 43, // 43 = "r" 115 : 44, // 44 = "s" 116 : 45, // 45 = "t" 117 : 46, // 46 = "u" 118 : 47, // 47 = "v" 119 : 48, // 48 = "w" 120 : 49, // 49 = "x" 121 : 50, // 50 = "y" 122 : 51, // 51 = "z" 48 : 52, // 52 = "0" 49 : 53, // 53 = "1" 50 : 54, // 54 = "2" 51 : 55, // 55 = "3" 52 : 56, // 56 = "4" 53 : 57, // 57 = "5" 54 : 58, // 58 = "6" 55 : 59, // 59 = "7" 56 : 60, // 60 = "8" 57 : 61, // 61 = "9" 45 : 62, // 62 = "-" 95 : 63, // 63 = "_" ] public static func encodeString(str: String) -> String { // Get the ascii representation and return let data = str.data(using: .utf8)! let encData = encode(data: [UInt8](data)) let retStr = String(data: Data(encData), encoding: .utf8)! return retStr } public static func encode(data: [UInt8]) -> [UInt8] { var result: [UInt8] = [] let size = data.count // Step through 3 bytes at a time for i in stride(from: 0, to: size, by: 3) { // Get the first 6 bits, and add the Base64 letter let first = data[i] >> 2 result.append(filenameSafeAlphabet[Int(first)]) // Get the remaining 2 bits from the previous byte var second = (data[i] & 0b11) << 4 // If there is more of the array, add the next 4 bits from byte 2, or return with padding for the 3rd and 4th characters if i + 1 < size { second |= (data[i + 1] & 0b11110000) >> 4 result.append(filenameSafeAlphabet[Int(second)]) } else { result.append(filenameSafeAlphabet[Int(second)]) result.append(padding) result.append(padding) return result } // Get the remaining 4 bits from the previous byte var third = (data[i + 1] & 0b1111) << 2 // If there is more of the array, add the next 2 bits from byte 3, or return with padding for the 4th character if i + 2 < size { third |= (data[i + 2] & 0b11000000) >> 6 result.append(filenameSafeAlphabet[Int(third)]) } else { result.append(filenameSafeAlphabet[Int(third)]) result.append(padding) return result } // Get the remaining 6 bits from the previous byte, add to the result let forth = data[i + 2] & 0b00111111 result.append(filenameSafeAlphabet[Int(forth)]) } return result } public static func decode(data: [UInt8]) -> [UInt8] { var result: [UInt8] = [] let size = data.count // We loop over the 4 letters at a time // We know it is padded, so we dont need to check for size for i in stride(from: 0, to: size, by: 4) { // Get the 4 letters, then get back to their non-index values let first = safeAlphabetToIndex[data[i]]! let second = safeAlphabetToIndex[data[i + 1]]! let third = safeAlphabetToIndex[data[i + 2]]! let forth = safeAlphabetToIndex[data[i + 3]]! // Get the 3 binary letters from the four 6-bit ones let l1 = first << 2 | ((second & 0b110000) >> 4) let l2 = ((second & 0b1111) << 4) | ((third & 0b111100) >> 2) let l3 = ((third & 0b11) << 6) | forth // Return the letters if they arent empty result.append(l1) if l3 != 0 { result.append(l2) result.append(l3) } else if l2 != 0 { result.append(l2) } } return result } public static func decodeString(str: String) -> String { // Get the ascii representation and return let data = str.data(using: .ascii)! let decData = decode(data: [UInt8](data)) let retStr = String(data: Data(decData), encoding: .ascii)! return retStr } }