basexx.lua 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. --------------------------------------------------------------------------------
  2. -- util functions
  3. --------------------------------------------------------------------------------
  4. local function divide_string( str, max )
  5. local result = {}
  6. local start = 1
  7. for i = 1, #str do
  8. if i % max == 0 then
  9. table.insert( result, str:sub( start, i ) )
  10. start = i + 1
  11. elseif i == #str then
  12. table.insert( result, str:sub( start, i ) )
  13. end
  14. end
  15. return result
  16. end
  17. local function number_to_bit( num, length )
  18. local bits = {}
  19. while num > 0 do
  20. local rest = math.floor( math.fmod( num, 2 ) )
  21. table.insert( bits, rest )
  22. num = ( num - rest ) / 2
  23. end
  24. while #bits < length do
  25. table.insert( bits, "0" )
  26. end
  27. return string.reverse( table.concat( bits ) )
  28. end
  29. local function ignore_set( str, set )
  30. if set then
  31. str = str:gsub( "["..set.."]", "" )
  32. end
  33. return str
  34. end
  35. local function pure_from_bit( str )
  36. return ( str:gsub( '........', function ( cc )
  37. return string.char( tonumber( cc, 2 ) )
  38. end ) )
  39. end
  40. local function unexpected_char_error( str, pos )
  41. local c = string.sub( str, pos, pos )
  42. return string.format( "unexpected character at position %d: '%s'", pos, c )
  43. end
  44. --------------------------------------------------------------------------------
  45. local basexx = {}
  46. --------------------------------------------------------------------------------
  47. -- base2(bitfield) decode and encode function
  48. --------------------------------------------------------------------------------
  49. local bitMap = { o = "0", i = "1", l = "1" }
  50. function basexx.from_bit( str, ignore )
  51. str = ignore_set( str, ignore )
  52. str = string.lower( str )
  53. str = str:gsub( '[ilo]', function( c ) return bitMap[ c ] end )
  54. local pos = string.find( str, "[^01]" )
  55. if pos then return nil, unexpected_char_error( str, pos ) end
  56. return pure_from_bit( str )
  57. end
  58. function basexx.to_bit( str )
  59. return ( str:gsub( '.', function ( c )
  60. local byte = string.byte( c )
  61. local bits = {}
  62. for _ = 1,8 do
  63. table.insert( bits, byte % 2 )
  64. byte = math.floor( byte / 2 )
  65. end
  66. return table.concat( bits ):reverse()
  67. end ) )
  68. end
  69. --------------------------------------------------------------------------------
  70. -- base16(hex) decode and encode function
  71. --------------------------------------------------------------------------------
  72. function basexx.from_hex( str, ignore )
  73. str = ignore_set( str, ignore )
  74. local pos = string.find( str, "[^%x]" )
  75. if pos then return nil, unexpected_char_error( str, pos ) end
  76. return ( str:gsub( '..', function ( cc )
  77. return string.char( tonumber( cc, 16 ) )
  78. end ) )
  79. end
  80. function basexx.to_hex( str )
  81. return ( str:gsub( '.', function ( c )
  82. return string.format('%02X', string.byte( c ) )
  83. end ) )
  84. end
  85. --------------------------------------------------------------------------------
  86. -- generic function to decode and encode base32/base64
  87. --------------------------------------------------------------------------------
  88. local function from_basexx( str, alphabet, bits )
  89. local result = {}
  90. for i = 1, #str do
  91. local c = string.sub( str, i, i )
  92. if c ~= '=' then
  93. local index = string.find( alphabet, c, 1, true )
  94. if not index then
  95. return nil, unexpected_char_error( str, i )
  96. end
  97. table.insert( result, number_to_bit( index - 1, bits ) )
  98. end
  99. end
  100. local value = table.concat( result )
  101. local pad = #value % 8
  102. return pure_from_bit( string.sub( value, 1, #value - pad ) )
  103. end
  104. local function to_basexx( str, alphabet, bits, pad )
  105. local bitString = basexx.to_bit( str )
  106. local chunks = divide_string( bitString, bits )
  107. local result = {}
  108. for _,value in ipairs( chunks ) do
  109. if ( #value < bits ) then
  110. value = value .. string.rep( '0', bits - #value )
  111. end
  112. local pos = tonumber( value, 2 ) + 1
  113. table.insert( result, alphabet:sub( pos, pos ) )
  114. end
  115. table.insert( result, pad )
  116. return table.concat( result )
  117. end
  118. --------------------------------------------------------------------------------
  119. -- rfc 3548: http://www.rfc-editor.org/rfc/rfc3548.txt
  120. --------------------------------------------------------------------------------
  121. local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  122. local base32PadMap = { "", "======", "====", "===", "=" }
  123. function basexx.from_base32( str, ignore )
  124. str = ignore_set( str, ignore )
  125. return from_basexx( string.upper( str ), base32Alphabet, 5 )
  126. end
  127. function basexx.to_base32( str )
  128. return to_basexx( str, base32Alphabet, 5, base32PadMap[ #str % 5 + 1 ] )
  129. end
  130. --------------------------------------------------------------------------------
  131. -- crockford: http://www.crockford.com/wrmg/base32.html
  132. --------------------------------------------------------------------------------
  133. local crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
  134. local crockfordMap = { O = "0", I = "1", L = "1" }
  135. function basexx.from_crockford( str, ignore )
  136. str = ignore_set( str, ignore )
  137. str = string.upper( str )
  138. str = str:gsub( '[ILOU]', function( c ) return crockfordMap[ c ] end )
  139. return from_basexx( str, crockfordAlphabet, 5 )
  140. end
  141. function basexx.to_crockford( str )
  142. return to_basexx( str, crockfordAlphabet, 5, "" )
  143. end
  144. --------------------------------------------------------------------------------
  145. -- base64 decode and encode function
  146. --------------------------------------------------------------------------------
  147. local base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"..
  148. "abcdefghijklmnopqrstuvwxyz"..
  149. "0123456789+/"
  150. local base64PadMap = { "", "==", "=" }
  151. function basexx.from_base64( str, ignore )
  152. str = ignore_set( str, ignore )
  153. return from_basexx( str, base64Alphabet, 6 )
  154. end
  155. function basexx.to_base64( str )
  156. return to_basexx( str, base64Alphabet, 6, base64PadMap[ #str % 3 + 1 ] )
  157. end
  158. --------------------------------------------------------------------------------
  159. -- URL safe base64 decode and encode function
  160. --------------------------------------------------------------------------------
  161. local url64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"..
  162. "abcdefghijklmnopqrstuvwxyz"..
  163. "0123456789-_"
  164. function basexx.from_url64( str, ignore )
  165. str = ignore_set( str, ignore )
  166. return from_basexx( str, url64Alphabet, 6 )
  167. end
  168. function basexx.to_url64( str )
  169. return to_basexx( str, url64Alphabet, 6, "" )
  170. end
  171. --------------------------------------------------------------------------------
  172. --
  173. --------------------------------------------------------------------------------
  174. local function length_error( len, d )
  175. return string.format( "invalid length: %d - must be a multiple of %d", len, d )
  176. end
  177. local z85Decoder = { 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
  178. 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
  179. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  180. 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
  181. 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
  182. 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
  183. 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
  184. 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
  185. 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
  186. 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  187. 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
  188. 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00 }
  189. function basexx.from_z85( str, ignore )
  190. str = ignore_set( str, ignore )
  191. if ( #str % 5 ) ~= 0 then
  192. return nil, length_error( #str, 5 )
  193. end
  194. local result = {}
  195. local value = 0
  196. for i = 1, #str do
  197. local index = string.byte( str, i ) - 31
  198. if index < 1 or index >= #z85Decoder then
  199. return nil, unexpected_char_error( str, i )
  200. end
  201. value = ( value * 85 ) + z85Decoder[ index ]
  202. if ( i % 5 ) == 0 then
  203. local divisor = 256 * 256 * 256
  204. while divisor ~= 0 do
  205. local b = math.floor( value / divisor ) % 256
  206. table.insert( result, string.char( b ) )
  207. divisor = math.floor( divisor / 256 )
  208. end
  209. value = 0
  210. end
  211. end
  212. return table.concat( result )
  213. end
  214. local z85Encoder = "0123456789"..
  215. "abcdefghijklmnopqrstuvwxyz"..
  216. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"..
  217. ".-:+=^!/*?&<>()[]{}@%$#"
  218. function basexx.to_z85( str )
  219. if ( #str % 4 ) ~= 0 then
  220. return nil, length_error( #str, 4 )
  221. end
  222. local result = {}
  223. local value = 0
  224. for i = 1, #str do
  225. local b = string.byte( str, i )
  226. value = ( value * 256 ) + b
  227. if ( i % 4 ) == 0 then
  228. local divisor = 85 * 85 * 85 * 85
  229. while divisor ~= 0 do
  230. local index = ( math.floor( value / divisor ) % 85 ) + 1
  231. table.insert( result, z85Encoder:sub( index, index ) )
  232. divisor = math.floor( divisor / 85 )
  233. end
  234. value = 0
  235. end
  236. end
  237. return table.concat( result )
  238. end
  239. --------------------------------------------------------------------------------
  240. return basexx