C++ implementation of base64

Introduction to base64

There have been many people on the base64 network who have described it. The following is an excerpt from the wiki:

Base64 is a representation method for representing binary data based on 64 printable characters. Since 2 to the 6th power is equal to 64, every 6 bits is a unit, corresponding to a certain printable character. Three bytes have 24 bits, corresponding to 4 Base64 units, that is, 3 bytes can represent 4 printable characters. It can be used as a transfer encoding for e-mail. The printable characters in Base64 include letters AZ, az, numbers 0-9, so there are 62 characters in total, and the two printable symbols are different in different systems.
In MIME-formatted email, base64 can be used to encode binary byte sequence data into text consisting of a sequence of ASCII characters. When used, specify base64 in the transfer encoding method. The characters used include 26 uppercase and lowercase letters, plus 10 numbers, plus sign "+", slash "/", a total of 64 characters, and equal sign "=" is used as a suffix.
The complete base64 definition can be found in RFC 1421 and RFC 2045. The encoded data is slightly longer than the original data, 4/3 of the original. In e-mail, according to RFC 822, every 76 characters, you need to add a carriage return and line feed. It can be estimated that the length of the encoded data is about 135.1% of the original length. When converting, put three bytes of data into a 24-bit buffer one after another, and the first byte occupies the high position. If the data is less than 3 bytes, the remaining bits in the buffer are filled with 0. Then, each time 6 (because 26=64) bits are taken out, the characters in ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ are selected as the encoded output according to their values. Continue until all input data conversions are complete. When the original data length is not an integral multiple of 3, if there is one input data left at the end, add two "=" after the encoding result; if there are two remaining input data at the end, add one "=" after the encoding result; if If there is no data left, do not add anything, so as to ensure the correctness of data restoration.

For a complete base64 description, please refer to wiki_base64.
This article mainly states a C++ code implementation of base64 encoding.

base64_operation.h

First list the declaration header files of the implemented API:

#ifndef _BASE64_OPERATION_H_
#define _BASE64_OPERATION_H_

#include <iostream>
#include <stdio.h>

typedef enum 
{
    E_BASH64_OK = 0,
    E_BASH64_ERROR_PARAM,
    E_BASH64_FILE_ERROR,
    E_BASH64_BUFFER_OVERFLOW,
    E_BASH64_ERROR_USUAL
}BASE64_RESULT;

class Base64_Operation
{
public:
    /**
     * [base64_GetInstance Get the object pointer]
     * @return  [the object pointer]
     */
    static Base64_Operation* base64_GetInstance(void);
    Base64_Operation();
    ~Base64_Operation();
    /**
     * [base64_file_encode Ecode a file to base64 format file]
     * @param  pInFile  [The input file pointer from fopen(), must be opened with "rb"]
     * @param  pOutFile [The output file pointer from fopen(), must be opened with "wb+"]
     * @return          [The error code]
     */
    BASE64_RESULT base64_file_encode(FILE*  pInFile, FILE* pOutFile);
    /**
     * [base64_file_decode decode a file from base64 format file]
     * @param  pInFile  [The input file pointer from fopen(), must be opened with "rb"]
     * @param  pOutFile [The output file pointer from fopen(), must be opened with "wb+"]
     * @return          [The error code]
     */
    BASE64_RESULT base64_file_decode(FILE* pInFile, FILE* pOutFile);
    /**
     * [base64_hex_encode Encode a buffer data to base64 format]
     * @param  pInBuffer      [The normal data buffer]
     * @param  InBufferLen    [The normal data length]
     * @param  pOutBuffer     [The output buffer]
     * @param  pOutBufferLen  [The output buffer of base64 length]
     * @return                [The error code]
     */
    BASE64_RESULT base64_hex_encode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer,unsigned int *pOutBufferLen);
    /**
     * [base64_hex_decode decode a buffer data from base64 format]
     * @param  pInBuffer     [The base64 format buffer]
     * @param  InBufferLen   [The base64 buffer length]
     * @param  pOutBuffer    [The output buffer pointer]
     * @param  pOutBufferLen [The output buffer length]
     * @return               [The error code]
     */
    BASE64_RESULT base64_hex_decode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer,unsigned int *pOutBufferLen); 
protected:
private:
    static Base64_Operation* Instance;
    static void _initDecTable(void);
    static unsigned int _singleGroupDecode(char* inBuffer, char* outBuffer);
    static void _singleGroupEncode(char* inBuffer, char* outBuffer);
};

#endif

It mainly implements the encoding and decoding of base64 based on user buffer (supports less than the maximum length that can be represented by unsigned int); realizes the encoding and decoding of base64 of the file. In addition, the complete specification of base64 requires a line break every 76 bytes. This implementation does not consider this requirement. I think it should be better for users to add line breaks to the encoded base64 according to their own needs;

base64_operation.cpp

#include "base64_operation.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using std::cout;
using std::cin;
using std::endl;
using std::hex;


#define DECTABLE_LEN 123
#define EQUAL_SIGN_MAGIC 0xFF
#define FILE_BUFFER_LEN 0x200000
#define FILE_OUTTMP_LEN 0x300000

#define OPEN_DBG 0

Base64_Operation* Base64_Operation::Instance = NULL;

static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static unsigned char charDecTable[DECTABLE_LEN+1];

void Base64_Operation::_initDecTable()
{
    int index = 0;

    memset(charDecTable, 0x0, DECTABLE_LEN);

    for(index = 'A'; index <= 'Z';index++)
    {
        charDecTable[index] = index - 'A';
    }

    for(index = 'a'; index <= 'z';index++)
    {
        charDecTable[index] = index - 'a' + 26;
    }

    for(index = '0'; index <= '9';index++)
    {
        charDecTable[index] = index - '0' + 52;
    }

    charDecTable['+'] = 62;
    charDecTable['/'] = 63;
    charDecTable['='] = EQUAL_SIGN_MAGIC;
}

void Base64_Operation::_singleGroupEncode(char* inBuffer, char* outBuffer)
{
    unsigned int order;

    order = (inBuffer[0]&0xFC)>>2;
    outBuffer[0] = base64Char[order];
#if OPEN_DBG
    cout<<order<<endl;
    cout<<outBuffer[0]<<endl;
#endif

    order = ((inBuffer[0]&0x3)<<4)|((inBuffer[1]&0xF0)>>4);
    outBuffer[1] = base64Char[order];

#if OPEN_DBG
    cout<<order<<endl;
    cout<<outBuffer[1]<<endl;
#endif

    order = ((inBuffer[1]&0xF)<<2)|((inBuffer[2]&0xC0)>>6);
    outBuffer[2] = base64Char[order];

#if OPEN_DBG
    cout<<order<<endl;
    cout<<outBuffer[2]<<endl;
#endif

    order = inBuffer[2]&0x3F;
    outBuffer[3] = base64Char[order];

#if OPEN_DBG
    cout<<order<<endl;
    cout<<outBuffer[3]<<endl;
#endif
}

unsigned int Base64_Operation::_singleGroupDecode(char* inBuffer, char* outBuffer)
{
    unsigned int resLen = 0;
    unsigned char value[4];
    unsigned int index = 0;

#if 1
    for(index = 0;index<4;index++)
    {
        if((inBuffer[index]>= 'A') && (inBuffer[index]<='Z'))
        {
        }
        else if((inBuffer[index] >= 'a') && (inBuffer[index]<='z'))
        {
        }
        else if((inBuffer[index] >= '0') && (inBuffer[index]<='9'))
        {
        }
        else if((inBuffer[index] == '+') || (inBuffer[index] == '/'))
        {   
        }
        else if(inBuffer[index] == '=')
        {
            if(index<=1)
                return resLen;
        }
        else
        {
            return resLen;
        }
    }
#endif
    value[0] = charDecTable[(unsigned char)inBuffer[0]]; 
    value[1] = charDecTable[(unsigned char)inBuffer[1]]; 
    value[2] = charDecTable[(unsigned char)inBuffer[2]]; 
    value[3] = charDecTable[(unsigned char)inBuffer[3]]; 

    if((value[2] == EQUAL_SIGN_MAGIC) && (value[3] == EQUAL_SIGN_MAGIC))
    {
        resLen = 1;
        outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
    }
    else if(value[3] == EQUAL_SIGN_MAGIC)
    {
        resLen = 2;
        outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
        outBuffer[1] = ((value[1]&0x0F)<<4) | ((value[2]&0x3C)>>2);
    }
    else 
    {
        resLen = 3;
        outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
        outBuffer[1] = ((value[1]&0x0F)<<4) | ((value[2]&0x3C)>>2);
        outBuffer[2] = ((value[2]&0x03)<<6) | (value[3]&0x3F);
    }


    return resLen;
}

Base64_Operation *Base64_Operation::base64_GetInstance()
{
    if(Instance == NULL)
    {
        Instance = new Base64_Operation();
        Instance->_initDecTable();

#if OPEN_DBG
        int index = 0;
        for(index = 0; index<=DECTABLE_LEN; index++)
        {
            cout<<index<<":  "<<charDecTable[index]<<endl;
        }
#endif
    }
    return Instance;
}

Base64_Operation::Base64_Operation()
{
    cout<<"Create a base64 object"<<endl;
}

Base64_Operation::~Base64_Operation()
{
    cout<<"Destroy a base64 object"<<endl;
    Instance = NULL;
}


BASE64_RESULT Base64_Operation::base64_file_encode(FILE*  pInFile, FILE* pOutFile)
{
    if(pInFile == NULL || pOutFile == NULL)
    {
        cout<<__FUNCTION__<<" "<<__LINE__<<" The file pointer can't be NULL"<<endl;
        return E_BASH64_FILE_ERROR;
    }
    unsigned int readLen = 0;
    unsigned int outBufferLen = 0;

    //The hexadecimal digit:0x1FFFFE can be devided by 3;
    char *tmpBuffer = new char[0x1FFFFE];
    if(tmpBuffer == NULL)
    {
        return E_BASH64_BUFFER_OVERFLOW;
    }
    char *outBuffer = new char[FILE_OUTTMP_LEN];
    if(outBuffer == NULL)
    {
        delete []tmpBuffer;
        return E_BASH64_BUFFER_OVERFLOW;
    }

    do
    {
        memset(tmpBuffer,0x00, 0x1FFFFE);
        readLen = fread(tmpBuffer, 1, 0x1FFFFE, pInFile);
        if(E_BASH64_OK == (this->base64_hex_encode(tmpBuffer, readLen, outBuffer, &outBufferLen)))
        {
            if(fwrite(outBuffer, outBufferLen, 1, pOutFile)!=1)
            {
                cout<<__FUNCTION__<<" write file error!"<<endl;
                delete []tmpBuffer;
                delete []outBuffer;
                return E_BASH64_FILE_ERROR;
            }
        }
        else
        {
            cout<<__FUNCTION__<<" encode base64 failed!"<<endl;
            delete []tmpBuffer;
            delete []outBuffer;
            return E_BASH64_ERROR_USUAL;
        }

    }while(readLen==0x1FFFFE);

    delete []tmpBuffer;
    delete []outBuffer;
    return E_BASH64_OK;
}


BASE64_RESULT Base64_Operation::base64_file_decode(FILE* pInFile, FILE* pOutFile)
{
    if(pInFile == NULL || pOutFile == NULL)
    {
        cout<<__FUNCTION__<<" "<<__LINE__<<" The file pointer can't be NULL"<<endl;
        return E_BASH64_FILE_ERROR;
    }

    unsigned int readLen = 0;
    unsigned int outBufferLen = 0;

    char *tmpBuffer = new char[FILE_BUFFER_LEN];
    if(tmpBuffer == NULL)
    {
        return E_BASH64_BUFFER_OVERFLOW;
    }
    char *outBuffer = new char[FILE_OUTTMP_LEN];
    if(outBuffer == NULL)
    {
        delete []tmpBuffer;
        return E_BASH64_BUFFER_OVERFLOW;
    }
    do
    {
        memset(tmpBuffer,0x00, FILE_BUFFER_LEN);
        readLen = fread(tmpBuffer, 1, FILE_BUFFER_LEN, pInFile);
        if(E_BASH64_OK == (this->base64_hex_decode(tmpBuffer, readLen, outBuffer, &outBufferLen)))
        {
            if(fwrite(outBuffer, outBufferLen, 1, pOutFile)!=1)
            {
                cout<<__FUNCTION__<<" write file error!"<<endl;
                delete []tmpBuffer;
                delete []outBuffer;
                return E_BASH64_FILE_ERROR;
            }
        }
        else
        {
            cout<<__FUNCTION__<<" decode base64 failed!"<<endl;
            delete []tmpBuffer;
            delete []outBuffer;
            return E_BASH64_ERROR_USUAL;
        }

    }while(readLen==FILE_BUFFER_LEN);

    delete []tmpBuffer;
    delete []outBuffer;
    return E_BASH64_OK;
}

BASE64_RESULT Base64_Operation::base64_hex_encode(char* pInBuffer,unsigned int InBufferLen, char *pOutBuffer, unsigned int *pOutBufferLen)
{
    if(pInBuffer == NULL || pOutBuffer == NULL || pOutBufferLen == NULL)
    {
        cout<<__FUNCTION__<<" "<<"The buffer can't be NULL"<<endl;
        return E_BASH64_ERROR_PARAM;
    }

    char *pRunInBuffer = pInBuffer;
    char *pRunOutBuffer = pOutBuffer;
    char withoutEnc[3];
    char withEnc[4];
    unsigned int order = 0;

    *pOutBufferLen = 0;

    if(InBufferLen >= 3)
    {
        do
        {
            memcpy(withoutEnc, pRunInBuffer, 3);
            this->_singleGroupEncode(withoutEnc, withEnc);
            memcpy(pRunOutBuffer, withEnc, 4);

            pRunOutBuffer   = pRunOutBuffer + 4;
            *pOutBufferLen  = *pOutBufferLen + 4;
            InBufferLen     = InBufferLen-3;
            pRunInBuffer    = pRunInBuffer+3;

        }while(InBufferLen>=3);
    }

    if(InBufferLen == 2)
    {
        order = (pRunInBuffer[0]&0xFC)>>2;
        pRunOutBuffer[0] = base64Char[order];

        order = ((pRunInBuffer[0]&0x3)<<4) | ((pRunInBuffer[1]&0xF0)>>4);
        pRunOutBuffer[1] = base64Char[order];

        order = (pRunInBuffer[1]&0xF)<<2;
        pRunOutBuffer[2] = base64Char[order];

        pRunOutBuffer[3] = '=';

        *pOutBufferLen = *pOutBufferLen + 4;
    }
    else if(InBufferLen == 1)
    {
        order = (pRunInBuffer[0]&0xFC)>>2;
        pRunOutBuffer[0] = base64Char[order];

        order = (pRunInBuffer[0]&0x3)<<4;
        pRunOutBuffer[1] = base64Char[order];

        pRunOutBuffer[2] = '=';
        pRunOutBuffer[3] = '=';

        *pOutBufferLen = *pOutBufferLen + 4;
    }

    return E_BASH64_OK; 
}

BASE64_RESULT Base64_Operation::base64_hex_decode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer, unsigned int *pOutBufferLen)
{
    if(pInBuffer == NULL || pOutBuffer == NULL || pOutBufferLen == NULL)
    {
        cout<<__FUNCTION__<<" "<<"The buffer can't be NULL"<<endl;
        return E_BASH64_ERROR_PARAM;
    }

    if(InBufferLen%4)
    {
        cout<<__FUNCTION__<<" "<<"The inBufferLen must be devided by 4"<<endl;
        return E_BASH64_ERROR_PARAM;
    }

    char *pRunInBuffer  = pInBuffer;
    char *pRunOutBuffer = pOutBuffer;
    char withoutEnc[3];
    char withEnc[4];
    unsigned int resLen = 0;

    *pOutBufferLen = 0;

//  cout<<__FUNCTION__<<" "<<__LINE__<<endl;
    while((InBufferLen/4))
    {
        memcpy(withEnc, pRunInBuffer, 4);
        resLen = this->_singleGroupDecode(withEnc, withoutEnc);
        memcpy(pRunOutBuffer, withoutEnc, resLen);
    //  cout<<__FUNCTION__<<" "<<__LINE__<<endl;
        if((resLen<3) && (InBufferLen>4))
        {
            cout<<__FUNCTION__<<" wrong input string for decode!"<<endl;
            return E_BASH64_ERROR_USUAL;
        }
        else if(resLen == 0)
        {
            cout<<__FUNCTION__<<" error base64 string!"<<endl;
            return E_BASH64_ERROR_USUAL;
        }

        InBufferLen     = InBufferLen-4;
        pRunInBuffer    = pRunInBuffer+4;
        pRunOutBuffer   = pRunOutBuffer+resLen;
        *pOutBufferLen  = *pOutBufferLen+resLen;
    }

    return E_BASH64_OK;
}

This is the specific implementation of base64 encoding and decoding;

base64_main.cpp

#include <iostream>
#include <stdio.h>
#include <string.h>
#include "base64_operation.h"

using std::cin;
using std::cout;
using std::endl;
using std::hex;

#define TEST_FILEIN "./test_fileIn.bin"
#define TEST_FILEOUT "./test_fileOut.bin"
#define TEST_DECOUT "./test_dec.bin"

int main(int argc, char* argv[])
{
    FILE* pInFile = NULL;
    FILE* pOutFile = NULL;

    char *pOutBuffer = new char[100];
    unsigned int outBufferLen = 0;

    BASE64_RESULT eRet = E_BASH64_ERROR_USUAL;

    Base64_Operation* pBase64 = Base64_Operation::base64_GetInstance();
    if(pBase64 == NULL)
    {
        cout<<__FUNCTION__<<"Get base64 instance failed!"<<endl;

        goto DESTROY;
    }

    //memset(pOutBuffer, '\0', 100);
    eRet = pBase64->base64_hex_encode((char*)"s13sdfasdf*we", 13, pOutBuffer, &outBufferLen);
    if(eRet == E_BASH64_OK)
    {
        cout<<"outBufferLen is: "<<outBufferLen<<endl;
        cout<<pOutBuffer<<endl;
    //  delete []pOutBuffer;
    //  goto DESTROY;
    }

    memset(pOutBuffer, '\0', 100);
    eRet = pBase64->base64_hex_decode((char*)"czEzc2RmYXNkZip3ZQ==",0x14,  pOutBuffer, &outBufferLen);
    if(eRet == E_BASH64_OK)
    {
        cout<<"outBufferLen is: "<<outBufferLen<<endl;
        cout<<pOutBuffer<<endl;

    //  goto DESTROY;
    }

    if((pInFile=fopen((const char*)TEST_FILEIN, "rb")) == NULL)
    {
        cout<<"source file open failed!"<<endl;
        return -1;
    }
    if((pOutFile=fopen((const char*)TEST_FILEOUT, "wb+")) == NULL)
    {
        cout<<"output file open failed!"<<endl;
        return -1;
    }

    eRet = pBase64->base64_file_encode(pInFile, pOutFile);
    if(eRet == E_BASH64_OK)
    {
        cout<<"file encode successfully!"<<endl;
        fclose(pInFile);
        fclose(pOutFile);
        pInFile = NULL;
        pOutFile = NULL;
    }

    if((pInFile=fopen((const char*)TEST_FILEOUT, "rb")) == NULL)
    {
        cout<<"source file open failed!"<<endl;
        return -1;
    }
    if((pOutFile=fopen((const char*)TEST_DECOUT, "wb+")) == NULL)
    {
        cout<<"output file open failed!"<<endl;
        return -1;
    }
    eRet = pBase64->base64_file_decode(pInFile, pOutFile);
    if(eRet == E_BASH64_OK)
    {
        cout<<"file dec successfully!"<<endl;
        fclose(pInFile);
        fclose(pOutFile);
        pInFile = NULL;
        pOutFile = NULL;
    }



DESTROY:
    if(pBase64 != NULL)
    {
        delete pBase64;
        pBase64 = NULL;
    }
    return 0;
}

This is the corresponding test function, which can be compiled and tested in a g++ environment in linux:
g++ base64_main.cpp base64_operaion.cpp -o base64
and then run:
./base64
Under normal circumstances, the following printout will be output:

Create a base64 object
outBufferLen is: 20
czEzc2RmYXNkZip3ZQ==
outBufferLen is: 13
s13sdfasdf*we
file encode successfully!
file dec successfully!
Destroy a base64 object

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325629050&siteId=291194637