Module:Base64: Difference between revisions

From Celeste Wiki
Jump to navigation Jump to search
Content added Content deleted
No edit summary
No edit summary
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 = args.data
encode = args.encode
encode = args.encode
yesno(encode, false)
yesno(encode, false)


function enc(data)
bit = 0
return ((data:gsub('.', function(x)
require "bit"
local r,b='',x:byte()

for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
base64 = {}
return r;

end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
--- octet -> char encoding.
if (#x < 6) then return '' end
local ENCODABET = {
local c=0
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
return b:sub(c+1,c+1)
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
end)..({ '', '==', '=' })[#data%3+1])
'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
end


-- decoding
local PAD = nil
function dec(data)

data = string.gsub(data, '[^'..b..'=]', '')
--- Converts a 6-bit octet into the associated Base64 character.
return (data:gsub('.', function(x)
--
if (x == '=') then return '' end
-- @param octet A 6-bit integer.
local r,f='',(b:find(x)-1)
-- @return The Base64 representation of the character
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
local function toChar (octet)
return r;
return assert(ENCODABET[octet + 1], "No Base64 character for octet: "..tostring(octet))
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(7-i) or 0) end
return string.char(c)
end))
end
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
if (encode == true) then
enc(data)
base64.encode(input)
else
else
dec(data)
base64.decode(input)
end
end
end
end

Revision as of 15:16, 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 = args.data
	encode = args.encode
	yesno(encode, false)

function enc(data)
    return ((data:gsub('.', function(x) 
        local r,b='',x:byte()
        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#data%3+1])
end

-- decoding
function dec(data)
    data = string.gsub(data, '[^'..b..'=]', '')
    return (data:gsub('.', function(x)
        if (x == '=') then return '' end
        local r,f='',(b:find(x)-1)
        for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
        return r;
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
        if (#x ~= 8) then return '' end
        local c=0
        for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(7-i) or 0) end
        return string.char(c)
    end))
end

if (encode == true) then
		enc(data)
	else
		dec(data)
end
end

return p