Base Sixty Four |
|
自上次實作之後,我舉辦了一場小型的競賽,要選出 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
LuaSocket 也在 b64
/unb64
方法中實作了 Base64? ([1]),但您可能還是想使用這個片段程式,因為它的需求較少,而且它是一個有教育價值的範例,說明如何執行邏輯運算,而不需要實際的位移或 OR 運算子。
我對於有人在使用我以前版本 base64 時遇到嚴重錯誤一事感到抱歉,並非所有邏輯函式都可以輕易以數學函式取代,因此我必須想辦法解決。以下程式碼雖然不如 mime C 模組快,但對於較小的值,它的載入時間也會使程式變慢。在此處找到最新(且已修正)的版本
#!/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
另外,我收到一位使用我程式碼的人寄來的一封電子郵件,此人並未承認我的著作權和授權。如果您需要程式碼中明確規定的其他授權,請隨時透過我的電子郵件地址與我聯絡。
為了在使用 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")
\
和 =>
是 function
和 return
的替代方案。