1 апр. 2009 г.

Base64 Encoder

Велосипед отечественного производства для кодирования бинарных данных в текст с кодировкой Base64 и обратно.

Файл check_helpers.hpp
//------------------------------------------------------------------------
#ifndef __CHERCKHELPERS_HEADER_INCLUDED__
#define __CHERCKHELPERS_HEADER_INCLUDED__
//------------------------------------------------------------------------
#include <boost/type_traits.hpp>
//------------------------------------------------------------------------
template<class IterTypeT>
struct IsRandomAccesIterator
{
    static const bool value = false;
};
template<>
struct IsRandomAccesIterator<std::random_access_iterator_tag>
{
    static const bool value = true;
};
//------------------------------------------------------------------------
#define CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t) \
    BOOST_STATIC_ASSERT ( \
    boost::is_fundamental<typename stl_container_t::value_type>::value ); \
    BOOST_STATIC_ASSERT ( \
    sizeof(typename stl_container_t::value_type) == static_cast<std::size_t>(1) );
//------------------------------------------------------------------------
#define CHARTYPE_SOURCE_ITERATOR_CHECK(iterator_t) \
    BOOST_STATIC_ASSERT ( \
        boost::is_fundamental<typename std::iterator_traits<iterator_t>::value_type>::value ); \
    BOOST_STATIC_ASSERT ( \
        sizeof(typename std::iterator_traits<iterator_t>::value_type) == static_cast<std::size_t>(1) );
//------------------------------------------------------------------------
#define RANDOM_ACCESS_ITERATOR_CHECK(iterator_t) \
    BOOST_STATIC_ASSERT ( \
    IsRandomAccesIterator< \
        typename std::iterator_traits<iterator_t>::iterator_category>::value );
//------------------------------------------------------------------------
//------------------------------------------------------------------------
#endif // __CHERCKHELPERS_HEADER_INCLUDED__
//------------------------------------------------------------------------

Файл base64_conv.h
//------------------------------------------------------------------------
#ifndef __BASE64_CONVERTER_HEADER_INCLUDED__
#define __BASE64_CONVERTER_HEADER_INCLUDED__
//------------------------------------------------------------------------
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits.hpp>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include "check_helpers.hpp"
//------------------------------------------------------------------------
class base64_conv : private boost::noncopyable
{
public:
    typedef std::vector<char>           buffer_t;
    typedef boost::shared_ptr<buffer_t> p_buffer_t;

public:
    base64_conv();
    ~base64_conv();

    template<typename stl_container_t>
    void to_base64 ( const stl_container_t & sourse_data )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t);

        std::vector<typename stl_container_t::value_type> tmp_buffer (
            sourse_data.begin(), sourse_data.end() );
        to_base64(tmp_buffer);
    }
    
    template<typename char_t, typename allocator_t>
    void to_base64 (
        const std::vector<char_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        to_base64_for_vector_or_string ( sourse_data.begin(), sourse_data.end() );
    }

    template<typename char_t, typename char_traits_t, typename allocator_t>
    void to_base64(
        const std::basic_string<char_t, char_traits_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        to_base64_for_vector_or_string ( sourse_data.begin(), sourse_data.end() );
    }

    template<typename stl_container_t>
    void from_base64( const stl_container_t & sourse_data )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t);

        std::vector<typename stl_container_t::value_type> tmp_buffer (
            sourse_data.begin(), sourse_data.end() );
        from_base64(tmp_buffer);
    }

    template<typename char_t, typename allocator_t>
    void from_base64 (
        const std::vector<char_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        from_base64_for_vector_or_string (
            sourse_data.begin(), sourse_data.end() );
    }

    template<typename char_t, typename char_traits_t, typename allocator_t>
    void from_base64 (
        const std::basic_string<char_t, char_traits_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        from_base64_for_vector_or_string (
            sourse_data.begin(), sourse_data.end() );
    }

    p_buffer_t get_result();
    void get_result ( std::string & out_str );
    void get_result ( std::vector<char> & out_buffer );

private:
    template<typename in_rait_t>
    void to_base64_for_vector_or_string (
        const in_rait_t it_begin, const in_rait_t it_end )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_ITERATOR_CHECK(in_rait_t);

        // This code can not be compiled, because iterator_category of in_rait_t
        // is not equal std::random_access_iterator_tag
        RANDOM_ACCESS_ITERATOR_CHECK(in_rait_t);

        std::size_t data_length = std::distance(it_begin, it_end);
        if ( data_length <= 0 )
        {
            p_buffer->clear();
            return;
        }

        const char * p_data_begin = reinterpret_cast<const char*>( & * it_begin );
        do_encode_to_base64 ( p_data_begin, data_length );
    }

    template<typename in_rait_t>
    void from_base64_for_vector_or_string (
        const in_rait_t it_begin, const in_rait_t it_end )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_ITERATOR_CHECK(in_rait_t);

        // This code can not be compiled, because iterator_category of in_rait_t
        // is not equal std::random_access_iterator_tag
        RANDOM_ACCESS_ITERATOR_CHECK(in_rait_t);

        std::size_t data_length = std::distance(it_begin, it_end);
        if ( data_length <= 0 )
        {
            p_buffer->clear();
            return;
        }

        const char * p_data_begin = reinterpret_cast<const char*>( & * it_begin );
        do_decode_from_base64 ( p_data_begin, data_length );
    }

    void do_encode_to_base64 (
        const char * sourse_data,
        std::size_t sourse_data_size );

    void do_decode_from_base64 (
        const char * sourse_data,
        std::size_t sourse_data_size );

private:
    enum { MAX_LINE_LENGTH = 72 };
    p_buffer_t p_buffer;
};
//------------------------------------------------------------------------
#endif // __BASE64_CONVERTER_HEADER_INCLUDED__
//------------------------------------------------------------------------

Файл base64_conv.cpp
//------------------------------------------------------------------------
#include "stdafx.h"
#include "base64_conv.h"
//------------------------------------------------------------------------
namespace {

// Translation Table as described in RFC1113
//------------------------------------------------------------------------
const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Translation Table to decode (created by author)
//------------------------------------------------------------------------
const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";

// encode 3 8-bit binary bytes as 4 '6-bit' characters
//------------------------------------------------------------------------
void encodeblock ( unsigned char in[3], unsigned char out[4], int len )
{
    out[0] = cb64[ in[0] >> 2 ];
    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}

// decode 4 '6-bit' characters into 3 8-bit binary bytes
//------------------------------------------------------------------------
void decodeblock( unsigned char in[4], unsigned char out[3] )
{   
    out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
    out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
    out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
}

// base64 encode a sequence adding padding and line breaks as per spec.
//------------------------------------------------------------------------
void encode (
    const char * in_sequence,
    std::size_t in_length,
    std::vector<char> & out_sequence,
    int line_size )
{
    unsigned char in[3], out[4];
    int i, len, blocks_out = 0;

    out_sequence.clear();
    out_sequence.reserve(in_length * 4/3);

    const char * it_begin   = in_sequence;
    const char * it_end     = in_sequence + in_length;

    while ( it_begin < it_end )
    {
        len = 0;
        for ( i = 0; i < 3; i++ )
        {
            if ( it_begin < it_end )
            {
                in[i] = static_cast<unsigned char>(*it_begin++);
                len++;
            }
            else
            {
                in[i] = 0;
            }
        }
        if ( len )
        {
            encodeblock( in, out, len );
            for( i = 0; i < 4; i++ )
            {
                out_sequence.push_back(out[i]);
            }
            blocks_out++;
        }
        if ( blocks_out >= (line_size/4) || ( it_begin == it_end ) )
        {
            if ( blocks_out )
            {
                out_sequence.push_back('\r');
                out_sequence.push_back('\n');
            }
            blocks_out = 0;
        }
    }
}

// decode a base64 encoded sequence discarding padding, line breaks and noise
//------------------------------------------------------------------------
void decode (
    const char * in_sequence,
    std::size_t in_length,
    std::vector<char> & out_sequence )
{
    unsigned char in[4], out[3], v;
    int i, len;

    out_sequence.clear();
    out_sequence.reserve(in_length * 3/4);

    const char * it_begin   = in_sequence;
    const char * it_end     = in_sequence + in_length;

    while( it_begin < it_end )
    {
        for( len = 0, i = 0; i < 4 && (it_begin < it_end); i++ )
        {
            v = 0;
            for ( ; (it_begin < it_end) && v == 0 ; ++it_begin )
            {
                v = static_cast<unsigned char>(*it_begin);
                v = static_cast<unsigned char>((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
                if( v )
                {
                    v = static_cast<unsigned char>((v == '$') ? 0 : v - 61);
                }
            }
            if ( v )
            {
                len++;
                in[i] = static_cast<unsigned char>(v - 1);
            }
            else
            {
                in[i] = 0;
            }
        }
        if( len )
        {
            decodeblock( in, out );
            for( i = 0; i < len - 1; i++ )
            {
                out_sequence.push_back(out[i]);
            }
        }
    }
}

}
//------------------------------------------------------------------------
base64_conv::base64_conv()
    : p_buffer ( new std::vector<char> )
{
};
//------------------------------------------------------------------------
base64_conv::~base64_conv()
{
};
//------------------------------------------------------------------------
void base64_conv::do_encode_to_base64 (
    const char * sourse_data, std::size_t sourse_data_size )
{
    encode ( sourse_data, sourse_data_size, *p_buffer, MAX_LINE_LENGTH );
}
//------------------------------------------------------------------------
void base64_conv::do_decode_from_base64 (
    const char * sourse_data, std::size_t sourse_data_size )
{
    decode ( sourse_data, sourse_data_size, *p_buffer );
}
//------------------------------------------------------------------------
base64_conv::p_buffer_t base64_conv::get_result()
{
    return p_buffer;
}
//------------------------------------------------------------------------
void base64_conv::get_result (
    std::string & out_str )
{
    std::string ret_val;
    ret_val.reserve(p_buffer->size());
    std::copy (
        p_buffer->begin(),
        p_buffer->end(),
        std::back_inserter(ret_val) );
    std::swap(out_str, ret_val);
}
//------------------------------------------------------------------------
void base64_conv::get_result (
    std::vector<char> & out_buffer )
{
    std::swap(out_buffer, *p_buffer);
    p_buffer->clear();
}
//------------------------------------------------------------------------

Комментариев нет:

Отправить комментарий