Want to read Slashdot from your mobile device? Point it at m.slashdot.org and keep reading!

 



Forgot your password?
typodupeerror
×
Programming

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;
}

Never test for an error condition you don't know how to handle. -- Steinbach

Working...