#ifndef _WDL_BASE64_H_
#define _WDL_BASE64_H_

#ifndef WDL_BASE64_FUNCDECL
#define WDL_BASE64_FUNCDECL static
#endif

static const char wdl_base64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
WDL_BASE64_FUNCDECL void wdl_base64encode(const unsigned char *in, char *out, int len)
{
  while (len >= 6)
  {
    const int accum = (in[0] << 16) + (in[1] << 8) + in[2];
    const int accum2 = (in[3] << 16) + (in[4] << 8) + in[5];
    out[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
    out[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
    out[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
    out[3] = wdl_base64_alphabet[accum & 0x3F];
    out[4] = wdl_base64_alphabet[(accum2 >> 18) & 0x3F];
    out[5] = wdl_base64_alphabet[(accum2 >> 12) & 0x3F];
    out[6] = wdl_base64_alphabet[(accum2 >> 6) & 0x3F];
    out[7] = wdl_base64_alphabet[accum2 & 0x3F];
    out+=8;
    in+=6;
    len-=6;
  }

  if (len >= 3)
  {
    const int accum = (in[0]<<16)|(in[1]<<8)|in[2];
    out[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
    out[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
    out[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
    out[3] = wdl_base64_alphabet[accum & 0x3F];    
    in+=3;
    len-=3;
    out+=4;
  }

  if (len>0)
  {
    if (len == 2)
    {
      const int accum = (in[0] << 8) | in[1];
      out[0] = wdl_base64_alphabet[(accum >> 10) & 0x3F];
      out[1] = wdl_base64_alphabet[(accum >> 4) & 0x3F];
      out[2] = wdl_base64_alphabet[(accum & 0xF)<<2];
    }
    else
    {
      const int accum = in[0];
      out[0] = wdl_base64_alphabet[(accum >> 2) & 0x3F];
      out[1] = wdl_base64_alphabet[(accum & 0x3)<<4];
      out[2]='=';  
    }
    out[3]='=';  
    out+=4;
  }

  out[0]=0;
}

WDL_BASE64_FUNCDECL int wdl_base64decode(const char *src, unsigned char *dest, int destsize)
{
  static const char *tab = // poolable string
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\xff\xff\xff\x3f"
    "\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\xff\xff\xff"
    "\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
    "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\xff"
    "\xff\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28"
    "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
    "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";


  unsigned int accum=0;
  int nbits=0, wpos=0;

  if (destsize <= 0) return 0;

  for (;;)
  {
    const int v=(int)tab[*(unsigned char *)src++];
    if (v<0) return wpos;

    accum += v;
    nbits += 6;   

    if (nbits >= 8)
    {
      nbits-=8;
      dest[wpos] = (accum>>nbits)&0xff;
      if (++wpos >= destsize) return wpos;
    }
    accum <<= 6;
  }
}


#endif
