Module:Base64: Difference between revisions

From Celeste Wiki
Jump to navigation Jump to search
Content added Content deleted
No edit summary
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 11: Line 11:
local data
local data
local encode
local encode
local input
if frame == mw.getCurrentFrame() then
if frame == mw.getCurrentFrame() then
args = frame.args
args = frame.args
Line 17: Line 16:
args = frame
args = frame
end
end
input = args.data
data = frame.args.data
encode = args.encode
encode = frame.args.encode
yesno(encode, false)
yesno(encode, false)


return "Sorry, I gave up that time"
bit = 0
require "bit"

base64 = {}

--- octet -> char encoding.
local ENCODABET = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '-', '_'
}

--- char -> octet encoding.
local DECODABET = {}
for i, v in ipairs(ENCODABET) do
DECODABET[v] = i - 1
end

local PAD = nil

--- Converts a 6-bit octet into the associated Base64 character.
--
-- @param octet A 6-bit integer.
-- @return The Base64 representation of the character
local function toChar (octet)
return assert(ENCODABET[octet + 1], "No Base64 character for octet: "..tostring(octet))
end

--- Converts a Base64 character into the associated octet.
--
-- @param char The single Base64 character.
-- @return The 6-bit integer representing the Base64 character.
local function toOctet (char)
return assert(DECODABET[char], "Not a valid Base64 character: "..tostring(char))
end

--- Encodes a string into a Base64 string.
-- The input can be any string of arbitrary bytes.
-- If the input is not a string, or the string is empty, an error will be thrown.
--
-- @param input The input string.
-- @return The Base64 representation of the input string.
function base64.encode (input)
assert(type(input) == "string", "Invalid input, expected type string but got: "..tostring(input).." as a: "..type(input))
assert(#input > 0, "Invalid input, cannot encode an empty string.")
local bytes = { input:byte(i, #input) }

local out = {}
-- Go through each triplet of 3 bytes, which produce 4 octets.
local i = 1
while i <= #bytes - 2 do
local buffer = 0
-- Fill the buffer with the bytes, producing a 24-bit integer.
local b = bit.blshift(bytes[i], 16)
b = bit.band(b, 0xff0000)
buffer = bit.bor(buffer, b)
b = bit.blshift(bytes[i + 1], 8)
b = bit.band(b, 0xff00)
buffer = bit.bor(buffer, b)
b = bit.band(bytes[i + 2], 0xff)
buffer = bit.bor(buffer, b)
-- Read out the 4 octets into the output buffer.
b = bit.blogic_rshift(buffer, 18)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.blogic_rshift(buffer, 12)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.blogic_rshift(buffer, 6)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.band(buffer, 0x3f)
out[#out + 1] = toChar(b)
i = i + 3
end
-- Special case 1: One byte extra, will produce 2 octets.
if #bytes % 3 == 1 then
local buffer = bit.blshift(bytes[i], 16)
buffer = bit.band(buffer, 0xff0000)
local b = bit.blogic_rshift(buffer, 18)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.blogic_rshift(buffer, 12)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
out[#out + 1] = PAD
out[#out + 1] = PAD
-- Special case 2: Two bytes extra, will produce 3 octets.
elseif #bytes % 3 == 2 then
local buffer = 0
local b = bit.blshift(bytes[i], 16)
b = bit.band(b, 0xff0000)
buffer = bit.bor(buffer, b)
b = bit.blshift(bytes[i + 1], 8)
b = bit.band(b, 0xff00)
buffer = bit.bor(buffer, b)

b = bit.blogic_rshift(buffer, 18)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.blogic_rshift(buffer, 12)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
b = bit.blogic_rshift(buffer, 6)
b = bit.band(b, 0x3f)
out[#out + 1] = toChar(b)
out[#out + 1] = PAD
end
return table.concat(out)
end

--- Decodes a Base64 string into an output string of arbitrary bytes.
-- If the input is not a string, or the string is empty, or the string is not well-formed Base64, an error will be thrown.
--
-- @param input The Base64 input to decode.
-- @return The decoded Base64 string, as a string of bytes.
function base64.decode (input)
assert(type(input) == "string", "Invalid input, expected type string but got: "..tostring(input).." as a: "..type(input))
assert(#input > 0, "Invalid input, cannot decode an empty string.")
local length = #input
-- Ignore any padding.
if PAD then
length = input:find(PAD, 1, true) or (length + 1)
length = length - 1
end
assert(length > 0, "Invalid input, cannot decode a padded string with no bytes: "..tostring(input))
local out = {}
-- Go through each group of 4 octets to obtain 3 bytes.
local i = 1
while i <= length - 3 do
local buffer = 0
-- Read the 4 octets into the buffer, producing a 24-bit integer.
local b = toOctet(input:sub(i, i))
b = bit.blshift(b, 18)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
b = bit.blshift(b, 12)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
b = bit.blshift(b, 6)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
buffer = bit.bor(buffer, b)
i = i + 1
-- Append the 3 re-constructed bytes into the output buffer.
b = bit.blogic_rshift(buffer, 16)
b = bit.band(b, 0xff)
out[#out + 1] = b
b = bit.blogic_rshift(buffer, 8)
b = bit.band(b, 0xff)
out[#out + 1] = b
b = bit.band(buffer, 0xff)
out[#out + 1] = b
end

-- Special case 1: Only 2 octets remain, producing 1 byte.
if length % 4 == 2 then
local buffer = 0

local b = toOctet(input:sub(i, i))
b = bit.blshift(b, 18)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
b = bit.blshift(b, 12)
buffer = bit.bor(buffer, b)
i = i + 1
b = bit.blogic_rshift(buffer, 16)
b = bit.band(b, 0xff)
out[#out + 1] = b
-- Special case 2: Only 3 octets remain, producing 2 bytes.
elseif length % 4 == 3 then
local buffer = 0
local b = toOctet(input:sub(i, i))
b = bit.blshift(b, 18)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
b = bit.blshift(b, 12)
buffer = bit.bor(buffer, b)
i = i + 1
b = toOctet(input:sub(i, i))
b = bit.blshift(b, 6)
buffer = bit.bor(buffer, b)
i = i + 1

b = bit.blogic_rshift(buffer, 16)
b = bit.band(b, 0xff)
out[#out + 1] = b
b = bit.blogic_rshift(buffer, 8)
b = bit.band(b, 0xff)
out[#out + 1] = b
-- Special case 3: One octet remains, we can't get any bytes out of this, throw error.
elseif length % 4 == 1 then
error("Invalid length input string, extra character: "..tostring(input:sub(i, i)))
end

return string.char(unpack(out))
end



if (encode == true) then
base64.encode(input)
else
base64.decode(input)
end
end
end



Latest revision as of 15:48, 14 January 2021

Documentation for this module may be created at Module:Base64/doc

-- Made by [[User:Celeste]] - Licensed under GPLv3

local p = {}

local yesno = require('Module:Yesno') -- invoke Yesno

local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -- You will need this for encoding/decoding

function p.base64(frame)
	local args
	local data
	local encode
	if frame == mw.getCurrentFrame() then
    	args = frame.args
	else
    	args = frame
	end
	data = frame.args.data
	encode = frame.args.encode
	yesno(encode, false)

	return "Sorry, I gave up that time"
end

return p