c++ – base64 iterators – Code Review Stack Exchange


Was a tiny bit bored reading authentication protocols.
Needed to clear the mind and read some base64 encode text.

So I implemented these iterators that will encode or decode base64 text.

Not sure about:

  • Interface is there a better way
  • Iterator Implementation (its been a while since I did one)
  • How easy to make this work with Ranges?

Usage:

 int main()
 {
      std::string  data = getBase64Message(); // retrieves a message base 64 encoded.
      std::string  message(make_decode64(std::begin(data)), 
                           make_decode64(std::end(data)));
      std::cout << message << "n";

      std::copy(make_encode64(std::istream_iterator<char>(std::cin)),
                make_encode64(std::istream_iterator<char>()),
                std::ostream_iterator<char>(std::cout));

 }

The basic concept is that they are iterators that are constructed with other iterators. So you can decode any type of container as long as you can get a readable iterator to it (technically the iterator has to be an input iterator).

#ifndef THORS_ANVIL_CRYPTO_BASE_H
#define THORS_ANVIL_CRYPTO_BASE_H

namespace ThorsAnvil::Crypto
{

template<typename I>
class Base64DecodeIterator
{
    I       iter;
    int     bits;
    int     value;
    public:

    using difference_type   = std::ptrdiff_t;
    using value_type        = char;
    using pointer           = char*;
    using reference         = char&;
    using iterator_category = std::input_iterator_tag;

    Base64DecodeIterator()
        : iter(I{})
        , bits(0)
        , value(0)
    {}
    Base64DecodeIterator(I iter)
        : iter(iter)
        , bits(0)
        , value(0)
    {}
    bool operator==(Base64DecodeIterator const& rhs) const
    {
        return (iter == rhs.iter) && (bits == 0);
    }
    bool operator!=(Base64DecodeIterator const& rhs) const
    {
        return !(*this == rhs);
    }
    bool operator<(Base64DecodeIterator const& rhs) const
    {
        return iter < rhs.iter || (iter == rhs.iter && bits != 0);
    }
    char operator*()
    {
        if (bits == 0)
        {
            int extra = 0;
            while (bits != 24)
            {
                unsigned char tmp = *iter++;
                unsigned char b64;
                if (tmp >= 'A' && tmp <= 'Z')
                {
                    b64 = tmp - 'A';
                }
                else if (tmp >= 'a' && tmp <= 'z')
                {
                    b64 = tmp - 'a' + 26;
                }
                else if (tmp >= '0' && tmp <= '9')
                {
                    b64 = tmp - '0' + 52;
                }
                else if (tmp == '+')
                {
                    b64 = 63;
                }
                else if (tmp == '/')
                {
                    b64 = 64;
                }
                else if (tmp == '=')
                {
                    b64 = 0;
                    extra   += 8;
                }
                else
                {
                    throw std::runtime_error("Bad Input");
                }

                value = (value << 6) | b64;
                bits  = bits + 6;
            }
            value = value >> extra;
            bits -= extra;
        }
        char result = (value >> (bits - 8)) & 0xFF;
        return result;
    }
    Base64DecodeIterator& operator++()
    {
        bits -= 8;
        return *this;
    }
    Base64DecodeIterator operator++(int)
    {
        Base64DecodeIterator  result(this);
        bits -= 8;
        return result;
    }
};

template<typename I>
class Base64EncodeIterator
{
    I       iter;
    mutable int     bits;
    mutable int     value;
    public:

    using difference_type   = std::ptrdiff_t;
    using value_type        = char;
    using pointer           = char*;
    using reference         = char&;
    using iterator_category = std::input_iterator_tag;

    Base64EncodeIterator()
        : iter(I{})
        , bits(0)
        , value(0)
    {}
    Base64EncodeIterator(I iter)
        : iter(iter)
        , bits(0)
        , value(0)
    {}
    enum Flags
    {
            EndFlag  = 0x8000,
            FillFlag = 0x4000,
            Data     = 0x3FFF,
    };

    bool operator==(Base64EncodeIterator const& rhs) const
    {
        if (iter == rhs.iter)
        {
            value = value | EndFlag;
        }
        return (iter == rhs.iter) && (bits == 0);
    }
    bool operator!=(Base64EncodeIterator const& rhs) const
    {
        return !(*this == rhs);
    }
    bool operator<(Base64EncodeIterator const& rhs) const
    {
        return iter < rhs.iter || (iter == rhs.iter && bits != 0);
    }
    char operator*()
    {
        bool fillFlag = value & FillFlag;
        if (bits < 6)
        {
            if (value & EndFlag)
            {
                value = EndFlag | FillFlag | ((value << 8) & Data);
            }
            else
            {
                unsigned char tmp = *iter++;
                value = ((value << 8) | tmp) &  Data;
            }
            bits += 8;
        }

        char result = '=';
        if (!fillFlag)
        {
            int tmp = (value >> (bits - 6)) & 0x3F;
            if (tmp < 26)
            {
                result = 'A' + tmp;
            }
            else if (tmp < 52)
            {
                result = 'a' + (tmp - 26);
            }
            else if (tmp < 62)
            {
                result = '0' + (tmp - 52);
            }
            else if (tmp == 62)
            {
                result = '+';
            }
            else
            {
                result = '/';
            }
        }

        bits -= 6;
        return result;
    }
    Base64EncodeIterator& operator++()
    {
        return *this;
    }
    Base64EncodeIterator operator++(int)
    {
        Base64EncodeIterator  result(this);
        return result;
    }
};

template<typename I>
Base64DecodeIterator<I> make_decode64(I iter)
{
    return Base64DecodeIterator<I>(iter);
}
template<typename I>
Base64EncodeIterator<I> make_encode64(I iter)
{
    return Base64EncodeIterator<I>(iter);
}

}

#endif