├── .busted ├── .travis.yml ├── CHANGELOG.adoc ├── LICENSE ├── README.adoc ├── dist ├── basexx-0.1.0-1.rockspec ├── basexx-0.1.1-1.rockspec ├── basexx-0.2.0-1.rockspec ├── basexx-0.3.0-1.rockspec ├── basexx-0.4.0-1.rockspec ├── basexx-0.4.1-1.rockspec └── basexx-scm-0.rockspec ├── lib └── basexx.lua └── test ├── base32_spec.lua ├── base64_spec.lua ├── bit_spec.lua ├── crockford_spec.lua ├── hex_spec.lua ├── url64_spec.lua └── z85_spec.lua /.busted: -------------------------------------------------------------------------------- 1 | return { 2 | default = { 3 | ROOT = { "test" }, 4 | verbose = true, 5 | lpath = "lib/?.lua" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | sudo: false 4 | 5 | env: 6 | matrix: 7 | - LUA="lua=5.1" 8 | - LUA="lua=5.2" 9 | - LUA="lua=5.3" 10 | - LUA="luajit=2.0" 11 | - LUA="luajit=2.1" 12 | 13 | branches: 14 | only: 15 | - master 16 | 17 | before_install: 18 | - pip install hererocks 19 | - hererocks base -r^ --$LUA 20 | - export PATH=$PATH:$PWD/base/bin 21 | - luarocks install busted 22 | 23 | script: 24 | - busted -v 25 | 26 | notifications: 27 | email: 28 | on_success: change 29 | on_failure: always 30 | -------------------------------------------------------------------------------- /CHANGELOG.adoc: -------------------------------------------------------------------------------- 1 | = changelog 2 | 3 | == from 0.4.0 to 0.4.1 4 | 5 | * fix: removed luacheck lint errors 6 | 7 | == from 0.3.0 to 0.4.0 8 | 9 | * removed unnecessary check 10 | * changed error handling 11 | ** the returned error value is now a message 12 | ** can potentially break existing code - please check your code! 13 | 14 | == from 0.2.0 to 0.3.0 15 | 16 | * fix: updated outdated README example for crockford 17 | * added the examples as tests for the crockford examples 18 | * improved from functions 19 | ** added the possibility to ignore characters in the encoded string 20 | ** handles now unknown characters in the encoded string without a crash 21 | 22 | == from 0.1.1 to 0.2.0 23 | 24 | * fix: supports now for unit tests busted 2.0 25 | * added the functions from_url64 / to_url64 and from_z85 / to_z85 26 | * removed the mapping for U and u in from crockford 27 | * internal: 28 | ** travis.ci support 29 | ** 80 lines width 30 | ** busted 2.0 support 31 | ** switched from MarkDown to AsciiDoc 32 | 33 | == from 0.1.0 to 0.1.1 34 | 35 | * fix: divider_string & number_to_bit should be local‎ 36 | * better README 37 | * internal: divided the unit tests into separate files 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 aiq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = basexx 2 | 3 | image::https://api.travis-ci.org/aiq/basexx.png[travis] 4 | 5 | A Lua library for base2, base16, base32, base64, base85 decoding and encoding of data strings. 6 | 7 | == API 8 | 9 | For every supported format has basexx a *_from_* and *_to_* function. 10 | 11 | The *_from_* functions have two parameters: 12 | 13 | * *str* which represent the encoded string that should be decoded 14 | * *ignore* is an optional set of characters that should be ignored in the string, see the *_from_bit_* and *_from_hex_* examples 15 | 16 | The *_from_* functions return a string if the string can be decoded, otherwise *_nil_* and an error message. 17 | 18 | The *_to_* functions have just one parameter: 19 | 20 | * *str* the data string that should be encoded 21 | 22 | The *_to_z85_* function can return *_nil_* and an error message, all other functions return allways the encoded string. 23 | 24 | === from_bit / to_bit 25 | 26 | Converts a byte string to a bitfield string. 27 | 28 | * 0, O and o maps to the same value 29 | * 1, I, i, L and l maps to the same value 30 | 31 | [source,lua] 32 | ---- 33 | basexx.to_bit( "ACDC" ) --> 01000001010000110100010001000011 34 | basexx.from_bit( "01000001010000110100010001000011" ) --> ACDC 35 | basexx.from_bit( "o1ooooo1o1oooo11" ) --> AC 36 | basexx.from_bit( "Oioooooi Oiooooii\n", " \n" ) --> AC 37 | ---- 38 | 39 | === from_hex / to_hex 40 | 41 | Converts a byte string to a uppercase http://tools.ietf.org/html/rfc3548#section-6[hex] data string. 42 | 43 | [source,lua] 44 | ---- 45 | basexx.to_hex( "Hello world!" ) --> 48656C6C6F20776F726C6421 46 | basexx.from_hex( "4865-6C6C 6F20-776F 726C-6421", "- " ) --> Hello world! 47 | basexx.from_hex( "48656c6c6f20776f726c6421" ) --> Hello world! 48 | ---- 49 | 50 | === from_base32 / to_base32 51 | 52 | Converts a byte string to a http://tools.ietf.org/html/rfc3548#section-5[base32(_rfc3548)] uppercase data string. 53 | 54 | * It's case insensitive 55 | 56 | [source,lua] 57 | ---- 58 | basexx.to_base32( "chunky bacon!" ) --> MNUHK3TLPEQGEYLDN5XCC=== 59 | basexx.from_base32( "MNUHK3TLPEQGEYLDN5XCC===" ) --> chunky bacon! 60 | ---- 61 | 62 | === from_crockford / to_crockford 63 | 64 | Converts a byte string to a http://www.crockford.com/wrmg/base32.html[base32(_crockford_)] uppercase data string. The optional check value is not implemented. 65 | 66 | * It's case insensitive 67 | * 1, I, i, L and l maps to the same value 68 | * 0, O and o maps to the same value 69 | 70 | [source,lua] 71 | ---- 72 | string.lower( basexx.to_crockford( "Hello World" ) ) --> 91jprv3f41bpywkccg 73 | basexx.from_crockford( "axqqeb10d5t20wk5c5p6ry90exqq4tvk44" ) --> Wow, it really works! 74 | ---- 75 | 76 | === from_base64 / to_base64 77 | 78 | Converts a byte string to a https://tools.ietf.org/html/rfc4648#section-4[base64] data string. 79 | 80 | [source,lua] 81 | ---- 82 | basexx.to_base64( "Man") --> TWFu 83 | basexx.from_base64( "TWFu" ) --> Man 84 | ---- 85 | 86 | === from_url64 / to_url64 87 | 88 | Same as above, but uses a https://tools.ietf.org/html/rfc4648#section-5[URL Safe base64] alphabet and no padding. 89 | 90 | === from_z85 / to_z85 91 | 92 | Converts a byte string to a http://rfc.zeromq.org/spec:32[base85(ZeroMQ)] data string. 93 | to_z85 expects only a binary string that length is divisible by 4 with no remainder, and from_z85 expects only printable a string that length is divisible by 5 with no remainder. 94 | 95 | [source,lua] 96 | ---- 97 | basexx.to_z85( "1234" ) --> f!$Kw 98 | basexx.from_z85( "f!$Kw" ) --> 1234 99 | ---- 100 | 101 | == Installation 102 | 103 | To install the version 0.4.1 of basexx use LuaRocks with the following line. 104 | 105 | ---- 106 | luarocks install basexx 107 | ---- 108 | 109 | 110 | If you want to use the current development version, clone this repository and use 111 | LuaRocks with the following command. 112 | 113 | ---- 114 | luarocks make dist/basexx-scm-0.rockspec 115 | ---- 116 | 117 | -------------------------------------------------------------------------------- /dist/basexx-0.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.1.0-1" 3 | description = { 4 | summary = "A base2, base32 and base64 library for Lua", 5 | detailed = "A library which provides base2(bitfield), base32(crock ford/rfc 3548), base64 decoding and encoding.", 6 | license = "MIT", 7 | homepage = "https://github.com/aiq/basexx" 8 | } 9 | dependencies = { 10 | "lua >= 5.1" 11 | } 12 | source = { 13 | url = "https://github.com/aiq/basexx/archive/v0.1.0.tar.gz", 14 | md5 = "66570a1e354ce0c919192c895a1ee8bb", 15 | dir = "basexx-0.1.0" 16 | } 17 | build = { 18 | type = 'builtin', 19 | modules = { 20 | basexx = "lib/basexx.lua" 21 | }, 22 | copy_directories = { "test" } 23 | } -------------------------------------------------------------------------------- /dist/basexx-0.1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.1.1-1" 3 | description = { 4 | summary = "A base2, base32 and base64 library for Lua", 5 | detailed = "A library which provides base2(bitfield), base32(crock ford/rfc 3548), base64 decoding and encoding.", 6 | license = "MIT", 7 | homepage = "https://github.com/aiq/basexx" 8 | } 9 | dependencies = { 10 | "lua >= 5.1" 11 | } 12 | source = { 13 | url = "https://github.com/aiq/basexx/archive/v0.1.1.tar.gz", 14 | md5 = "6481b5f980c0da1248821273271290be", 15 | dir = "basexx-0.1.1" 16 | } 17 | build = { 18 | type = 'builtin', 19 | modules = { 20 | basexx = "lib/basexx.lua" 21 | }, 22 | copy_directories = { "test" } 23 | } -------------------------------------------------------------------------------- /dist/basexx-0.2.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.2.0-1" 3 | 4 | description = { 5 | summary = "A base2, base16, base32, base64 and base85 library for Lua", 6 | detailed = "A Lua library which provides base2(bitfield), base16(hex), base32(crockford/rfc), base64(rfc/url), base85(z85) decoding and encoding.", 7 | license = "MIT", 8 | homepage = "https://github.com/aiq/basexx" 9 | } 10 | 11 | source = { 12 | url = "https://github.com/aiq/basexx/archive/v0.2.0.tar.gz", 13 | md5 = "2c40dd08dbd39310d312756f8eda178b", 14 | dir = "basexx-0.2.0" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = 'builtin', 23 | modules = { 24 | basexx = "lib/basexx.lua" 25 | }, 26 | copy_directories = { "test" } 27 | } 28 | -------------------------------------------------------------------------------- /dist/basexx-0.3.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.3.0-1" 3 | 4 | description = { 5 | summary = "A base2, base16, base32, base64 and base85 library for Lua", 6 | detailed = "A Lua library which provides base2(bitfield), base16(hex), base32(crockford/rfc), base64(rfc/url), base85(z85) decoding and encoding.", 7 | license = "MIT", 8 | homepage = "https://github.com/aiq/basexx" 9 | } 10 | 11 | source = { 12 | url = "https://github.com/aiq/basexx/archive/v0.3.0.tar.gz", 13 | md5 = "32277d2c4564dabd0c45c9c67ec1e811", 14 | dir = "basexx-0.3.0" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = 'builtin', 23 | modules = { 24 | basexx = "lib/basexx.lua" 25 | }, 26 | copy_directories = { "test" } 27 | } 28 | -------------------------------------------------------------------------------- /dist/basexx-0.4.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.4.0-1" 3 | 4 | description = { 5 | summary = "A base2, base16, base32, base64 and base85 library for Lua", 6 | detailed = "A Lua library which provides base2(bitfield), base16(hex), base32(crockford/rfc), base64(rfc/url), base85(z85) decoding and encoding.", 7 | license = "MIT", 8 | homepage = "https://github.com/aiq/basexx" 9 | } 10 | 11 | source = { 12 | url = "https://github.com/aiq/basexx/archive/v0.4.0.tar.gz", 13 | md5 = "c931e3abdb788be95c319be4fa0ac79f", 14 | dir = "basexx-0.4.0" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = 'builtin', 23 | modules = { 24 | basexx = "lib/basexx.lua" 25 | }, 26 | copy_directories = { "test" } 27 | } 28 | -------------------------------------------------------------------------------- /dist/basexx-0.4.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "0.4.1-1" 3 | 4 | description = { 5 | summary = "A base2, base16, base32, base64 and base85 library for Lua", 6 | detailed = "A Lua library which provides base2(bitfield), base16(hex), base32(crockford/rfc), base64(rfc/url), base85(z85) decoding and encoding.", 7 | license = "MIT", 8 | homepage = "https://github.com/aiq/basexx" 9 | } 10 | 11 | source = { 12 | url = "https://github.com/aiq/basexx/archive/v0.4.1.tar.gz", 13 | md5 = "85fda02f7068183ced02d88696972e81", 14 | dir = "basexx-0.4.1" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = 'builtin', 23 | modules = { 24 | basexx = "lib/basexx.lua" 25 | }, 26 | copy_directories = { "test" } 27 | } 28 | -------------------------------------------------------------------------------- /dist/basexx-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "basexx" 2 | version = "scm-0" 3 | 4 | description = { 5 | summary = "A base2, base16, base32, base64 and base85 library for Lua", 6 | detailed = "A Lua library which provides base2(bitfield), base16(hex), base32(crockford/rfc), base64(rfc/url), base85(z85) decoding and encoding.", 7 | license = "MIT", 8 | homepage = "https://github.com/aiq/basexx" 9 | } 10 | 11 | source = { 12 | url = "https://github.com/aiq/basexx/archive/master.tar.gz", 13 | dir = "basexx-master", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1" 18 | } 19 | 20 | build = { 21 | type = 'builtin', 22 | modules = { 23 | basexx = "lib/basexx.lua" 24 | }, 25 | copy_directories = { "test" } 26 | } 27 | -------------------------------------------------------------------------------- /lib/basexx.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- util functions 3 | -------------------------------------------------------------------------------- 4 | 5 | local function divide_string( str, max ) 6 | local result = {} 7 | 8 | local start = 1 9 | for i = 1, #str do 10 | if i % max == 0 then 11 | table.insert( result, str:sub( start, i ) ) 12 | start = i + 1 13 | elseif i == #str then 14 | table.insert( result, str:sub( start, i ) ) 15 | end 16 | end 17 | 18 | return result 19 | end 20 | 21 | local function number_to_bit( num, length ) 22 | local bits = {} 23 | 24 | while num > 0 do 25 | local rest = math.floor( math.fmod( num, 2 ) ) 26 | table.insert( bits, rest ) 27 | num = ( num - rest ) / 2 28 | end 29 | 30 | while #bits < length do 31 | table.insert( bits, "0" ) 32 | end 33 | 34 | return string.reverse( table.concat( bits ) ) 35 | end 36 | 37 | local function ignore_set( str, set ) 38 | if set then 39 | str = str:gsub( "["..set.."]", "" ) 40 | end 41 | return str 42 | end 43 | 44 | local function pure_from_bit( str ) 45 | return ( str:gsub( '........', function ( cc ) 46 | return string.char( tonumber( cc, 2 ) ) 47 | end ) ) 48 | end 49 | 50 | local function unexpected_char_error( str, pos ) 51 | local c = string.sub( str, pos, pos ) 52 | return string.format( "unexpected character at position %d: '%s'", pos, c ) 53 | end 54 | 55 | -------------------------------------------------------------------------------- 56 | 57 | local basexx = {} 58 | 59 | -------------------------------------------------------------------------------- 60 | -- base2(bitfield) decode and encode function 61 | -------------------------------------------------------------------------------- 62 | 63 | local bitMap = { o = "0", i = "1", l = "1" } 64 | 65 | function basexx.from_bit( str, ignore ) 66 | str = ignore_set( str, ignore ) 67 | str = string.lower( str ) 68 | str = str:gsub( '[ilo]', function( c ) return bitMap[ c ] end ) 69 | local pos = string.find( str, "[^01]" ) 70 | if pos then return nil, unexpected_char_error( str, pos ) end 71 | 72 | return pure_from_bit( str ) 73 | end 74 | 75 | function basexx.to_bit( str ) 76 | return ( str:gsub( '.', function ( c ) 77 | local byte = string.byte( c ) 78 | local bits = {} 79 | for _ = 1,8 do 80 | table.insert( bits, byte % 2 ) 81 | byte = math.floor( byte / 2 ) 82 | end 83 | return table.concat( bits ):reverse() 84 | end ) ) 85 | end 86 | 87 | -------------------------------------------------------------------------------- 88 | -- base16(hex) decode and encode function 89 | -------------------------------------------------------------------------------- 90 | 91 | function basexx.from_hex( str, ignore ) 92 | str = ignore_set( str, ignore ) 93 | local pos = string.find( str, "[^%x]" ) 94 | if pos then return nil, unexpected_char_error( str, pos ) end 95 | 96 | return ( str:gsub( '..', function ( cc ) 97 | return string.char( tonumber( cc, 16 ) ) 98 | end ) ) 99 | end 100 | 101 | function basexx.to_hex( str ) 102 | return ( str:gsub( '.', function ( c ) 103 | return string.format('%02X', string.byte( c ) ) 104 | end ) ) 105 | end 106 | 107 | -------------------------------------------------------------------------------- 108 | -- generic function to decode and encode base32/base64 109 | -------------------------------------------------------------------------------- 110 | 111 | local function from_basexx( str, alphabet, bits ) 112 | local result = {} 113 | for i = 1, #str do 114 | local c = string.sub( str, i, i ) 115 | if c ~= '=' then 116 | local index = string.find( alphabet, c, 1, true ) 117 | if not index then 118 | return nil, unexpected_char_error( str, i ) 119 | end 120 | table.insert( result, number_to_bit( index - 1, bits ) ) 121 | end 122 | end 123 | 124 | local value = table.concat( result ) 125 | local pad = #value % 8 126 | return pure_from_bit( string.sub( value, 1, #value - pad ) ) 127 | end 128 | 129 | local function to_basexx( str, alphabet, bits, pad ) 130 | local bitString = basexx.to_bit( str ) 131 | 132 | local chunks = divide_string( bitString, bits ) 133 | local result = {} 134 | for _,value in ipairs( chunks ) do 135 | if ( #value < bits ) then 136 | value = value .. string.rep( '0', bits - #value ) 137 | end 138 | local pos = tonumber( value, 2 ) + 1 139 | table.insert( result, alphabet:sub( pos, pos ) ) 140 | end 141 | 142 | table.insert( result, pad ) 143 | return table.concat( result ) 144 | end 145 | 146 | -------------------------------------------------------------------------------- 147 | -- rfc 3548: http://www.rfc-editor.org/rfc/rfc3548.txt 148 | -------------------------------------------------------------------------------- 149 | 150 | local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" 151 | local base32PadMap = { "", "======", "====", "===", "=" } 152 | 153 | function basexx.from_base32( str, ignore ) 154 | str = ignore_set( str, ignore ) 155 | return from_basexx( string.upper( str ), base32Alphabet, 5 ) 156 | end 157 | 158 | function basexx.to_base32( str ) 159 | return to_basexx( str, base32Alphabet, 5, base32PadMap[ #str % 5 + 1 ] ) 160 | end 161 | 162 | -------------------------------------------------------------------------------- 163 | -- crockford: http://www.crockford.com/wrmg/base32.html 164 | -------------------------------------------------------------------------------- 165 | 166 | local crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 167 | local crockfordMap = { O = "0", I = "1", L = "1" } 168 | 169 | function basexx.from_crockford( str, ignore ) 170 | str = ignore_set( str, ignore ) 171 | str = string.upper( str ) 172 | str = str:gsub( '[ILOU]', function( c ) return crockfordMap[ c ] end ) 173 | return from_basexx( str, crockfordAlphabet, 5 ) 174 | end 175 | 176 | function basexx.to_crockford( str ) 177 | return to_basexx( str, crockfordAlphabet, 5, "" ) 178 | end 179 | 180 | -------------------------------------------------------------------------------- 181 | -- base64 decode and encode function 182 | -------------------------------------------------------------------------------- 183 | 184 | local base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".. 185 | "abcdefghijklmnopqrstuvwxyz".. 186 | "0123456789+/" 187 | local base64PadMap = { "", "==", "=" } 188 | 189 | function basexx.from_base64( str, ignore ) 190 | str = ignore_set( str, ignore ) 191 | return from_basexx( str, base64Alphabet, 6 ) 192 | end 193 | 194 | function basexx.to_base64( str ) 195 | return to_basexx( str, base64Alphabet, 6, base64PadMap[ #str % 3 + 1 ] ) 196 | end 197 | 198 | -------------------------------------------------------------------------------- 199 | -- URL safe base64 decode and encode function 200 | -------------------------------------------------------------------------------- 201 | 202 | local url64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".. 203 | "abcdefghijklmnopqrstuvwxyz".. 204 | "0123456789-_" 205 | 206 | function basexx.from_url64( str, ignore ) 207 | str = ignore_set( str, ignore ) 208 | return from_basexx( str, url64Alphabet, 6 ) 209 | end 210 | 211 | function basexx.to_url64( str ) 212 | return to_basexx( str, url64Alphabet, 6, "" ) 213 | end 214 | 215 | -------------------------------------------------------------------------------- 216 | -- 217 | -------------------------------------------------------------------------------- 218 | 219 | local function length_error( len, d ) 220 | return string.format( "invalid length: %d - must be a multiple of %d", len, d ) 221 | end 222 | 223 | local z85Decoder = { 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00, 224 | 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45, 225 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 226 | 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47, 227 | 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 228 | 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 229 | 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 230 | 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00, 231 | 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 232 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 233 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 234 | 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00 } 235 | 236 | function basexx.from_z85( str, ignore ) 237 | str = ignore_set( str, ignore ) 238 | if ( #str % 5 ) ~= 0 then 239 | return nil, length_error( #str, 5 ) 240 | end 241 | 242 | local result = {} 243 | 244 | local value = 0 245 | for i = 1, #str do 246 | local index = string.byte( str, i ) - 31 247 | if index < 1 or index >= #z85Decoder then 248 | return nil, unexpected_char_error( str, i ) 249 | end 250 | value = ( value * 85 ) + z85Decoder[ index ] 251 | if ( i % 5 ) == 0 then 252 | local divisor = 256 * 256 * 256 253 | while divisor ~= 0 do 254 | local b = math.floor( value / divisor ) % 256 255 | table.insert( result, string.char( b ) ) 256 | divisor = math.floor( divisor / 256 ) 257 | end 258 | value = 0 259 | end 260 | end 261 | 262 | return table.concat( result ) 263 | end 264 | 265 | local z85Encoder = "0123456789".. 266 | "abcdefghijklmnopqrstuvwxyz".. 267 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ".. 268 | ".-:+=^!/*?&<>()[]{}@%$#" 269 | 270 | function basexx.to_z85( str ) 271 | if ( #str % 4 ) ~= 0 then 272 | return nil, length_error( #str, 4 ) 273 | end 274 | 275 | local result = {} 276 | 277 | local value = 0 278 | for i = 1, #str do 279 | local b = string.byte( str, i ) 280 | value = ( value * 256 ) + b 281 | if ( i % 4 ) == 0 then 282 | local divisor = 85 * 85 * 85 * 85 283 | while divisor ~= 0 do 284 | local index = ( math.floor( value / divisor ) % 85 ) + 1 285 | table.insert( result, z85Encoder:sub( index, index ) ) 286 | divisor = math.floor( divisor / 85 ) 287 | end 288 | value = 0 289 | end 290 | end 291 | 292 | return table.concat( result ) 293 | end 294 | 295 | -------------------------------------------------------------------------------- 296 | 297 | return basexx 298 | -------------------------------------------------------------------------------- /test/base32_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle base32(rfc3548) strings", function() 4 | 5 | it( "should convert chunky bacon", function() 6 | -- https://github.com/stesla/base32 7 | assert.is.same( "MNUHK3TLPEQGEYLDN5XCC===", 8 | basexx.to_base32( "chunky bacon!" ) ) 9 | assert.is.same( "chunky bacon!", 10 | basexx.from_base32( "MNUHK3TLPEQGEYLDN5XCC===" ) ) 11 | end) 12 | 13 | it( "should allow to ignore characters in a base32 string", function() 14 | assert.is.same( "chunky bacon!", 15 | basexx.from_base32( "MNUHK3TLPEQGEYLDN5XCC===" ) ) 16 | end) 17 | 18 | it ( "should handle wrong characters without a crash", function() 19 | local res, err = basexx.from_base32( "MS$DF" ) 20 | assert.is.falsy( res ) 21 | assert.is.same( "unexpected character at position 3: '$'", err ) 22 | end) 23 | 24 | end) 25 | -------------------------------------------------------------------------------- /test/base64_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle base64 strings", function() 4 | 5 | local longtxt = "Man is distinguished, not only by his reason, but by ".. 6 | "this singular passion from other animals, which is a ".. 7 | "lust of the mind, that by a perseverance of delight in ".. 8 | "the continued and indefatigable generation of knowledge, ".. 9 | "exceeds the short vehemence of any carnal pleasure." 10 | 11 | local long64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb2".. 12 | "4sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBh".. 13 | "bmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYn".. 14 | "kgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVk".. 15 | "IGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLC".. 16 | "BleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBw".. 17 | "bGVhc3VyZS4=" 18 | 19 | it( "should work with wikipedia examples", function() 20 | -- http://en.wikipedia.org/wiki/Base64 21 | assert.is.same( 'TWFu', basexx.to_base64( 'Man') ) 22 | assert.is.same( 'Man', basexx.from_base64( 'TWFu') ) 23 | 24 | assert.is.same( 'bGVhc3VyZS4=', basexx.to_base64( 'leasure.') ) 25 | assert.is.same( 'leasure.', basexx.from_base64( 'bGVhc3VyZS4=') ) 26 | 27 | assert.is.same( 'cGxlYXN1cmUu', basexx.to_base64( 'pleasure.') ) 28 | assert.is.same( 'pleasure.', basexx.from_base64( 'cGxlYXN1cmUu') ) 29 | 30 | assert.is.same( 'ZWFzdXJlLg==', basexx.to_base64( 'easure.') ) 31 | assert.is.same( 'easure.', basexx.from_base64( 'ZWFzdXJlLg==') ) 32 | 33 | assert.is.same( 'c3VyZS4=', basexx.to_base64( 'sure.') ) 34 | assert.is.same( 'sure.', basexx.from_base64( 'c3VyZS4=') ) 35 | 36 | assert.is.same( long64, basexx.to_base64( longtxt ) ) 37 | assert.is.same( longtxt, basexx.from_base64( long64) ) 38 | end) 39 | 40 | it( "should handle padding correct", function() 41 | -- http://en.wikipedia.org/wiki/Base64#Padding 42 | assert.is.same( "YW55IGNhcm5hbCBwbGVhc3VyZS4=", 43 | basexx.to_base64( "any carnal pleasure." ) ) 44 | assert.is.same( "YW55IGNhcm5hbCBwbGVhc3VyZQ==", 45 | basexx.to_base64( "any carnal pleasure" ) ) 46 | assert.is.same( "YW55IGNhcm5hbCBwbGVhc3Vy", 47 | basexx.to_base64( "any carnal pleasur" ) ) 48 | assert.is.same( "YW55IGNhcm5hbCBwbGVhc3U=", 49 | basexx.to_base64( "any carnal pleasu" ) ) 50 | assert.is.same( "YW55IGNhcm5hbCBwbGVhcw==", 51 | basexx.to_base64( "any carnal pleas" ) ) 52 | 53 | assert.is.same( "any carnal pleas", 54 | basexx.from_base64( "YW55IGNhcm5hbCBwbGVhcw==" ) ) 55 | assert.is.same( "any carnal pleasu", 56 | basexx.from_base64( "YW55IGNhcm5hbCBwbGVhc3U=" ) ) 57 | assert.is.same( "any carnal pleasur", 58 | basexx.from_base64( "YW55IGNhcm5hbCBwbGVhc3Vy" ) ) 59 | end) 60 | 61 | it( "should allow to ignore characters in a base64 string", function() 62 | assert.is.same( "Man", basexx.from_base64( "TW-Fu", "-" ) ) 63 | end) 64 | 65 | it( "should handle wrong characters without a crash", function() 66 | local res, err = basexx.from_base64( "TW`Fu" ) 67 | assert.is.falsy( res ) 68 | assert.is.same( "unexpected character at position 3: '`'", err ) 69 | end) 70 | 71 | end) 72 | -------------------------------------------------------------------------------- /test/bit_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle bitfields strings", function() 4 | 5 | it( "should convert data to a bitfields string", function() 6 | assert.is.same( "01000001010000110100010001000011", 7 | basexx.to_bit( "ACDC" ) ) 8 | end) 9 | 10 | it( "should read data from a bitfields string", function() 11 | assert.is.same( "ACDC", 12 | basexx.from_bit( "01000001010000110100010001000011" ) ) 13 | end) 14 | 15 | it( "should read data with o instead of 0", function() 16 | assert.is.same( "AC", basexx.from_bit( "o1ooooo1o1oooo11" ) ) 17 | assert.is.same( "AC", basexx.from_bit( "OioooooiOiooooii" ) ) 18 | end) 19 | 20 | it( "should allow to ignore characters in a bitfield string", function() 21 | assert.is.same( "AC", basexx.from_bit( "o1ooooo1 o1oooo11", " " ) ) 22 | end) 23 | 24 | it( "should handle wrong characters without a crash", function() 25 | local res, err = basexx.from_bit( "o1oo*ooo1*o1oo*oo11" ) 26 | assert.is.falsy( res ) 27 | assert.is.same( "unexpected character at position 5: '*'", err ) 28 | end) 29 | 30 | end) 31 | -------------------------------------------------------------------------------- /test/crockford_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle base32(crockford) strings", function() 4 | 5 | it( "should fulfill crockford-py test case", function() 6 | -- https://github.com/ingydotnet/crockford-py/blob/master/tests/test_functions.py 7 | assert.is.same( "CSQPY", basexx.to_crockford( "foo" ) ) 8 | assert.is.same( "foo", basexx.from_crockford( "CSQPY" ) ) 9 | end) 10 | 11 | it( "should really work ;-)", function() 12 | -- https://github.com/aiq/basexx/issues/3 13 | assert.is.same( "91jprv3f41bpywkccg", string.lower( basexx.to_crockford( "Hello World" ) ) ) 14 | assert.is.same( "AXQQEB10D5T20WK5C5P6RY90EXQQ4TVK44", basexx.to_crockford( "Wow, it really works!" ) ) 15 | assert.is.same( "Wow, it really works!", basexx.from_crockford( "axqqeb10d5t20wk5c5p6ry90exqq4tvk44" ) ) 16 | end) 17 | 18 | it( "should allow to ignore characters in a crockford string", function() 19 | assert.is.same( "foo", basexx.from_crockford( "CSQPY\n", "\n" ) ) 20 | end) 21 | 22 | it( "should handle wrong characters without a crash", function() 23 | local res, err = basexx.from_crockford( "CSQ%PY" ) 24 | assert.is.falsy( res ) 25 | assert.is.same( "unexpected character at position 4: '%'", err ) 26 | end) 27 | 28 | end) 29 | -------------------------------------------------------------------------------- /test/hex_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle hex strings", function() 4 | 5 | it( "should convert data to a hex string", function() 6 | assert.is.same( "48656C6C6F20776F726C6421", 7 | basexx.to_hex( "Hello world!" ) ) 8 | end) 9 | 10 | it( "should read data from a upper and lower hex string", function() 11 | assert.is.same( "Hello world!", 12 | basexx.from_hex( "48656C6C6F20776F726C6421" ) ) 13 | assert.is.same( "Hello world!", 14 | basexx.from_hex( "48656c6c6f20776f726c6421" ) ) 15 | end) 16 | 17 | it( "should allow to ignore characters in a hex string", function() 18 | assert.is.same( "Hello world!", 19 | basexx.from_hex( "4865-6c6c 6f20-776f 726c-6421", " -" ) ) 20 | end) 21 | 22 | it( "should handle wrong characters without a crash", function() 23 | local res, err = basexx.from_hex( "4865-6c6c" ) 24 | assert.is.falsy( res ) 25 | assert.is.same( "unexpected character at position 5: '-'", err ) 26 | end) 27 | 28 | end) 29 | -------------------------------------------------------------------------------- /test/url64_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle base64 strings", function() 4 | 5 | local longtxt = "Man is distinguished, not only by his reason, but by ".. 6 | "this singular passion from other animals, which is a ".. 7 | "lust of the mind, that by a perseverance of delight in ".. 8 | "the continued and indefatigable generation of knowledge, ".. 9 | "exceeds the short vehemence of any carnal pleasure." 10 | 11 | local long64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb2".. 12 | "4sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBh".. 13 | "bmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYn".. 14 | "kgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVk".. 15 | "IGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLC".. 16 | "BleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBw".. 17 | "bGVhc3VyZS4" 18 | 19 | it( "should convert data to a base64 string", function() 20 | -- http://en.wikipedia.org/wiki/Base64#URL_applications 21 | assert.is.same( 'TWFu', basexx.to_url64( 'Man') ) 22 | assert.is.same( 'Man', basexx.from_url64( 'TWFu') ) 23 | 24 | assert.is.same( 'bGVhc3VyZS4', basexx.to_url64( 'leasure.') ) 25 | assert.is.same( 'leasure.', basexx.from_url64( 'bGVhc3VyZS4') ) 26 | 27 | assert.is.same( 'cGxlYXN1cmUu', basexx.to_url64( 'pleasure.') ) 28 | assert.is.same( 'pleasure.', basexx.from_url64( 'cGxlYXN1cmUu') ) 29 | 30 | assert.is.same( 'ZWFzdXJlLg', basexx.to_url64( 'easure.') ) 31 | assert.is.same( 'easure.', basexx.from_url64( 'ZWFzdXJlLg') ) 32 | 33 | assert.is.same( 'c3VyZS4', basexx.to_url64( 'sure.') ) 34 | assert.is.same( 'sure.', basexx.from_url64( 'c3VyZS4') ) 35 | 36 | assert.is.same( long64, basexx.to_url64( longtxt ) ) 37 | assert.is.same( longtxt, basexx.from_url64( long64 ) ) 38 | end) 39 | 40 | local msgtxt = '{"msg_en":"Hello","msg_jp":"こんにちは","msg_cn":"你好"'.. 41 | ',"msg_kr":"안녕하세요","msg_ru":"Здравствуйте!"'.. 42 | ',"msg_de":"Grüß Gott"}' 43 | 44 | local msg64 = 'eyJtc2dfZW4iOiJIZWxsbyIsIm1zZ19qcCI6IuOBk-OCk-OBq-OBoeOBryI'.. 45 | 'sIm1zZ19jbiI6IuS9oOWlvSIsIm1zZ19rciI6IuyViOuFle2VmOyEuOyalC'.. 46 | 'IsIm1zZ19ydSI6ItCX0LTRgNCw0LLRgdGC0LLRg9C50YLQtSEiLCJtc2dfZ'.. 47 | 'GUiOiJHcsO8w58gR290dCJ9' 48 | 49 | it( "should work with the msg example", function() 50 | assert.is.same( msg64, basexx.to_url64( msgtxt ) ) 51 | assert.is.same( msgtxt, basexx.from_url64( msg64 ) ) 52 | end) 53 | 54 | it( "should allow to ignore characters in a url64 string", function() 55 | assert.is.same( "Man", basexx.from_url64( "TW-Fu\n", "-\n" ) ) 56 | end) 57 | 58 | end) 59 | -------------------------------------------------------------------------------- /test/z85_spec.lua: -------------------------------------------------------------------------------- 1 | basexx = require( "basexx" ) 2 | 3 | describe( "should handle ZeroMQ base85 strings", function() 4 | 5 | it( "should fulfill spec test case", function() 6 | -- http://rfc.zeromq.org/spec:32 7 | local data = string.char( 0x86, 0x4f, 0xd2, 0x6f, 0xb5, 0x59, 0xf7, 0x5b ) 8 | local z85 = "HelloWorld" 9 | assert.is.same( z85, basexx.to_z85( data ) ) 10 | assert.is.same( data, basexx.from_z85( z85 ) ) 11 | end) 12 | 13 | it( "should encode a numeric string correctly", function() 14 | -- https://github.com/msealand/z85.node/blob/master/test/encode.test.js 15 | assert.is.same( "f!$Kw", basexx.to_z85( "1234" ) ) 16 | assert.is.same( "1234", basexx.from_z85( "f!$Kw" ) ) 17 | end) 18 | 19 | it( "should allow to ignore characters in a base85 string", function() 20 | assert.is.same( "1234", basexx.from_z85( "'f!$Kw'\n", "'\n" ) ) 21 | end) 22 | 23 | it( "should handle wrong input lenght without a crash", function() 24 | local res, err = basexx.from_z85( "abcdefghi" ) 25 | assert.is.falsy( res ) 26 | assert.is.same( err, "invalid length: 9 - must be a multiple of 5" ) 27 | 28 | res, err = basexx.to_z85( "abcdefghi" ) 29 | assert.is.falsy( res ) 30 | assert.is.same( err, "invalid length: 9 - must be a multiple of 4" ) 31 | end) 32 | 33 | it( "should handle wrong characters without a crash", function() 34 | local c = string.char( 140 ) 35 | local res, err = basexx.from_z85( "f"..c.."$Kw" ) 36 | assert.is.falsy( res ) 37 | local msg = string.format( "unexpected character at position 2: '%s'", c ) 38 | assert.is.same( err, msg ) 39 | end) 40 | 41 | end) 42 | --------------------------------------------------------------------------------