|
| 1 | +#ifndef BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP |
| 2 | +#define BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP |
| 3 | + |
| 4 | +#include <boost/network/utils/base64/encode.hpp> |
| 5 | +#include <boost/archive/iterators/ostream_iterator.hpp> |
| 6 | +#include <boost/range/begin.hpp> |
| 7 | +#include <boost/range/end.hpp> |
| 8 | +#include <iterator> |
| 9 | + |
| 10 | +namespace boost { |
| 11 | +namespace network { |
| 12 | +namespace utils { |
| 13 | + |
| 14 | +// Offers an interface to the BASE64 converter from encode.hpp, which is |
| 15 | +// based on stream manipulators to be friendly to the usage with output |
| 16 | +// streams combining heterogenous output by using the output operators. |
| 17 | +// The encoding state is serialized to long and maintained in te extensible |
| 18 | +// internal array of the output stream. |
| 19 | +// |
| 20 | +// Summarized interface - ostream manipulators and one function: |
| 21 | +// |
| 22 | +// encode(InputIterator begin, InputIterator end) |
| 23 | +// encode(InputRange const & value) |
| 24 | +// encode(char const * value) |
| 25 | +// encode_rest |
| 26 | +// clear_state |
| 27 | +// bool empty_state(std::basic_ostream<Char> & output) |
| 28 | + |
| 29 | +namespace base64 { |
| 30 | +namespace io { |
| 31 | + |
| 32 | +// force using the ostream_iterator from boost::archive to write wide |
| 33 | +// characters reliably, althoth wchar_t may not be a native character type |
| 34 | +using namespace boost::archive::iterators; |
| 35 | + |
| 36 | +namespace detail { |
| 37 | + |
| 38 | +// Enables transferring of the input sequence for the BASE64 encoding into |
| 39 | +// the ostream operator defined for it, which performs the operation. It |
| 40 | +// is to be used from ostream manipulators. |
| 41 | +// |
| 42 | +// std::basic_ostream<Char> & output = ...; |
| 43 | +// output << input_wrapper<InputIterator>(value.begin(), value.end()); |
| 44 | +template <typename InputIterator> |
| 45 | +struct input_wrapper { |
| 46 | + input_wrapper(InputIterator begin, InputIterator end) |
| 47 | + : begin(begin), end(end) {} |
| 48 | + |
| 49 | +private: |
| 50 | + InputIterator begin, end; |
| 51 | + |
| 52 | + // only encoding of an input sequence needs access to it |
| 53 | + template < |
| 54 | + typename Char, |
| 55 | + typename InputIterator2 |
| 56 | + > |
| 57 | + friend std::basic_ostream<Char> & |
| 58 | + operator <<(std::basic_ostream<Char> & output, |
| 59 | + input_wrapper<InputIterator2> input_wrapper); |
| 60 | +}; |
| 61 | + |
| 62 | +// The output stream state should be used only in a single scope around |
| 63 | +// encoding operations. Constructor performs initialization from the |
| 64 | +// output stream internal extensible array and destructor updates it |
| 65 | +// according to the encoding result. It inherits from the base64::state |
| 66 | +// to gain access to its protected members and allow easy value passing |
| 67 | +// to base64::encode. |
| 68 | +// |
| 69 | +// |
| 70 | +// std::basic_ostream<Char> & output = ...; |
| 71 | +// { |
| 72 | +// state<Char, Value> rest(output); |
| 73 | +// base64::encode(..., ostream_iterator<Char>(output), rest); |
| 74 | +// } |
| 75 | +template <typename Char, typename Value> |
| 76 | +struct state : public boost::network::utils::base64::state<Value> { |
| 77 | + typedef boost::network::utils::base64::state<Value> super_t; |
| 78 | + |
| 79 | + // initialize the (inherited) contents of the base64::state<> from the |
| 80 | + // output stream internal extensible array |
| 81 | + state(std::basic_ostream<Char> & output) : super_t(), output(output) { |
| 82 | + const unsigned triplet_index_size = sizeof(super_t::triplet_index) * 8; |
| 83 | + unsigned long data = static_cast<unsigned long>(storage()); |
| 84 | + // mask the long value with the bit-size of the triplet_index member |
| 85 | + super_t::triplet_index = data & ((1 << triplet_index_size) - 1); |
| 86 | + // shift the long value right to remove the the triplet_index value; |
| 87 | + // masking is not necessary because the last_encoded_value it is the |
| 88 | + // last record stored in the data value |
| 89 | + super_t::last_encoded_value = data >> triplet_index_size; |
| 90 | + } |
| 91 | + |
| 92 | + // update the value in the output stream internal extensible array by |
| 93 | + // the last (inherited) contents of the base64::state<> |
| 94 | + ~state() { |
| 95 | + const unsigned triplet_index_size = sizeof(super_t::triplet_index) * 8; |
| 96 | + // store the last_encoded_value in the data value first |
| 97 | + unsigned long data = |
| 98 | + static_cast<unsigned long>(super_t::last_encoded_value); |
| 99 | + // shift the long data value left to make place for storing the |
| 100 | + // full triplet_index value there |
| 101 | + data <<= triplet_index_size; |
| 102 | + data |= static_cast<unsigned long>(super_t::triplet_index); |
| 103 | + storage() = static_cast<long>(data); |
| 104 | + } |
| 105 | + |
| 106 | +private: |
| 107 | + // all data of the base64::state<> must be serializable into a long |
| 108 | + // value allocated in the output stream internal extensible array |
| 109 | + BOOST_STATIC_ASSERT(sizeof(super_t) <= sizeof(long)); |
| 110 | + |
| 111 | + // allow only the construction with an output stream (strict RAII) |
| 112 | + state(); |
| 113 | + state(state<Char, Value> const & source); |
| 114 | + |
| 115 | + std::basic_ostream<Char> & output; |
| 116 | + |
| 117 | + long & storage() { |
| 118 | + static int index = std::ios_base::xalloc(); |
| 119 | + return output.iword(index); |
| 120 | + } |
| 121 | +}; |
| 122 | + |
| 123 | +// Output operator implementing the BASE64 encoding for an input sequence |
| 124 | +// which was wrapped by the ostream manipulator; the state must be preserved |
| 125 | +// because multiple sequences can be sent in the ouptut by this operator. |
| 126 | +template < |
| 127 | + typename Char, |
| 128 | + typename InputIterator |
| 129 | + > |
| 130 | +std::basic_ostream<Char> & |
| 131 | +operator <<(std::basic_ostream<Char> & output, |
| 132 | + input_wrapper<InputIterator> input) { |
| 133 | + typedef typename iterator_value<InputIterator>::type value_type; |
| 134 | + state<Char, value_type> rest(output); |
| 135 | + base64::encode(input.begin, input.end, ostream_iterator<Char>(output), rest); |
| 136 | + return output; |
| 137 | +} |
| 138 | + |
| 139 | +} // namespace detail |
| 140 | + |
| 141 | +// Encoding ostream manipulator for sequences specified by the pair of begin |
| 142 | +// and end iterators. |
| 143 | +// |
| 144 | +// std::vector<unsigned char> buffer = ...; |
| 145 | +// std::basic_ostream<Char> & output = ...; |
| 146 | +// output << base64::io::encode(buffer.begin(), buffer.end()) << ... << |
| 147 | +// base64::io::encode_rest<unsigned char>; |
| 148 | +template <typename InputIterator> |
| 149 | +detail::input_wrapper<InputIterator> encode(InputIterator begin, |
| 150 | + InputIterator end) { |
| 151 | + return detail::input_wrapper<InputIterator>(begin, end); |
| 152 | +} |
| 153 | + |
| 154 | +// Encoding ostream manipulator processing whole sequences which either |
| 155 | +// support begin() and end() methods returning boundaries of the sequence |
| 156 | +// or the boundaries can be computed by the Boost::Range. |
| 157 | +// |
| 158 | +// Warning: Buffers identified by C-pointers are processed including their |
| 159 | +// termination character, if they have any. This is unexpected at least |
| 160 | +// for the storing literals, which have a specialization here to avoid it. |
| 161 | +// |
| 162 | +// std::vector<unsigned char> buffer = ...; |
| 163 | +// std::basic_ostream<Char> & output = ...; |
| 164 | +// output << base64::io::encode(buffer) << ... << |
| 165 | +// base64::io::encode_rest<unsigned char>; |
| 166 | +template <typename InputRange> |
| 167 | +detail::input_wrapper<typename boost::range_const_iterator<InputRange>::type> |
| 168 | +encode(InputRange const & value) { |
| 169 | + typedef typename boost::range_const_iterator<InputRange>::type InputIterator; |
| 170 | + return detail::input_wrapper<InputIterator>(boost::begin(value), |
| 171 | + boost::end(value)); |
| 172 | +} |
| 173 | + |
| 174 | +// Encoding ostream manipulator processing string literals; the usual |
| 175 | +// expectation from their encoding is processing only the string content |
| 176 | +// without the terminating zero character. |
| 177 | +// |
| 178 | +// std::basic_ostream<Char> & output = ...; |
| 179 | +// output << base64::io::encode("ab") << ... << base64::io::encode_rest<char>; |
| 180 | +inline detail::input_wrapper<char const *> encode(char const * value) { |
| 181 | + return detail::input_wrapper<char const *>(value, value + strlen(value)); |
| 182 | +} |
| 183 | + |
| 184 | +// Encoding ostream manipulator which finishes encoding of the previously |
| 185 | +// processed chunks. If their total byte-length was divisible by three, |
| 186 | +// nothing is needed, if not, the last quantum will be encoded as if padded |
| 187 | +// with zeroes, which will be indicated by appending '=' characters to the |
| 188 | +// output. This manipulator must be always used at the end of encoding, |
| 189 | +// after previous usages of the encode manipulator. |
| 190 | +// |
| 191 | +// std::basic_ostream<Char> & output = ...; |
| 192 | +// output << base64::io::encode("ab") << ... << base64::io::encode_rest<char>; |
| 193 | +template <typename Value, typename Char> |
| 194 | +std::basic_ostream<Char> & encode_rest(std::basic_ostream<Char> & output) { |
| 195 | + detail::state<Char, Value> rest(output); |
| 196 | + base64::encode_rest(ostream_iterator<Char>(output), rest); |
| 197 | + return output; |
| 198 | +} |
| 199 | + |
| 200 | +// Clears the encoding state in the internal array of the output stream. |
| 201 | +// Use it to re-use a state object in an unknown state only; Encoding of |
| 202 | +// the last chunk must be followed by encode_rest otherwise the end of the |
| 203 | +// input sequence may be missing in the encoded output. The encode_rest |
| 204 | +// ensures that the rest of the input sequence will be encoded corectly and |
| 205 | +// the '=' padding applied as necessary. The encode rest clears the state |
| 206 | +// when finished. |
| 207 | +// |
| 208 | +// std::basic_ostream<Char> & output = ...; |
| 209 | +// output << base64::io::encode("ab") << ...; |
| 210 | +// output << clear_state<char>; |
| 211 | +template <typename Value, typename Char> |
| 212 | +std::basic_ostream<Char> & clear_state(std::basic_ostream<Char> & output) { |
| 213 | + detail::state<Char, Value> rest(output); |
| 214 | + rest.clear(); |
| 215 | + return output; |
| 216 | +} |
| 217 | + |
| 218 | +// Checks if the encoding state in the internal array of the output stream |
| 219 | +// is empty. |
| 220 | +// |
| 221 | +// std::basic_ostream<Char> & output = ...; |
| 222 | +// output << base64::io::encode("ab") << ...; |
| 223 | +// bool is_complete = base64::io::empty_state<char>(output); |
| 224 | +template <typename Value, typename Char> |
| 225 | +bool empty_state(std::basic_ostream<Char> & output) { |
| 226 | + detail::state<Char, Value> rest(output); |
| 227 | + return rest.empty(); |
| 228 | +} |
| 229 | + |
| 230 | +} // namespace io |
| 231 | +} // namespace base64 |
| 232 | + |
| 233 | +} // namespace utils |
| 234 | +} // namespace network |
| 235 | +} // namespace boost |
| 236 | + |
| 237 | +#endif // BOOST_NETWORK_UTILS_BASE64_ENCODE_IO_HPP |
0 commit comments