forked from rojo-rbx/rojo
139 lines
3.1 KiB
Lua
139 lines
3.1 KiB
Lua
-- Thanks to Tiffany352 for this base64 implementation!
|
|
|
|
local floor = math.floor
|
|
local char = string.char
|
|
|
|
local function encodeBase64(str)
|
|
local out = {}
|
|
local nOut = 0
|
|
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
local strLen = #str
|
|
|
|
-- 3 octets become 4 hextets
|
|
for i = 1, strLen - 2, 3 do
|
|
local b1, b2, b3 = str:byte(i, i + 3)
|
|
local word = b3 + b2 * 256 + b1 * 256 * 256
|
|
|
|
local h4 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h3 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h2 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h1 = word % 64 + 1
|
|
|
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
|
out[nOut + 3] = alphabet:sub(h3, h3)
|
|
out[nOut + 4] = alphabet:sub(h4, h4)
|
|
nOut = nOut + 4
|
|
end
|
|
|
|
local remainder = strLen % 3
|
|
|
|
if remainder == 2 then
|
|
-- 16 input bits -> 3 hextets (2 full, 1 partial)
|
|
local b1, b2 = str:byte(-2, -1)
|
|
-- partial is 4 bits long, leaving 2 bits of zero padding ->
|
|
-- offset = 4
|
|
local word = b2 * 4 + b1 * 4 * 256
|
|
|
|
local h3 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h2 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h1 = word % 64 + 1
|
|
|
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
|
out[nOut + 3] = alphabet:sub(h3, h3)
|
|
out[nOut + 4] = "="
|
|
elseif remainder == 1 then
|
|
-- 8 input bits -> 2 hextets (2 full, 1 partial)
|
|
local b1 = str:byte(-1, -1)
|
|
-- partial is 2 bits long, leaving 4 bits of zero padding ->
|
|
-- offset = 16
|
|
local word = b1 * 16
|
|
|
|
local h2 = word % 64 + 1
|
|
word = floor(word / 64)
|
|
local h1 = word % 64 + 1
|
|
|
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
|
out[nOut + 3] = "="
|
|
out[nOut + 4] = "="
|
|
end
|
|
-- if the remainder is 0, then no work is needed
|
|
|
|
return table.concat(out, "")
|
|
end
|
|
|
|
local function decodeBase64(str)
|
|
local out = {}
|
|
local nOut = 0
|
|
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
local strLen = #str
|
|
local acc = 0
|
|
local nAcc = 0
|
|
|
|
local alphabetLut = {}
|
|
for i = 1, #alphabet do
|
|
alphabetLut[alphabet:sub(i, i)] = i - 1
|
|
end
|
|
|
|
-- 4 hextets become 3 octets
|
|
for i = 1, strLen do
|
|
local ch = str:sub(i, i)
|
|
local byte = alphabetLut[ch]
|
|
if byte then
|
|
acc = acc * 64 + byte
|
|
nAcc = nAcc + 1
|
|
end
|
|
|
|
if nAcc == 4 then
|
|
local b3 = acc % 256
|
|
acc = floor(acc / 256)
|
|
local b2 = acc % 256
|
|
acc = floor(acc / 256)
|
|
local b1 = acc % 256
|
|
|
|
out[nOut + 1] = char(b1)
|
|
out[nOut + 2] = char(b2)
|
|
out[nOut + 3] = char(b3)
|
|
nOut = nOut + 3
|
|
nAcc = 0
|
|
acc = 0
|
|
end
|
|
end
|
|
|
|
if nAcc == 3 then
|
|
-- 3 hextets -> 16 bit output
|
|
acc = acc * 64
|
|
acc = floor(acc / 256)
|
|
local b2 = acc % 256
|
|
acc = floor(acc / 256)
|
|
local b1 = acc % 256
|
|
|
|
out[nOut + 1] = char(b1)
|
|
out[nOut + 2] = char(b2)
|
|
elseif nAcc == 2 then
|
|
-- 2 hextets -> 8 bit output
|
|
acc = acc * 64
|
|
acc = floor(acc / 256)
|
|
acc = acc * 64
|
|
acc = floor(acc / 256)
|
|
local b1 = acc % 256
|
|
|
|
out[nOut + 1] = char(b1)
|
|
elseif nAcc == 1 then
|
|
error("Base64 has invalid length")
|
|
end
|
|
|
|
return table.concat(out, "")
|
|
end
|
|
|
|
return {
|
|
decode = decodeBase64,
|
|
encode = encodeBase64,
|
|
} |