Skip to content

Initial version of base64 encoding #300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 15, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ if (Boost_FOUND)
enable_testing()
add_subdirectory(libs/network/src)
add_subdirectory(libs/network/test)
add_subdirectory(libs/network/experiment)
if (NOT MSVC)
add_subdirectory(libs/mime/test)
endif(NOT MSVC)
Expand Down
237 changes: 237 additions & 0 deletions boost/network/utils/base64/encode-io.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#ifndef BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP
#define BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP

#include <boost/network/utils/base64/encode.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <iterator>

namespace boost {
namespace network {
namespace utils {

// Offers an interface to the BASE64 converter from encode.hpp, which is
// based on stream manipulators to be friendly to the usage with output
// streams combining heterogenous output by using the output operators.
// The encoding state is serialized to long and maintained in te extensible
// internal array of the output stream.
//
// Summarized interface - ostream manipulators and one function:
//
// encode(InputIterator begin, InputIterator end)
// encode(InputRange const & value)
// encode(char const * value)
// encode_rest
// clear_state
// bool empty_state(std::basic_ostream<Char> & output)

namespace base64 {
namespace io {

// force using the ostream_iterator from boost::archive to write wide
// characters reliably, althoth wchar_t may not be a native character type
using namespace boost::archive::iterators;

namespace detail {

// Enables transferring of the input sequence for the BASE64 encoding into
// the ostream operator defined for it, which performs the operation. It
// is to be used from ostream manipulators.
//
// std::basic_ostream<Char> & output = ...;
// output << input_wrapper<InputIterator>(value.begin(), value.end());
template <typename InputIterator>
struct input_wrapper {
input_wrapper(InputIterator begin, InputIterator end)
: begin(begin), end(end) {}

private:
InputIterator begin, end;

// only encoding of an input sequence needs access to it
template <
typename Char,
typename InputIterator2
>
friend std::basic_ostream<Char> &
operator <<(std::basic_ostream<Char> & output,
input_wrapper<InputIterator2> input_wrapper);
};

// The output stream state should be used only in a single scope around
// encoding operations. Constructor performs initialization from the
// output stream internal extensible array and destructor updates it
// according to the encoding result. It inherits from the base64::state
// to gain access to its protected members and allow easy value passing
// to base64::encode.
//
//
// std::basic_ostream<Char> & output = ...;
// {
// state<Char, Value> rest(output);
// base64::encode(..., ostream_iterator<Char>(output), rest);
// }
template <typename Char, typename Value>
struct state : public boost::network::utils::base64::state<Value> {
typedef boost::network::utils::base64::state<Value> super_t;

// initialize the (inherited) contents of the base64::state<> from the
// output stream internal extensible array
state(std::basic_ostream<Char> & output) : super_t(), output(output) {
const unsigned triplet_index_size = sizeof(super_t::triplet_index) * 8;
unsigned long data = static_cast<unsigned long>(storage());
// mask the long value with the bit-size of the triplet_index member
super_t::triplet_index = data & ((1 << triplet_index_size) - 1);
// shift the long value right to remove the the triplet_index value;
// masking is not necessary because the last_encoded_value it is the
// last record stored in the data value
super_t::last_encoded_value = data >> triplet_index_size;
}

// update the value in the output stream internal extensible array by
// the last (inherited) contents of the base64::state<>
~state() {
const unsigned triplet_index_size = sizeof(super_t::triplet_index) * 8;
// store the last_encoded_value in the data value first
unsigned long data =
static_cast<unsigned long>(super_t::last_encoded_value);
// shift the long data value left to make place for storing the
// full triplet_index value there
data <<= triplet_index_size;
data |= static_cast<unsigned long>(super_t::triplet_index);
storage() = static_cast<long>(data);
}

private:
// all data of the base64::state<> must be serializable into a long
// value allocated in the output stream internal extensible array
BOOST_STATIC_ASSERT(sizeof(super_t) <= sizeof(long));

// allow only the construction with an output stream (strict RAII)
state();
state(state<Char, Value> const &);

std::basic_ostream<Char> & output;

long & storage() {
static int index = std::ios_base::xalloc();
return output.iword(index);
}
};

// Output operator implementing the BASE64 encoding for an input sequence
// which was wrapped by the ostream manipulator; the state must be preserved
// because multiple sequences can be sent in the ouptut by this operator.
template <
typename Char,
typename InputIterator
>
std::basic_ostream<Char> &
operator <<(std::basic_ostream<Char> & output,
input_wrapper<InputIterator> input) {
typedef typename iterator_value<InputIterator>::type value_type;
state<Char, value_type> rest(output);
base64::encode(input.begin, input.end, ostream_iterator<Char>(output), rest);
return output;
}

} // namespace detail

// Encoding ostream manipulator for sequences specified by the pair of begin
// and end iterators.
//
// std::vector<unsigned char> buffer = ...;
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode(buffer.begin(), buffer.end()) << ... <<
// base64::io::encode_rest<unsigned char>;
template <typename InputIterator>
detail::input_wrapper<InputIterator> encode(InputIterator begin,
InputIterator end) {
return detail::input_wrapper<InputIterator>(begin, end);
}

// Encoding ostream manipulator processing whole sequences which either
// support begin() and end() methods returning boundaries of the sequence
// or the boundaries can be computed by the Boost::Range.
//
// Warning: Buffers identified by C-pointers are processed including their
// termination character, if they have any. This is unexpected at least
// for the storing literals, which have a specialization here to avoid it.
//
// std::vector<unsigned char> buffer = ...;
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode(buffer) << ... <<
// base64::io::encode_rest<unsigned char>;
template <typename InputRange>
detail::input_wrapper<typename boost::range_const_iterator<InputRange>::type>
encode(InputRange const & value) {
typedef typename boost::range_const_iterator<InputRange>::type InputIterator;
return detail::input_wrapper<InputIterator>(boost::begin(value),
boost::end(value));
}

// Encoding ostream manipulator processing string literals; the usual
// expectation from their encoding is processing only the string content
// without the terminating zero character.
//
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode("ab") << ... << base64::io::encode_rest<char>;
inline detail::input_wrapper<char const *> encode(char const * value) {
return detail::input_wrapper<char const *>(value, value + strlen(value));
}

// Encoding ostream manipulator which finishes encoding of the previously
// processed chunks. If their total byte-length was divisible by three,
// nothing is needed, if not, the last quantum will be encoded as if padded
// with zeroes, which will be indicated by appending '=' characters to the
// output. This manipulator must be always used at the end of encoding,
// after previous usages of the encode manipulator.
//
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode("ab") << ... << base64::io::encode_rest<char>;
template <typename Value, typename Char>
std::basic_ostream<Char> & encode_rest(std::basic_ostream<Char> & output) {
detail::state<Char, Value> rest(output);
base64::encode_rest(ostream_iterator<Char>(output), rest);
return output;
}

// Clears the encoding state in the internal array of the output stream.
// Use it to re-use a state object in an unknown state only; Encoding of
// the last chunk must be followed by encode_rest otherwise the end of the
// input sequence may be missing in the encoded output. The encode_rest
// ensures that the rest of the input sequence will be encoded corectly and
// the '=' padding applied as necessary. The encode rest clears the state
// when finished.
//
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode("ab") << ...;
// output << clear_state<char>;
template <typename Value, typename Char>
std::basic_ostream<Char> & clear_state(std::basic_ostream<Char> & output) {
detail::state<Char, Value> rest(output);
rest.clear();
return output;
}

// Checks if the encoding state in the internal array of the output stream
// is empty.
//
// std::basic_ostream<Char> & output = ...;
// output << base64::io::encode("ab") << ...;
// bool is_complete = base64::io::empty_state<char>(output);
template <typename Value, typename Char>
bool empty_state(std::basic_ostream<Char> & output) {
detail::state<Char, Value> rest(output);
return rest.empty();
}

} // namespace io
} // namespace base64

} // namespace utils
} // namespace network
} // namespace boost

#endif // BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP
Loading