Base64Transcoder.C 7.83 KB
Newer Older
Francois Gygi committed
1 2
////////////////////////////////////////////////////////////////////////////////
//
Francois Gygi committed
3 4 5 6
// Copyright (c) 2008 The Regents of the University of California
//
// This file is part of Qbox
//
Francois Gygi committed
7 8
// Qbox is distributed under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 2 of
Francois Gygi committed
9 10 11 12 13 14
// the License, or (at your option) any later version.
// See the file COPYING in the root directory of this distribution
// or <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
//
Francois Gygi committed
15 16 17 18 19 20
// Base64Transcoder.C
//
////////////////////////////////////////////////////////////////////////////////

#include "Base64Transcoder.h"
#include <iostream>
21
#include <cstdio>
Francois Gygi committed
22
#include <string>
23
#include <cstring> // memcpy
Francois Gygi committed
24 25 26 27 28 29 30
#include <cassert>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
Base64Transcoder::Base64Transcoder()
{
  // initialize encoding/decoding tables
31

32 33
  for (int i = 0; i < 26; i++)
  {
Francois Gygi committed
34 35 36
      etable[i] = 'A' + i;
      etable[26 + i] = 'a' + i;
  }
37 38
  for (int i = 0; i < 10; i++)
  {
Francois Gygi committed
39 40 41 42 43
      etable[52 + i] = '0' + i;
  }
  etable[62] = '+';
  etable[63] = '/';

44 45
  for (int i = 0; i < 255; i++)
  {
Francois Gygi committed
46 47
      dtable[i] = 0x80;
  }
48 49
  for (int i = 'A'; i <= 'Z'; i++)
  {
Francois Gygi committed
50 51
      dtable[i] = 0 + (i - 'A');
  }
52 53
  for (int i = 'a'; i <= 'z'; i++)
  {
Francois Gygi committed
54 55
      dtable[i] = 26 + (i - 'a');
  }
56 57
  for (int i = '0'; i <= '9'; i++)
  {
Francois Gygi committed
58 59 60 61 62 63 64 65 66
      dtable[i] = 52 + (i - '0');
  }
  dtable['+'] = 62;
  dtable['/'] = 63;
  dtable['='] = 0;
}

////////////////////////////////////////////////////////////////////////////////
int Base64Transcoder::encode(int nbytes, const byte* const from, char* const to)
67
{
Francois Gygi committed
68 69
  const byte* fptr = from;
  char* tptr = to;
70

Francois Gygi committed
71
  int n3 = nbytes / 3; // number of groups of three bytes
72

Francois Gygi committed
73 74 75 76 77
  while ( n3-- > 0 )
  {
    byte ig0 = *fptr++;
    byte ig1 = *fptr++;
    byte ig2 = *fptr++;
78

Francois Gygi committed
79 80 81
    *tptr++ = etable[ig0 >> 2];
    *tptr++ = etable[((ig0 & 3) << 4) | (ig1 >> 4)];
    *tptr++ = etable[((ig1 & 0xF) << 2) | (ig2 >> 6)];
82
    *tptr++ = etable[ig2 & 0x3F];
Francois Gygi committed
83
  }
84

Francois Gygi committed
85
  int nr = nbytes % 3; // remaining bytes
86

Francois Gygi committed
87 88 89 90 91
  if ( nr == 2 )
  {
    byte ig0 = *fptr++;
    byte ig1 = *fptr++;
    byte ig2 = 0;
92

Francois Gygi committed
93 94 95 96 97 98 99 100 101
    *tptr++ = etable[ig0 >> 2];
    *tptr++ = etable[((ig0 & 3) << 4) | (ig1 >> 4)];
    *tptr++ = etable[((ig1 & 0xF) << 2) | (ig2 >> 6)];
    *tptr++ = '=';
  }
  else if ( nr == 1 )
  {
    byte ig0 = *fptr++;
    byte ig1 = 0;
102

Francois Gygi committed
103 104 105 106 107
    *tptr++ = etable[ig0 >> 2];
    *tptr++ = etable[((ig0 & 3) << 4) | (ig1 >> 4)];
    *tptr++ = '=';
    *tptr++ = '=';
  }
108 109

  return 0;
Francois Gygi committed
110 111 112
}

////////////////////////////////////////////////////////////////////////////////
113 114
int Base64Transcoder::decode(const int nchars, const char* const from,
  byte* const to)
Francois Gygi committed
115 116 117 118 119 120 121
{
  // Decode Base64 chars in array "from" into bytes in array "to"
  // White space and new lines are skipped
  // extra characters at end that do not form a valid group of 4 chars are
  // ignored.
  // nchars: number of chars in array "from"
  // the number of bytes successfully translated is returned
122

Francois Gygi committed
123
  byte a2,a3,b0,b1,b2,b3;
Francois Gygi committed
124 125 126 127
  int c;
  const char* fptr = from;
  const char* const fptr_end = from+nchars+1;
  byte* tptr = to;
128

Francois Gygi committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
  while ( fptr < fptr_end-4 )
  {
    // get 4 valid characters from input string
    do
    {
      c = *fptr++;
    }
    while ( (c <= ' ') && (fptr < fptr_end) );
    if ( fptr >= fptr_end )
    {
#ifdef DEBUG
      cerr << " Base64Transcoder::decode: end of string reached reading c0 "
           << endl;
#endif
      break;
    }
Francois Gygi committed
145
    // a0 = (byte) c;
Francois Gygi committed
146
    b0 = (byte) dtable[c];
147

Francois Gygi committed
148 149 150 151 152 153 154 155 156 157 158 159 160
    do
    {
      c = *fptr++;
    }
    while ( (c <= ' ') && (fptr < fptr_end) );
    if ( fptr >= fptr_end )
    {
#ifdef DEBUG
      cerr << " Base64Transcoder::decode: end of string reached reading c1 "
           << endl;
#endif
      break;
    }
Francois Gygi committed
161
    // a1 = (byte) c;
Francois Gygi committed
162
    b1 = (byte) dtable[c];
163

Francois Gygi committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    do
    {
      c = *fptr++;
    }
    while ( (c <= ' ') && (fptr < fptr_end) );
    if ( fptr >= fptr_end )
    {
#ifdef DEBUG
      cerr << " Base64Transcoder::decode: end of string reached reading c2 "
           << endl;
#endif
      break;
    }
    a2 = (byte) c;
    b2 = (byte) dtable[c];
179

Francois Gygi committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    do
    {
      c = *fptr++;
    }
    while ( (c <= ' ') && (fptr < fptr_end) );
    if ( (c <= ' ') && fptr >= fptr_end )
    {
#ifdef DEBUG
      cerr << " Base64Transcoder::decode: end of string reached reading c3\n"
           << " (without reading a valid c3) " << endl;
#endif
      break;
    }
    a3 = (byte) c;
    b3 = (byte) dtable[c];
195

Francois Gygi committed
196 197 198 199 200 201 202 203
    if ((b0|b1|b2|b3) & 0x80)
    {
#ifdef DEBUG
      cerr << " Base64Transcoder::decode: Illegal character in input: "
           << endl;
#endif
      return tptr - to;
    }
204

Francois Gygi committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
    if ( a3 == '=' )
    {
      if ( a2 == '=' )
      {
        // write 1 byte only
        *tptr++ = (b0 << 2) | (b1 >> 4);
      }
      else
      {
        // write 2 bytes only
        *tptr++ = (b0 << 2) | (b1 >> 4);
        *tptr++ = (b1 << 4) | (b2 >> 2);
      }
    }
    else
    {
      // write 3 bytes
      *tptr++ = (b0 << 2) | (b1 >> 4);
      *tptr++ = (b1 << 4) | (b2 >> 2);
      *tptr++ = (b2 << 6) | b3;
    }
226

Francois Gygi committed
227 228 229 230 231 232 233 234
  }
#ifdef DEBUG
  if ( fptr >= fptr_end )
  {
    cerr << " Base64Transcoder::decode: end of string reached in input: "
         << endl;
  }
#endif
235

Francois Gygi committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
  return tptr - to;
}

////////////////////////////////////////////////////////////////////////////////
void Base64Transcoder::byteswap_double(size_t n, double* const x)
{
  if (n==0) return;
  unsigned char* c = (unsigned char*) x;
  while ( n-- > 0 )
  {
    unsigned char tmp;
    tmp = c[7]; c[7] = c[0]; c[0] = tmp;
    tmp = c[6]; c[6] = c[1]; c[1] = tmp;
    tmp = c[5]; c[5] = c[2]; c[2] = tmp;
    tmp = c[4]; c[4] = c[3]; c[3] = tmp;
251

Francois Gygi committed
252 253
    c+=8;
  }
254
}
Francois Gygi committed
255 256 257 258 259 260 261 262 263 264 265

////////////////////////////////////////////////////////////////////////////////
void Base64Transcoder::byteswap_int(size_t n, int* const x)
{
  if (n==0) return;
  unsigned char* c = (unsigned char*) x;
  while ( n-- > 0 )
  {
    unsigned char tmp;
    tmp = c[3]; c[3] = c[0]; c[0] = tmp;
    tmp = c[2]; c[2] = c[1]; c[1] = tmp;
266

Francois Gygi committed
267 268
    c+=4;
  }
269
}
Francois Gygi committed
270 271 272 273

////////////////////////////////////////////////////////////////////////////////
int Base64Transcoder::print(const string buf, ostream& o)
{
274
  return print(buf.size(),buf.c_str(),o);
Francois Gygi committed
275 276 277 278 279 280 281
}

////////////////////////////////////////////////////////////////////////////////
int Base64Transcoder::print(int nchars, const char* const buf, ostream& o)
{
  const char* b = buf;
  int nl = nchars / 72;
282

283 284 285 286 287 288 289 290
  // compute total size of output string including newline chars
  int outstr_size = nchars + nl;
  if ( nchars%72 != 0 )
    outstr_size++;
  char* outstr = new char[outstr_size];
  char* p = outstr;

  // assemble output string
291 292
  for ( int i = 0; i < nl; i++ )
  {
293 294
    memcpy(p,b,72*sizeof(char));
    p[72] = '\n';
295
    b += 72;
296
    p += 73;
297
  }
298

299 300
  if ( nchars%72 != 0 )
  {
301 302 303
    int size = nchars%72;
    memcpy(p,b,size*sizeof(char));
    p[size] = '\n';
304
  }
305 306 307

  o.write(outstr,outstr_size);
  delete [] outstr;
308 309 310
  return 0;
}

311 312 313 314 315 316
////////////////////////////////////////////////////////////////////////////////
int Base64Transcoder::print(const string buf, FILE* outfile)
{
  return print(buf.size(),buf.c_str(),outfile);
}

317 318 319 320 321
////////////////////////////////////////////////////////////////////////////////
int Base64Transcoder::print(int nchars, const char* const buf, FILE* outfile)
{
  const char* b = buf;
  int nl = nchars / 72;
322 323 324 325 326 327 328 329 330

  // compute total size of output string including newline chars
  int outstr_size = nchars + nl;
  if ( nchars%72 != 0 )
    outstr_size++;
  char* outstr = new char[outstr_size];
  char* p = outstr;

  // assemble output string
331 332
  for ( int i = 0; i < nl; i++ )
  {
333 334
    memcpy(p,b,72*sizeof(char));
    p[72] = '\n';
335
    b += 72;
336
    p += 73;
337
  }
338

339 340
  if ( nchars%72 != 0 )
  {
341 342 343
    int size = nchars%72;
    memcpy(p,b,size*sizeof(char));
    p[size] = '\n';
344
  }
345 346 347

  fwrite(outstr,sizeof(char),outstr_size,outfile);
  delete [] outstr;
348 349
  return 0;
}