tobase64url

function tobase64url( str ) --> url64str

Description

Encodes a data string to a base 64 string with URL and filename safe alphabet.

Parameters

str

The data string that should be encoded.

Return Values

url64str

The with URL and filename safe alphabet encoded base 64 string.

Code

--ZFUNC-tobase64url-v1
local function tobase64url( str ) --> url64str
   --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 tobase64url

Examples

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

--http://en.wikipedia.org/wiki/Base64#URL_applications
t.is( tobase64url( "Man" ), "TWFu" )
t.is( tobase64url( "leasure." ), "bGVhc3VyZS4=" )
t.is( tobase64url( "pleasure." ), "cGxlYXN1cmUu" )
t.is( tobase64url( "easure." ), "ZWFzdXJlLg==" )
t.is( tobase64url( "sure." ), "c3VyZS4=" )

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."
local long64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb2"..
               "4sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBh"..
               "bmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYn"..
               "kgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVk"..
               "IGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLC"..
               "BleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBw"..
               "bGVhc3VyZS4="
t.is( tobase64url( longtxt ), long64 )

local msgtxt = '{"msg_en":"Hello","msg_jp":"こんにちは","msg_cn":"你好"'..
               ',"msg_kr":"안녕하세요","msg_ru":"Здравствуйте!"'..
               ',"msg_de":"Grüß Gott"}'
local msg64 = 'eyJtc2dfZW4iOiJIZWxsbyIsIm1zZ19qcCI6IuOBk-OCk-OBq-OBoeOBryI'..
              'sIm1zZ19jbiI6IuS9oOWlvSIsIm1zZ19rciI6IuyViOuFle2VmOyEuOyalC'..
              'IsIm1zZ19ydSI6ItCX0LTRgNCw0LLRgdGC0LLRg9C50YLQtSEiLCJtc2dfZ'..
              'GUiOiJHcsO8w58gR290dCJ9'
t.is( tobase64url( msgtxt ), msg64 )

t.done()

Inspired by