dev



local base64 = {}

local data = mw.loadData('Dev:Base64/data')
local DECODE = data.DECODE
local ENCODE = data.ENCODE
local i18n = require('Dev:i18n').loadMessages('Base64')

local extract = require('bit32').extract

local char = string.char
local byte = string.byte
local sub = string.sub
local trim = mw.text.trim

local PAD = byte('=')

function base64.encode(str)
	if str == '' then return '' end
	local result = {}
	local strlen = #str
	local lastn = strlen % 3
	local j = 0
	
	for i = 1, strlen - lastn, 3 do
		local first, second, third = byte(str, i, i + 2)
		local v = first * 0x10000 + second * 0x100 + third
		local a = v / 2^18 % 2^6; a = a - a % 1
		local b = v / 2^12 % 2^6; b = b - b % 1
		local c = v / 2^6 % 2^6; c = c - c % 1
		local d = v / 2^0 % 2^6; d = d - d % 1
		result[j + 1] = ENCODE[a + 1]
		result[j + 2] = ENCODE[b + 1]
		result[j + 3] = ENCODE[c + 1]
		result[j + 4] = ENCODE[d + 1]
		j = j + 4
	end
	
	if lastn == 2 then
		local first, second = byte(str, strlen - 1, strlen)
		local v = first * 0x10000 + second * 0x100
		local a = v / 2^18 % 2^6; a = a - a % 1
		local b = v / 2^12 % 2^6; b = b - b % 1
		local c = v / 2^6 % 2^6; c = c - c % 1
		result[j + 1] = ENCODE[a + 1]
		result[j + 2] = ENCODE[b + 1]
		result[j + 3] = ENCODE[c + 1]
		result[j + 4] = PAD
	elseif lastn == 1 then
		local v = byte(str, strlen) * 0x10000
		local a = v / 2^18 % 2^6; a = a - a % 1
		local b = v / 2^12 % 2^6; b = b - b % 1
		result[j + 1] = ENCODE[a + 1]
		result[j + 2] = ENCODE[b + 1]
		result[j + 3] = PAD
		result[j + 4] = PAD
	end
	
	return char(unpack(result))
end

-- for the reverse compatibility
base64.encoder = base64.encode

function base64.decode(str)
	str = trim(str)
	
	if string.match(str, data.pattern) ~= nil then
		return error(i18n:msg('error-ascii'))
	end
	
	if str == '' then return '' end

	local result = {}
	local strlen = #str
	
	if (strlen % 4 ~= 0) then
		if (strlen % 4 ~= 1) then
			str = str .. string.rep('=', 4 - (strlen % 4))
		else
			return error(i18n:msg('error-length'))
		end
	end
	
	strlen = #str
	
	local padding = sub(str, -2) == '==' and 2 or sub(str, -1) == '=' and 1 or 0
	local j = 0
	
	for i = 1, padding > 0 and strlen - 4 or strlen, 4 do
		local first, second, third, fourth = byte(str, i, i + 4)
		local v = DECODE[first+1] * 0x40000 + DECODE[second+1] * 0x1000 + DECODE[third+1] * 0x40 + DECODE[fourth+1]
		local a = v / 2^16 % 2^8; a = a - a % 1
		local b = v / 2^8 % 2^8; b = b - b % 1
		local c = v / 2^0 % 2^8; c = c - c % 1
		result[j + 1] = a
		result[j + 2] = b
		result[j + 3] = c
		j = j + 3
	end
	
	if padding == 1 then
		local first, second, third = byte(str, strlen - 3, strlen - 1)
		local v = DECODE[first+1] * 0x40000 + DECODE[second+1] * 0x1000 + DECODE[third+1] * 0x40
		local a = v / 2^16 % 2^8; a = a - a % 1
		local b = v / 2^8 % 2^8; b = b - b % 1
		result[j + 1] = a
		result[j + 2] = b
	elseif padding == 2 then
		local first, second = byte(str, strlen - 3, strlen - 2)
		local v = DECODE[first+1] * 0x40000 + DECODE[second+1] * 0x1000
		local a = v / 2^16 % 2^8; a = a - a % 1
		result[j + 1] = a
	end
	
	return char(unpack(result))
end

-- for the reverse compatibility
base64.decoder = base64.decode

return base64