AutoCat/AutoCat/ThirdParty/Base64FS.swift

263 lines
7.4 KiB
Swift

//
// 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
}
}