├── .gitignore ├── .travis.yml ├── README.md ├── base64.lua ├── bench.lua ├── rockspec ├── base64-1.5-1.rockspec ├── base64-1.5-2.rockspec └── base64-1.5-3.rockspec └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.swp 3 | *.swo 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | 4 | env: 5 | - LUA="lua 5.1" 6 | - LUA="lua 5.2" 7 | - LUA="lua 5.3" 8 | - LUA="lua 5.4" 9 | - LUA="luajit 2.0" 10 | - LUA="luajit 2.1" 11 | 12 | before_install: 13 | - pip install hererocks 14 | - hererocks env --$LUA -rlatest # Use latest LuaRocks, install into 'env' directory. 15 | - source env/bin/activate # Add directory with all installed binaries to PATH.notifications: 16 | 17 | notifications: 18 | email: false 19 | 20 | install: 21 | - luarocks install luacheck 22 | 23 | script: 24 | - luacheck --no-unused-args *.lua 25 | - lua test.lua 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/iskolbin/lbase64.svg?branch=master)](https://travis-ci.org/iskolbin/lbase64) 2 | [![license](https://img.shields.io/badge/license-public%20domain-blue.svg)]() 3 | [![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php) 4 | 5 | Lua base64 encoder/decoder 6 | ========================== 7 | 8 | Pure Lua [base64](https://en.wikipedia.org/wiki/Base64) encoder/decoder. Works with 9 | Lua 5.1+ and LuaJIT. Fallbacks to pure Lua bit operations if bit/bit32/native bit 10 | operators are not available. 11 | 12 | ```lua 13 | local base64 = require'base64' 14 | local str = 'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.' 15 | local b64str = 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=' 16 | local encoded = base64.encode( str ) 17 | local decoded = base64.decode( b64str ) 18 | assert( str == decoded ) 19 | assert( b64str == encoded ) 20 | ``` 21 | 22 | base64.encode( str, encoder = DEFAULT, usecache = false ) 23 | --------------------------------------------------------- 24 | Encodes `str` string using `encoder` table. By default uses table with `+` as 25 | char for 62, `/` as char for 63 and `=` as padding char. You can specify custom 26 | encoder. For this you could use `base64.makeencoder`. If you are encoding large 27 | chunks of text (or another highly redundant data) it's possible to highly 28 | increase performace (for text approx. x2 gain) by using `usecache = true`. For 29 | binary data like images using cache decreasing performance. 30 | 31 | base64.decode( str, decoder = DEFAULT, usecache = false ) 32 | --------------------------------------------------------- 33 | Decodes `str` string using `decoder` table. Default decoder uses same chars as 34 | default encoder. 35 | 36 | base64.makeencoder( s62 = '+', s63 = '/', spad = '=' ) 37 | ------------------------------------------------------ 38 | Make custom encoding table 39 | 40 | base64.makedecoder( s62 = '+', s63 = '/', spad = '=' ) 41 | ------------------------------------------------------ 42 | Make custom decoding table 43 | 44 | Install 45 | ------- 46 | ```bash 47 | luarocks install base64 48 | ``` 49 | -------------------------------------------------------------------------------- /base64.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | base64 -- v1.5.3 public domain Lua base64 encoder/decoder 4 | no warranty implied; use at your own risk 5 | 6 | Needs bit32.extract function. If not present it's implemented using BitOp 7 | or Lua 5.3 native bit operators. For Lua 5.1 fallbacks to pure Lua 8 | implementation inspired by Rici Lake's post: 9 | http://ricilake.blogspot.co.uk/2007/10/iterating-bits-in-lua.html 10 | 11 | author: Ilya Kolbin (iskolbin@gmail.com) 12 | url: github.com/iskolbin/lbase64 13 | 14 | COMPATIBILITY 15 | 16 | Lua 5.1+, LuaJIT 17 | 18 | LICENSE 19 | 20 | See end of file for license information. 21 | 22 | --]] 23 | 24 | 25 | local base64 = {} 26 | 27 | local extract = _G.bit32 and _G.bit32.extract -- Lua 5.2/Lua 5.3 in compatibility mode 28 | if not extract then 29 | if _G.bit then -- LuaJIT 30 | local shl, shr, band = _G.bit.lshift, _G.bit.rshift, _G.bit.band 31 | extract = function( v, from, width ) 32 | return band( shr( v, from ), shl( 1, width ) - 1 ) 33 | end 34 | elseif _G._VERSION == "Lua 5.1" then 35 | extract = function( v, from, width ) 36 | local w = 0 37 | local flag = 2^from 38 | for i = 0, width-1 do 39 | local flag2 = flag + flag 40 | if v % flag2 >= flag then 41 | w = w + 2^i 42 | end 43 | flag = flag2 44 | end 45 | return w 46 | end 47 | else -- Lua 5.3+ 48 | extract = load[[return function( v, from, width ) 49 | return ( v >> from ) & ((1 << width) - 1) 50 | end]]() 51 | end 52 | end 53 | 54 | 55 | function base64.makeencoder( s62, s63, spad ) 56 | local encoder = {} 57 | for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J', 58 | 'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y', 59 | 'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n', 60 | 'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2', 61 | '3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do 62 | encoder[b64code] = char:byte() 63 | end 64 | return encoder 65 | end 66 | 67 | function base64.makedecoder( s62, s63, spad ) 68 | local decoder = {} 69 | for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do 70 | decoder[charcode] = b64code 71 | end 72 | return decoder 73 | end 74 | 75 | local DEFAULT_ENCODER = base64.makeencoder() 76 | local DEFAULT_DECODER = base64.makedecoder() 77 | 78 | local char, concat = string.char, table.concat 79 | 80 | function base64.encode( str, encoder, usecaching ) 81 | encoder = encoder or DEFAULT_ENCODER 82 | local t, k, n = {}, 1, #str 83 | local lastn = n % 3 84 | local cache = {} 85 | for i = 1, n-lastn, 3 do 86 | local a, b, c = str:byte( i, i+2 ) 87 | local v = a*0x10000 + b*0x100 + c 88 | local s 89 | if usecaching then 90 | s = cache[v] 91 | if not s then 92 | s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)]) 93 | cache[v] = s 94 | end 95 | else 96 | s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)]) 97 | end 98 | t[k] = s 99 | k = k + 1 100 | end 101 | if lastn == 2 then 102 | local a, b = str:byte( n-1, n ) 103 | local v = a*0x10000 + b*0x100 104 | t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64]) 105 | elseif lastn == 1 then 106 | local v = str:byte( n )*0x10000 107 | t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64]) 108 | end 109 | return concat( t ) 110 | end 111 | 112 | function base64.decode( b64, decoder, usecaching ) 113 | decoder = decoder or DEFAULT_DECODER 114 | local pattern = '[^%w%+%/%=]' 115 | if decoder then 116 | local s62, s63 117 | for charcode, b64code in pairs( decoder ) do 118 | if b64code == 62 then s62 = charcode 119 | elseif b64code == 63 then s63 = charcode 120 | end 121 | end 122 | pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) ) 123 | end 124 | b64 = b64:gsub( pattern, '' ) 125 | local cache = usecaching and {} 126 | local t, k = {}, 1 127 | local n = #b64 128 | local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0 129 | for i = 1, padding > 0 and n-4 or n, 4 do 130 | local a, b, c, d = b64:byte( i, i+3 ) 131 | local s 132 | if usecaching then 133 | local v0 = a*0x1000000 + b*0x10000 + c*0x100 + d 134 | s = cache[v0] 135 | if not s then 136 | local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d] 137 | s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8)) 138 | cache[v0] = s 139 | end 140 | else 141 | local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d] 142 | s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8)) 143 | end 144 | t[k] = s 145 | k = k + 1 146 | end 147 | if padding == 1 then 148 | local a, b, c = b64:byte( n-3, n-1 ) 149 | local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 150 | t[k] = char( extract(v,16,8), extract(v,8,8)) 151 | elseif padding == 2 then 152 | local a, b = b64:byte( n-3, n-2 ) 153 | local v = decoder[a]*0x40000 + decoder[b]*0x1000 154 | t[k] = char( extract(v,16,8)) 155 | end 156 | return concat( t ) 157 | end 158 | 159 | return base64 160 | 161 | --[[ 162 | ------------------------------------------------------------------------------ 163 | This software is available under 2 licenses -- choose whichever you prefer. 164 | ------------------------------------------------------------------------------ 165 | ALTERNATIVE A - MIT License 166 | Copyright (c) 2018 Ilya Kolbin 167 | Permission is hereby granted, free of charge, to any person obtaining a copy of 168 | this software and associated documentation files (the "Software"), to deal in 169 | the Software without restriction, including without limitation the rights to 170 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 171 | of the Software, and to permit persons to whom the Software is furnished to do 172 | so, subject to the following conditions: 173 | The above copyright notice and this permission notice shall be included in all 174 | copies or substantial portions of the Software. 175 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 176 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 178 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 179 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 180 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 181 | SOFTWARE. 182 | ------------------------------------------------------------------------------ 183 | ALTERNATIVE B - Public Domain (www.unlicense.org) 184 | This is free and unencumbered software released into the public domain. 185 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 186 | software, either in source code form or as a compiled binary, for any purpose, 187 | commercial or non-commercial, and by any means. 188 | In jurisdictions that recognize copyright laws, the author or authors of this 189 | software dedicate any and all copyright interest in the software to the public 190 | domain. We make this dedication for the benefit of the public at large and to 191 | the detriment of our heirs and successors. We intend this dedication to be an 192 | overt act of relinquishment in perpetuity of all present and future rights to 193 | this software under copyright law. 194 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 195 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 196 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 197 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 198 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 199 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 200 | ------------------------------------------------------------------------------ 201 | --]] 202 | -------------------------------------------------------------------------------- /bench.lua: -------------------------------------------------------------------------------- 1 | local base64 = require'base64' 2 | local N = 10000000 3 | local st = {} 4 | local letters = ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' 5 | .. 'абвгдеёжзийклмнопрстуфхцшщчъыьэюя' 6 | .. 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦШЩЧЪЫЬЭЮЯ' 7 | local nletters = #letters 8 | for i = 1, N do 9 | local j = math.random( nletters ) 10 | st[i] = letters:sub( j, j ) 11 | end 12 | local s = table.concat( st ) 13 | local t = os.clock() 14 | local encoded = base64.encode( s ) 15 | local encodetime = os.clock() - t 16 | 17 | t = os.clock() 18 | local decoded = base64.decode( encoded ) 19 | local decodetime = os.clock() - t 20 | 21 | assert( s == decoded ) 22 | print('Common text') 23 | print(('Encoding: %d bytes/sec'):format( math.floor(N/encodetime))) 24 | print(('Decoding: %d bytes/sec'):format( math.floor(N/decodetime))) 25 | collectgarbage() 26 | 27 | t = os.clock() 28 | encoded = base64.encode( s, nil, true ) 29 | encodetime = os.clock() - t 30 | 31 | t = os.clock() 32 | decoded = base64.decode( encoded, nil, true ) 33 | assert( s == decoded ) 34 | decodetime = os.clock() - t 35 | print('Common text (cache)') 36 | print(('Encoding: %d bytes/sec'):format( math.floor(N/encodetime))) 37 | print(('Decoding: %d bytes/sec'):format( math.floor(N/decodetime))) 38 | collectgarbage() 39 | 40 | local lt = {} 41 | for i = 0, 255 do 42 | lt[i] = string.char(i) 43 | end 44 | nletters = #lt 45 | for i = 1, N do 46 | local j = math.random( nletters ) 47 | st[i] = lt[j] 48 | end 49 | s = table.concat( st ) 50 | 51 | t = os.clock() 52 | encoded = base64.encode( s, nil ) 53 | encodetime = os.clock() - t 54 | 55 | t = os.clock() 56 | decoded = base64.decode( encoded ) 57 | decodetime = os.clock() - t 58 | 59 | assert( s == decoded ) 60 | print('Binary') 61 | print(('Encoding: %d bytes/sec'):format( math.floor(N/encodetime))) 62 | print(('Decoding: %d bytes/sec'):format( math.floor(N/decodetime))) 63 | collectgarbage() 64 | 65 | t = os.clock() 66 | encoded = base64.encode( s, nil, true ) 67 | encodetime = os.clock() - t 68 | 69 | t = os.clock() 70 | decoded = base64.decode( encoded, nil, true ) 71 | assert( s == decoded ) 72 | decodetime = os.clock() - t 73 | print('Binary (cache)') 74 | print(('Encoding: %d bytes/sec'):format( math.floor(N/encodetime))) 75 | print(('Decoding: %d bytes/sec'):format( math.floor(N/decodetime))) 76 | collectgarbage() 77 | -------------------------------------------------------------------------------- /rockspec/base64-1.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "base64" 2 | version = "1.5-1" 3 | source = { 4 | url = "git+https://github.com/iskolbin/lbase64", 5 | tag = "v1.5.1", 6 | } 7 | description = { 8 | summary = "Pure Lua base64 encoder/decoder", 9 | detailed = [[ 10 | Pure Lua [base64](https://en.wikipedia.org/wiki/Base64) encoder/decoder. Works with Lua 5.1+ and LuaJIT. Fallbacks to pure Lua bit operations if bit/bit32/native bit operators are not available.]], 11 | homepage = "https://github.com/iskolbin/lbase64", 12 | license = "MIT/Public Domain" 13 | } 14 | dependencies = {} 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | base64 = "base64.lua", 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rockspec/base64-1.5-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "base64" 2 | version = "1.5-2" 3 | source = { 4 | url = "git://github.com/iskolbin/lbase64", 5 | tag = "v1.5.2", 6 | } 7 | description = { 8 | summary = "Pure Lua base64 encoder/decoder", 9 | detailed = [[ 10 | Pure Lua [base64](https://en.wikipedia.org/wiki/Base64) encoder/decoder. Works with Lua 5.1+ and LuaJIT. Fallbacks to pure Lua bit operations if bit/bit32/native bit operators are not available.]], 11 | homepage = "https://github.com/iskolbin/lbase64", 12 | license = "MIT/Public Domain" 13 | } 14 | dependencies = {} 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | base64 = "base64.lua", 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rockspec/base64-1.5-3.rockspec: -------------------------------------------------------------------------------- 1 | package = "base64" 2 | version = "1.5-3" 3 | source = { 4 | url = "git://github.com/iskolbin/lbase64", 5 | tag = "v1.5.3", 6 | } 7 | description = { 8 | summary = "Pure Lua base64 encoder/decoder", 9 | detailed = [[ 10 | Pure Lua base64 encoder/decoder. Works with Lua 5.1+ and LuaJIT. Fallbacks to pure Lua bit operations if bit/bit32/native bit operators are not available.]], 11 | homepage = "https://github.com/iskolbin/lbase64", 12 | license = "MIT/Public Domain" 13 | } 14 | dependencies = {} 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | base64 = "base64.lua", 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local base64 = require('base64') 2 | 3 | local function test( s, b64 ) 4 | assert( base64.encode( s ) == b64 ) 5 | assert( base64.decode( b64 ) == s ) 6 | assert( base64.decode( base64.encode( s )) == s ) 7 | assert( base64.encode( s, nil, true ) == b64 ) 8 | assert( base64.decode( b64, nil, true ) == s ) 9 | assert( base64.decode( base64.encode( s, nil, true ), nil, true ) == s ) 10 | end 11 | 12 | test( 'any carnal pleasure.', 'YW55IGNhcm5hbCBwbGVhc3VyZS4=' ) 13 | test( 'any carnal pleasure', 'YW55IGNhcm5hbCBwbGVhc3VyZQ==' ) 14 | test( 'any carnal pleasur', 'YW55IGNhcm5hbCBwbGVhc3Vy' ) 15 | test( 'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a ' .. 16 | 'lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, ' .. 17 | 'exceeds the short vehemence of any carnal pleasure.', 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb' .. 18 | '24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoY' .. 19 | 'XQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd' .. 20 | '2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=' ) 21 | test( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et ' .. 22 | 'dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ' .. 23 | 'commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat ' .. 24 | 'nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit ' .. 25 | 'anim id est laborum.', 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVp' .. 26 | 'dXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBx' .. 27 | 'dWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1' .. 28 | 'aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0' .. 29 | 'IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBv' .. 30 | 'ZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==') 31 | test( '«В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!»', 32 | 'wqvQkiDRh9Cw0YnQsNGFINGO0LPQsCDQttC40Lsg0LHRiyDR' .. 33 | 'htC40YLRgNGD0YE/INCU0LAsINC90L4g0YTQsNC70YzRiNC40LLRi9C5INGN0LrQt9C10LzQv9C70Y/RgCHCuw==') 34 | test( '«В чащах юга жил бы цитрус? Да, фальшивый экземпляр!»', 35 | 'wqvQkiDRh9Cw0YnQsNGFINGO0LPQsCDQttC40Lsg0LHRiyDRhtC' .. 36 | '40YLRgNGD0YE/INCU0LAsINGE0LDQu9GM0YjQuNCy0YvQuSDRjdC60LfQtdC80L/Qu9GP0YAhwrs=') 37 | test( '\137\080\078\071\013\010\026\010\000\000\000\013\073\072\068\082\000\000\000\032\000\000\000\032\001\003\000' .. 38 | '\000\000\073\180\232\183\000\000\000\006\080\076\084\069\255\255\255\000\000\000\085\194\211\126\000\000\000\018' .. 39 | '\073\068\065\084\008\215\099\248\015\004\196\016\084\006\196\218\011\000\237\189\063\193\243\000\141\059\000\000' .. 40 | '\000\000\073\069\078\068\174\066\096\130', 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX///8AAABVwtN+' .. 41 | 'AAAAEklEQVQI12P4DwTEEFQGxNoLAO29P8HzAI07AAAAAElFTkSuQmCC' ) 42 | 43 | assert( base64.decode('YW55IGNhcm5hbCBwbGVhc3VyZS4=\n\r\\' ) == 'any carnal pleasure.' ) 44 | 45 | assert( base64.decode('wйqеvнQсуkкiеDнRгhш9щCзwх0ъфYыnвQаsпNрGоFллIдNжGэOё0яLчPQsCDQttC40Lsg0LHRiyDRhtC' .. 46 | '40YLRgNGD0YE/INсCмUи0тLьAбsюIЙКNЕG\n\n\n\n\r\rE0LDQu9GM0YjQuNCy0YvQuSDRjdC60LfQtdC80L/Qu9GP0YAhwrs=') == 47 | '«В чащах юга жил бы цитрус? Да, фальшивый экземпляр!»' ) 48 | --------------------------------------------------------------------------------