Journal codefool's Journal: b64codec
First stab at a base64 encoder class:
====== b64codec.h ======
// b64codec.h: interface for the b64codec class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_B64CODEC_H__E281B1B6_D880_4F90_A308_BD44E726E146__INCLUDED_)
#define AFX_B64CODEC_H__E281B1B6_D880_4F90_A308_BD44E726E146__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <ctype.h>
#include <list>
using namespace std;
#if !defined( byte )
typedef unsigned char byte;
#endif
#define E_B64CODEC_ENCODE_BUFFER_EMPTY 100
#define E_B64CODEC_ENCODE_INVALID_STATE 101
#define E_B64CODEC_DECODE_BUFFER_EMPTY 200
#define E_B64CODEC_DECODE_INVALID_STATE 201
class b64codec
{
private:
static const char codes[];
public:
enum { B64_ENC_IDLE = 0, B64_ENC_WAIT2, B64_ENC_WAIT3 };
enum { B64_DEC_IDLE = 0, B64_DEC_WAIT2, B64_DEC_WAIT3, B64_DEC_WAIT4, B64_DEC_EOF };
private:
byte m_byEncodeState;
list<byte> m_lstEncodeBuff;
byte m_byEncRem; // encoding remainder "register"
byte m_byDecodeState;
list<byte> m_lstDecodeBuff;
byte m_byDecRem; // decoding remainder "register"
public:
b64codec();
virtual ~b64codec();
byte encIndex( byte byIndex ); // given an index, answer the base64 char
byte decChar( byte byChar ); // given a base64 char, answer its index
byte *encodeBuffer( unsigned int *puiBuffSize, bool bClear = true );
byte *decodeBuffer( unsigned int *puiBuffSize, bool bClear = true );
bool encReady() { return (bool)( m_lstEncodeBuff.size() > 0 ); };
bool decReady() { return (bool)( m_lstDecodeBuff.size() > 0 ); };
void encReset();
byte encPush( byte byVal );
void encEnd();
byte encPop();
void decReset();
byte decPush( byte byVal );
void decEnd();
byte decPop();
private:
byte *getBuffer( list<byte> *plist, unsigned int *puiBuffSize, bool bClear );
};
#endif // !defined(AFX_B64CODEC_H__E281B1B6_D880_4F90_A308_BD44E726E146__INCLUDED_)
====== b64codec.cpp ======
// b64codec.cpp: implementation of the b64codec class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "b64codec.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// 0....:....1....:....2....:....3....:....4....:....5....:....6...6
// 0 5 0 5 0 5 0 5 0 5 0 5 0 4
const char b64codec::codes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0
// 0 1 2 3 4
b64codec::b64codec()
{
encReset();
decReset();
}
b64codec::~b64codec()
{
encReset();
decReset();
}
byte b64codec::encIndex( byte byIndex )
{
if( 0 <= byIndex && byIndex <= 63 )
return codes[ byIndex ];
return 0xff;
}
byte b64codec::decChar( byte byChar ) // given a base64 char, answer its index
{
byte byRet;
//
// rather than use a reverse lookup, this is much more
// efficient in terms of computer cycles.
//
if( 'A' <= byChar && byChar <= 'Z' )
byRet = byChar - 'A';
else if( 'a' <= byChar && byChar <= 'z' )
byRet = byChar - 71;
else if( '0' <= byChar && byChar <= '9' )
byRet = byChar + 4;
else if( byChar == '/' )
byRet = 63;
else if( byChar == '+' )
byRet = 64;
else
byRet = 0xff;
//fprintf( stderr, "Got byte '%c' which decoded to %02x, and is '%c'\n", byVal, byRet, c_b64_codes[ byRet ] );
return byRet;
}
void b64codec::encReset()
{
m_lstEncodeBuff.empty();
m_byEncodeState = B64_ENC_IDLE;
}
byte b64codec::encPush( byte byVal )
{
byte byAcc; // accumulator "register"
switch( m_byEncodeState )
{
case B64_ENC_IDLE:
//
// mov acc, byVal ; copy byVal
// and acc, 0xfc ; mask upper three taystes
// shr acc, 2 ; shift to lower three taystes
// sto acc ; store acc
// mov rem, byVal ; copy byVal
// and rem, 0x03 ; mask lower three tasytes
// shl rem, 4 ; put in hilo tayste
// inc state ; transition to state 1
// ret ; done
byAcc = ( ( byVal & 0xfc ) >> 2 );
m_byEncRem = ( ( byVal & 0x03 ) << 4 );
m_byEncodeState++;
break;
case B64_ENC_WAIT2:
// mov acc, byVal ; copy byVal
// and acc, 0xf0 ; mask upper nibble
// shr acc, 4 ; put in lower nibble
// or acc, rem ; or in upper nibble
// sto acc ; store acc
// mov rem, byVal ; copy byVal
// and rem, 0x0f ; mask lower nibble
// shl rem, 2 ; shift to middle taystes
// inc state ; transistion to state 2
// ret ; done
byAcc = ( ( byVal & 0xf0 ) >> 4 ) | m_byEncRem;
m_byEncRem = ( ( byVal & 0x0f ) << 2 );
m_byEncodeState++;
break;
case B64_ENC_WAIT3:
//
// mov acc, byVal ; copy byVal
// and acc, 0xc0 ; mask hi tayste
// shr acc, 6 ; shift to lo tayste
// or acc, rem ; or in middle taystes
// sto acc ; store acc
// mov acc, byVal ; copy byVal
// and acc, 0x3f ; mask lower three taystes
// sto acc ; store accumulator
// xor state,state ; transition to state 0
// ret
//
byAcc = ( ( byVal & 0xc0 ) >> 6 ) | m_byEncRem;
m_lstEncodeBuff.push_back( encIndex( byAcc ) );
byAcc = ( byVal & 0x3f );
m_byEncodeState = 0;
break;
}
m_lstEncodeBuff.push_back( encIndex( byAcc ) );
return m_byEncodeState;
}
byte b64codec::encPop()
{
if( !encReady() )
throw E_B64CODEC_ENCODE_BUFFER_EMPTY;
byte byRet = m_lstEncodeBuff.front();
m_lstEncodeBuff.pop_front();
return byRet;
}
void b64codec::encEnd()
{
if( B64_ENC_IDLE != m_byEncodeState )
{
m_lstEncodeBuff.push_back( encIndex( m_byEncRem ) ); // push what we have
switch( m_byEncodeState )
{
case B64_ENC_WAIT2: m_lstEncodeBuff.push_back( '=' );
case B64_ENC_WAIT3: m_lstEncodeBuff.push_back( '=' );
break;
default:
throw E_B64CODEC_ENCODE_INVALID_STATE;
}
}
}
void b64codec::decReset()
{
m_lstDecodeBuff.empty();
m_byDecodeState = B64_ENC_IDLE;
}
//
// take four octets of input, one at a time, and decode into three octets
//
byte b64codec::decPush( byte byVal )
{
byte byAcc; // accumulator "register"
//
// check for special chars that effect the machine
//
if( '=' == byVal )
m_byDecodeState = B64_DEC_EOF;
else if( !isspace( byVal ) ) // ignore whitespace
{
byVal = decChar( byVal ); // convert char to index
switch( m_byDecodeState )
{
case B64_DEC_IDLE:
// mov rem, byVal ; copy byVal
// shl rem, 2 ; clear lower tayste
// inc state ; transition to state 1
// ret ; done
m_byDecRem = ( byVal << 2 );
m_byDecodeState++;
break;
case B64_DEC_WAIT2:
// mov acc, byVal ; copy byVal
// and acc, 0x30 ; mask hilo tayste
// shr acc, 4 ; put in lower tayste
// or acc, rem ; or in upper three tasytes
// sto acc ; store the accumulator
// mov rem, byVal ; copy byVal
// and rem, 0x0f ; mask lower nibble
// shl rem, 4 ; shift to upper nibble
// inc state ; transition to state 2
// ret ; done
byAcc = ( ( byVal & 0x30 ) >> 4 ) | m_byDecRem;
m_lstDecodeBuff.push_back( byAcc );
m_byDecRem = ( ( byVal & 0x0f ) << 4 );
m_byDecodeState++;
break;
case B64_DEC_WAIT3:
// mov acc, byVal ; copy byVal
// and acc, 0x3c ; mask middle taystes
// shr acc, 2 ; shift to lower nibble
// or acc, rem ; or in upper nible
// sto acc ; store the accumulator
// mov rem, byVal ; copy byVal
// and rem, 0x03 ; mask lower tayste
// shl rem, 6 ; shift to upper tayste
// inc state ; transition to state 3
// ret ; done
byAcc = ( ( byVal & 0x3c ) >> 2 ) | m_byDecRem;
m_lstDecodeBuff.push_back( byAcc );
m_byDecRem = ( ( byVal & 0x03 ) << 6 );
m_byDecodeState++;
break;
case B64_DEC_WAIT4:
// mov acc, byVal ; copy byVal
// or acc, rem ; or in upper tastye
// sto acc ; store the accumulator
// xor state,state ; transition to state 0
// done
byAcc= ( byVal | m_byDecRem );
m_lstDecodeBuff.push_back( byAcc );
m_byDecodeState = 0;
break;
}
}
return m_byDecodeState;
}
void b64codec::decEnd()
{
if( B64_DEC_IDLE != m_byDecodeState && B64_DEC_EOF != m_byDecodeState )
{
m_lstDecodeBuff.push_back( m_byDecRem );
}
}
byte b64codec::decPop()
{
if( !decReady() )
throw E_B64CODEC_DECODE_BUFFER_EMPTY;
byte byRet = m_lstDecodeBuff.front();
m_lstDecodeBuff.pop_front();
return byRet;
}
byte *b64codec::getBuffer( list<byte> *plist, unsigned int *puiBuffSize, bool bClear )
{
byte *pRet = NULL;
*puiBuffSize = 0;
if( plist->size() )
{
pRet = new byte[ plist->size() ];
byte *p = pRet;
list<byte>::iterator iter;
for( iter = plist->begin(); iter != plist->end(); iter++ )
*p++ = *iter;
*puiBuffSize = plist->size();
if( bClear )
{
plist->empty();
}
}
return pRet;
}
byte *b64codec::encodeBuffer( unsigned int *puiBuffSize, bool bClear /*= true*/ )
{
byte *pRet = getBuffer( &m_lstEncodeBuff, puiBuffSize, bClear );
if( bClear )
{
m_byEncodeState = B64_ENC_IDLE;
}
return pRet;
}
byte *b64codec::decodeBuffer( unsigned int *puiBuffSize, bool bClear /*= true*/ )
{
byte *pRet = getBuffer( &m_lstDecodeBuff, puiBuffSize, bClear );
if( bClear )
{
m_byDecodeState = B64_ENC_IDLE;
}
return pRet;
}
====== b64codec.h ======
// b64codec.h: interface for the b64codec class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_B64CODEC_H__E281B1B6_D880_4F90_A308_BD44E726E146__INCLUDED_)
#define AFX_B64CODEC_H__E281B1B6_D880_4F90_A308_BD44E726E146__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif
#include <ctype.h>
#include <list>
using namespace std;
#if !defined( byte )
typedef unsigned char byte;
#endif
#define E_B64CODEC_ENCODE_BUFFER_EMPTY 100
#define E_B64CODEC_ENCODE_INVALID_STATE 101
#define E_B64CODEC_DECODE_BUFFER_EMPTY 200
#define E_B64CODEC_DECODE_INVALID_STATE 201
class b64codec
{
private:
static const char codes[];
public:
enum { B64_ENC_IDLE = 0, B64_ENC_WAIT2, B64_ENC_WAIT3 };
enum { B64_DEC_IDLE = 0, B64_DEC_WAIT2, B64_DEC_WAIT3, B64_DEC_WAIT4, B64_DEC_EOF };
private:
byte m_byEncodeState;
list<byte> m_lstEncodeBuff;
byte m_byEncRem; // encoding remainder "register"
byte m_byDecodeState;
list<byte> m_lstDecodeBuff;
byte m_byDecRem; // decoding remainder "register"
public:
b64codec();
virtual ~b64codec();
byte encIndex( byte byIndex );
byte decChar( byte byChar );
byte *encodeBuffer( unsigned int *puiBuffSize, bool bClear = true );
byte *decodeBuffer( unsigned int *puiBuffSize, bool bClear = true );
bool encReady() { return (bool)( m_lstEncodeBuff.size() > 0 ); };
bool decReady() { return (bool)( m_lstDecodeBuff.size() > 0 ); };
void encReset();
byte encPush( byte byVal );
void encEnd();
byte encPop();
void decReset();
byte decPush( byte byVal );
void decEnd();
byte decPop();
private:
byte *getBuffer( list<byte> *plist, unsigned int *puiBuffSize, bool bClear );
};
#endif
====== b64codec.cpp ======
// b64codec.cpp: implementation of the b64codec class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "b64codec.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
const char b64codec::codes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
b64codec::b64codec()
{
encReset();
decReset();
}
b64codec::~b64codec()
{
encReset();
decReset();
}
byte b64codec::encIndex( byte byIndex )
{
if( 0 <= byIndex && byIndex <= 63 )
return codes[ byIndex ];
return 0xff;
}
byte b64codec::decChar( byte byChar )
{
byte byRet;
if( 'A' <= byChar && byChar <= 'Z' )
byRet = byChar - 'A';
else if( 'a' <= byChar && byChar <= 'z' )
byRet = byChar - 71;
else if( '0' <= byChar && byChar <= '9' )
byRet = byChar + 4;
else if( byChar == '/' )
byRet = 63;
else if( byChar == '+' )
byRet = 64;
else
byRet = 0xff;
return byRet;
}
void b64codec::encReset()
{
m_lstEncodeBuff.empty();
m_byEncodeState = B64_ENC_IDLE;
}
byte b64codec::encPush( byte byVal )
{
byte byAcc;
switch( m_byEncodeState )
{
case B64_ENC_IDLE:
byAcc = ( ( byVal & 0xfc ) >> 2 );
m_byEncRem = ( ( byVal & 0x03 ) << 4 );
m_byEncodeState++;
break;
case B64_ENC_WAIT2:
byAcc = ( ( byVal & 0xf0 ) >> 4 ) | m_byEncRem;
m_byEncRem = ( ( byVal & 0x0f ) << 2 );
m_byEncodeState++;
break;
case B64_ENC_WAIT3:
byAcc = ( ( byVal & 0xc0 ) >> 6 ) | m_byEncRem;
m_lstEncodeBuff.push_back( encIndex( byAcc ) );
byAcc = ( byVal & 0x3f );
m_byEncodeState = 0;
break;
}
m_lstEncodeBuff.push_back( encIndex( byAcc ) );
return m_byEncodeState;
}
byte b64codec::encPop()
{
if( !encReady() )
throw E_B64CODEC_ENCODE_BUFFER_EMPTY;
byte byRet = m_lstEncodeBuff.front();
m_lstEncodeBuff.pop_front();
return byRet;
}
void b64codec::encEnd()
{
if( B64_ENC_IDLE != m_byEncodeState )
{
m_lstEncodeBuff.push_back( encIndex( m_byEncRem ) ); // push what we have
switch( m_byEncodeState )
{
case B64_ENC_WAIT2: m_lstEncodeBuff.push_back( '=' );
case B64_ENC_WAIT3: m_lstEncodeBuff.push_back( '=' );
break;
default:
throw E_B64CODEC_ENCODE_INVALID_STATE;
}
}
}
void b64codec::decReset()
{
m_lstDecodeBuff.empty();
m_byDecodeState = B64_ENC_IDLE;
}
//
// take four octets of input, one at a time, and decode into three octets
//
byte b64codec::decPush( byte byVal )
{
byte byAcc;
if( '=' == byVal )
m_byDecodeState = B64_DEC_EOF;
else if( !isspace( byVal ) )
{
byVal = decChar( byVal ); // convert char to index
switch( m_byDecodeState )
{
case B64_DEC_IDLE:
m_byDecRem = ( byVal << 2 );
m_byDecodeState++;
break;
case B64_DEC_WAIT2:
byAcc = ( ( byVal & 0x30 ) >> 4 ) | m_byDecRem;
m_lstDecodeBuff.push_back( byAcc );
m_byDecRem = ( ( byVal & 0x0f ) << 4 );
m_byDecodeState++;
break;
case B64_DEC_WAIT3:
byAcc = ( ( byVal & 0x3c ) >> 2 ) | m_byDecRem;
m_lstDecodeBuff.push_back( byAcc );
m_byDecRem = ( ( byVal & 0x03 ) << 6 );
m_byDecodeState++;
break;
case B64_DEC_WAIT4:
byAcc= ( byVal | m_byDecRem );
m_lstDecodeBuff.push_back( byAcc );
m_byDecodeState = 0;
break;
}
}
return m_byDecodeState;
}
void b64codec::decEnd()
{
if( B64_DEC_IDLE != m_byDecodeState && B64_DEC_EOF != m_byDecodeState )
{
m_lstDecodeBuff.push_back( m_byDecRem );
}
}
byte b64codec::decPop()
{
if( !decReady() )
throw E_B64CODEC_DECODE_BUFFER_EMPTY;
byte byRet = m_lstDecodeBuff.front();
m_lstDecodeBuff.pop_front();
return byRet;
}
byte *b64codec::getBuffer( list<byte> *plist, unsigned int *puiBuffSize, bool bClear )
{
byte *pRet = NULL;
*puiBuffSize = 0;
if( plist->size() )
{
pRet = new byte[ plist->size() ];
byte *p = pRet;
list<byte>::iterator iter;
for( iter = plist->begin(); iter != plist->end(); iter++ )
*p++ = *iter;
*puiBuffSize = plist->size();
if( bClear )
{
plist->empty();
}
}
return pRet;
}
byte *b64codec::encodeBuffer( unsigned int *puiBuffSize, bool bClear
{
byte *pRet = getBuffer( &m_lstEncodeBuff, puiBuffSize, bClear );
if( bClear )
{
m_byEncodeState = B64_ENC_IDLE;
}
return pRet;
}
byte *b64codec::decodeBuffer( unsigned int *puiBuffSize, bool bClear
{
byte *pRet = getBuffer( &m_lstDecodeBuff, puiBuffSize, bClear );
if( bClear )
{
m_byDecodeState = B64_ENC_IDLE;
}
return pRet;
}