Base Sixty Four

lua-users home
wiki

下面的实现提供了一些Base64编码/解码函数的示例(背景信息请参见 [Wikipedia: Base64])。

自上次实现以来,我进行了一场关于最小的JavaScript Base64编解码器的竞赛? — 将新的结果转换为Lua,得到了以下非常小巧且快速的编解码器。

#!/usr/bin/env lua
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
-- licensed under the terms of the LGPL2

-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

-- encoding
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^(8-i) or 0) end
        return string.char(c)
    end))
end

-- command line if not called as library
if (arg ~= nil) then
	local func = 'enc'
	for n,v in ipairs(arg) do
		if (n > 0) then
			if (v == "-h") then print "base64.lua [-e] [-d] text/data" break
			elseif (v == "-e") then func = 'enc'
			elseif (v == "-d") then func = 'dec'
			else print(_G[func](v)) end
		end
	end
else
	module('base64',package.seeall)
end

Base64 也实现在 LuaSocket 的 b64/unb64 方法中?[1]),但你可能仍希望使用这个代码片段,因为它依赖少 — 并且它是一个关于如何在没有实际位移或OR运算符的情况下执行逻辑运算的很好的教育示例。

如果有人在以前版本的base64中遇到了严重错误,我深表歉意 - 并非所有逻辑函数都能轻易地用数学函数替代,所以我不得不另辟蹊径。下面的代码不像mime C-Module那样快 — 然而,在较小值的情况下,加载时间也可能使程序变慢。在此处查找最新(已更正)版本。

#!/usr/bin/env lua
-- working lua base64 codec (c) 2006-2008 by Alex Kloss
-- compatible with lua 5.1
-- http://www.it-rfc.de
-- licensed under the terms of the LGPL2

-- bitshift functions (<<, >> equivalent)
-- shift left
function lsh(value,shift)
	return (value*(2^shift)) % 256
end

-- shift right
function rsh(value,shift)
	return math.floor(value/2^shift) % 256
end

-- return single bit (for OR)
function bit(x,b)
	return (x % 2^b - x % 2^(b-1) > 0)
end

-- logic OR for number values
function lor(x,y)
	result = 0
	for p=1,8 do result = result + (((bit(x,p) or bit(y,p)) == true) and 2^(p-1) or 0) end
	return result
end

-- encryption table
local base64chars = {[0]='A',[1]='B',[2]='C',[3]='D',[4]='E',[5]='F',[6]='G',[7]='H',[8]='I',[9]='J',[10]='K',[11]='L',[12]='M',[13]='N',[14]='O',[15]='P',[16]='Q',[17]='R',[18]='S',[19]='T',[20]='U',[21]='V',[22]='W',[23]='X',[24]='Y',[25]='Z',[26]='a',[27]='b',[28]='c',[29]='d',[30]='e',[31]='f',[32]='g',[33]='h',[34]='i',[35]='j',[36]='k',[37]='l',[38]='m',[39]='n',[40]='o',[41]='p',[42]='q',[43]='r',[44]='s',[45]='t',[46]='u',[47]='v',[48]='w',[49]='x',[50]='y',[51]='z',[52]='0',[53]='1',[54]='2',[55]='3',[56]='4',[57]='5',[58]='6',[59]='7',[60]='8',[61]='9',[62]='-',[63]='_'}

-- function encode
-- encodes input string to base64.
function enc(data)
	local bytes = {}
	local result = ""
	for spos=0,string.len(data)-1,3 do
		for byte=1,3 do bytes[byte] = string.byte(string.sub(data,(spos+byte))) or 0 end
		result = string.format('%s%s%s%s%s',result,base64chars[rsh(bytes[1],2)],base64chars[lor(lsh((bytes[1] % 4),4), rsh(bytes[2],4))] or "=",((#data-spos) > 1) and base64chars[lor(lsh(bytes[2] % 16,2), rsh(bytes[3],6))] or "=",((#data-spos) > 2) and base64chars[(bytes[3] % 64)] or "=")
	end
	return result
end

-- decryption table
local base64bytes = {['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['-']=62,['_']=63,['=']=nil}

-- function decode
-- decode base64 input to string
function dec(data)
	local chars = {}
	local result=""
	for dpos=0,string.len(data)-1,4 do
		for char=1,4 do chars[char] = base64bytes[(string.sub(data,(dpos+char),(dpos+char)) or "=")] end
		result = string.format('%s%s%s%s',result,string.char(lor(lsh(chars[1],2), rsh(chars[2],4))),(chars[3] ~= nil) and string.char(lor(lsh(chars[2],4), rsh(chars[3],2))) or "",(chars[4] ~= nil) and string.char(lor(lsh(chars[3],6) % 192, (chars[4]))) or "")
	end
	return result
end

-- command line if not called as library
if (arg ~= nil) then
	local func = 'enc'
	for n,v in ipairs(arg) do
		if (n > 0) then
			if (v == "-h") then print "base64.lua [-e] [-d] text/data" break
			elseif (v == "-e") then func = 'enc'
			elseif (v == "-d") then func = 'dec'
			else print(_G[func](v)) end
		end
	end
else
	module('base64',package.seeall)
end

另请参见与lua 5.0兼容的同一版本。

-- working lua base64 codec (c) 2006-2008 by Alex Kloss
-- compatible with lua 5.0
-- http://www.it-rfc.de
-- licensed under the terms of the LGPL2

-- bitshift functions (<<, >> equivalent)
-- shift left
function lsh(value,shift)
	return math.mod((value*(2^shift)), 256)
end

-- shift right
function rsh(value,shift)
	return math.mod(math.floor(value/2^shift), 256)
end

-- return single bit (for OR)
function bit(x,b)
	return (math.mod(x, 2^b) - math.mod(x, 2^(b-1)) > 0)
end

-- logic OR for number values
function lor(x,y)
	result = 0
	for p=1,8 do result = result + (((bit(x,p) or bit(y,p)) == true) and 2^(p-1) or 0) end
	return result
end

-- encryption table
local base64chars = {[0]='A',[1]='B',[2]='C',[3]='D',[4]='E',[5]='F',[6]='G',[7]='H',[8]='I',[9]='J',[10]='K',[11]='L',[12]='M',[13]='N',[14]='O',[15]='P',[16]='Q',[17]='R',[18]='S',[19]='T',[20]='U',[21]='V',[22]='W',[23]='X',[24]='Y',[25]='Z',[26]='a',[27]='b',[28]='c',[29]='d',[30]='e',[31]='f',[32]='g',[33]='h',[34]='i',[35]='j',[36]='k',[37]='l',[38]='m',[39]='n',[40]='o',[41]='p',[42]='q',[43]='r',[44]='s',[45]='t',[46]='u',[47]='v',[48]='w',[49]='x',[50]='y',[51]='z',[52]='0',[53]='1',[54]='2',[55]='3',[56]='4',[57]='5',[58]='6',[59]='7',[60]='8',[61]='9',[62]='-',[63]='_'}

-- function encode
-- encodes input string to base64.
function enc(data)
	local bytes = {}
	local result = ""
	for spos=0,string.len(data)-1,3 do
		for byte=1,3 do bytes[byte] = string.byte(string.sub(data,(spos+byte))) or 0 end
		result = string.format('%s%s%s%s%s',
			result,
			base64chars[rsh(bytes[1],2)],
			base64chars[lor(lsh((math.mod(bytes[1], 4)),4), rsh(bytes[2],4))] or "=",
			((string.len(data)-spos) > 1) and base64chars[lor(lsh(
				math.mod(bytes[2], 16)
			,2), rsh(bytes[3],6))] or "=",
			((string.len(data)-spos) > 2) and base64chars[(math.mod(bytes[3], 64))] or "="
		)
	end
	return result
end

-- decryption table
local base64bytes = {['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['-']=62,['_']=63,['=']=nil}

-- function decode
-- decode base64 input to string
function dec(data)
	local chars = {}
	local result=""
	for dpos=0,string.len(data)-1,4 do
		for char=1,4 do chars[char] = base64bytes[(string.sub(data,(dpos+char),(dpos+char)) or "=")] end
		result = string.format('%s%s%s%s',
			result,
			string.char(lor(lsh(chars[1],2), rsh(chars[2],4))),
			(chars[3] ~= nil) and string.char(lor(lsh(chars[2],4), rsh(chars[3],2))) or "",
			(chars[4] ~= nil) and string.char(lor(math.mod(lsh(chars[3],6), 192), (chars[4]))) or ""
		)
	end
	return result
end

-- command line if not called as library
if (arg ~= nil) then
	local func = 'enc'
	for n,v in ipairs(arg) do
		if (n > 0) then
			if (v == "-h") then print "base64.lua [-e] [-d] text/data" break
			elseif (v == "-e") then func = 'enc'
			elseif (v == "-d") then func = 'dec'
			else print(_G[func](v)) end
		end
	end
else
	module('base64',package.seeall)
end

另外,我收到一封邮件,有人在使用我的代码时没有注明我的版权和许可。如果你需要不同于代码中明确说明的许可,请随时通过我的电子邮件地址与我联系。

在 Lua 5.3 中使用二进制运算符

我今天写了下面的代码片段,以利用Lua5.3中的新二进制运算符来加速我的应用程序中的base64编码,这对其他人可能也有用。我认为该算法几乎是最优的,但我乐于接受任何建议和改进。

每三个输入字节通过下面的二进制运算映射到四个输出字节。

        
         a                   b                   c
  +-----------------+-----------------+-----------------+
  | 0 0 0 0 0 0 1 1 | 1 1 1 1 2 2 2 2 | 2 2 3 3 3 3 3 3 |
  +|- - - - - -|- - + - - - -|- - - - + - -|- - - - - -|+
  /           /              |              \           \
 /           /               |               \           \
     a>>2     (a&3)<<4|b>>4    (b&15)<<2|c>>6      c&63

输入将被填充为总是三个字节的倍数,在计算填充后字符串的base64后,输出的填充字符'='将被修正。

local bs = { [0] =
   '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','+','/',
}

local function base64(s)
   local byte, rep = string.byte, string.rep
   local pad = 2 - ((#s-1) % 3)
   s = (s..rep('\0', pad)):gsub("...", function(cs)
      local a, b, c = byte(cs, 1, 3)
      return bs[a>>2] .. bs[(a&3)<<4|b>>4] .. bs[(b&15)<<2|c>>6] .. bs[c&63]
   end)
   return s:sub(1, #s-pad) .. rep('=', pad)
end


assert(base64("") == "")
assert(base64("f") == "Zg==")
assert(base64("fo") == "Zm8=")
assert(base64("foo") == "Zm9v")
assert(base64("foob") == "Zm9vYg==")
assert(base64("fooba") == "Zm9vYmE=")
assert(base64("foobar") == "Zm9vYmFy")

另请参阅


RecentChanges · preferences
编辑 · 历史
最后编辑于 2014年11月4日 12:56 GMT (差异)