tobase64

function tobase64( str ) --> base64str

Description

Encodes a data string to a base 64 string.

Parameters

str

The data string that should be encoded.

Return Values

base64str

The base 64 encoded string.

Code

--ZFUNC-tobase64-v1
local function tobase64( str ) --> base64str
   --ZFUNC-asciichunks-v1
   local function asciichunks( str, length )
      length = length or 1
      local chunks = {}

      local i = 1
      local last = string.len( str )
      while i <= last do
         local j = i + length - 1
         table.insert( chunks, str:sub( i, j ) )
         i = i + length
      end

      return chunks
   end

   local bitMap = {
     "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
     "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
   }
   local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"..
                    "abcdefghijklmnopqrstuvwxyz"..
                    "0123456789+/"
   local padMap = { "", "==", "=" }

   local bitTab = {}
   for i = 1, #str do
      local b = string.byte( str, i, i )
      local firstHalf = math.floor( b / 2 ^ 4 )
      local secondHalf = b - ( firstHalf * 2 ^ 4 )
      table.insert( bitTab, bitMap[ firstHalf + 1 ] )
      table.insert( bitTab, bitMap[ secondHalf + 1 ] )
   end
   local fullBitStr = table.concat( bitTab )
   local mod6 = #fullBitStr % 6
   if mod6 > 0 then
      fullBitStr = fullBitStr..string.rep( '0', 6 - mod6 )
   end

   local chunks = asciichunks( fullBitStr, 6 )
   local result = {}
   for _, value in ipairs( chunks ) do
      local pos = tonumber( value, 2 ) + 1
      table.insert( result, alphabet:sub( pos, pos ) )
   end

   local pad = padMap[ #str % 3 + 1 ]
   table.insert( result, pad )
   return table.concat( result )
end

return tobase64

Examples

local t = require( "tapered" )
local tobase64 = require( "tobase64" )

--should work with wikipedia examples
-- http://en.wikipedia.org/wiki/Base64
t.is( tobase64( "Man" ), "TWFu" )
t.is( tobase64( "leasure." ), "bGVhc3VyZS4=" )
t.is( tobase64( "pleasure." ), "cGxlYXN1cmUu")
t.is( tobase64( "easure." ), "ZWFzdXJlLg==")
t.is( tobase64( "sure." ), "c3VyZS4=")

local long64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb2"..
               "4sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBh"..
               "bmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYn"..
               "kgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVk"..
               "IGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLC"..
               "BleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBw"..
               "bGVhc3VyZS4="

local longtxt = "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."

t.is( tobase64( longtxt ), long64 )

-- http://en.wikipedia.org/wiki/Base64#Padding
t.is( tobase64( "any carnal pleas" ), "YW55IGNhcm5hbCBwbGVhcw==" )
t.is( tobase64( "any carnal pleasu" ), "YW55IGNhcm5hbCBwbGVhc3U=" )
t.is( tobase64( "any carnal pleasur" ), "YW55IGNhcm5hbCBwbGVhc3Vy" )

t.done()

Inspired by