1 #ifndef BOOST_NETWORK_UTILS_BASE64_STATEFUL_BUFFER_HPP
2 #define BOOST_NETWORK_UTILS_BASE64_STATEFUL_BUFFER_HPP
3
4 #include <boost/archive/iterators/base64_from_binary.hpp>
5 #include <boost/archive/iterators/transform_width.hpp>
6 #include <boost/range/begin.hpp>
7 #include <boost/range/end.hpp>
8 #include <boost/array.hpp>
9 #include <algorithm>
10 #include <iterator>
11 #include <string>
12
13 namespace boost {
14 namespace network {
15 namespace utils {
16
17 // Uses base64_from_binary and transform_width to implement a BASE64
18 // converter working on an iterator range. Because the transform_width
19 // encodes immediately every input byte, while the BASE64 encoding
20 // processes
21 // the input by byte-triplets, if the input sequence does not end at the
22 // three-byte boundary, the rest is remembered in an encoding state to
23 // be able to continue with the next chunk. It uses an internal buffer
24 // of 4095 input octets to be able to read the input by octet-triplets.
25 //
26 // Summarized interface:
27 //
28 // struct state<Value> {
29 // bool empty () const;
30 // void clear();
31 // }
32 //
33 // OutputIterator encode(InputIterator begin, InputIterator end,
34 // OutputIterator output, State & rest)
35 // OutputIterator encode_rest(OutputIterator output, State & rest)
36 // OutputIterator encode(InputRange const & input, OutputIterator output,
37 // State & rest)
38 // OutputIterator encode(char const * value, OutputIterator output,
39 // state<char> & rest)
40 // std::basic_string<Char> encode(InputRange const & value, State & rest)
41 // std::basic_string<Char> encode(char const * value, state<char> & rest)
42 //
43 // OutputIterator encode(InputIterator begin, InputIterator end,
44 // OutputIterator output)
45 // OutputIterator encode(InputRange const & input, OutputIterator output)
46 // OutputIterator encode(char const * value, OutputIterator output)
47 // std::basic_string<Char> encode(InputRange const & value)
48 // std::basic_string<Char> encode(char const * value) {
49
50 namespace base64_stateful_buffer {
51
52 // force using the ostream_iterator from boost::archive to write wide
53 // characters reliably, althoth wchar_t may not be a native character
54 // type
55 using namespace boost::archive::iterators;
56
57 template <typename Value>
58 struct state {
stateboost::network::utils::base64_stateful_buffer::state59 state() : size(0) {}
60
stateboost::network::utils::base64_stateful_buffer::state61 state(state<Value> const& source) : data(source.data), size(source.size) {}
62
emptyboost::network::utils::base64_stateful_buffer::state63 bool empty() const { return size == 0; }
64
clearboost::network::utils::base64_stateful_buffer::state65 void clear() { size = 0; }
66
67 private:
68 typedef boost::array<Value, 3> data_type;
69 typedef typename data_type::const_iterator const_iterator_type;
70
71 template <typename InputIterator>
fillboost::network::utils::base64_stateful_buffer::state72 void fill(InputIterator begin, InputIterator end) {
73 // make sure that there is always zero padding for the incomplete
74 // triplet; the encode will read three bytes from the vector
75 data.fill(0);
76 size = std::copy(begin, end, data.begin()) - data.begin();
77 }
78
79 template <typename OutputIterator>
writeboost::network::utils::base64_stateful_buffer::state80 OutputIterator write(OutputIterator output) {
81 return std::copy(data.begin(), data.begin() + size, output);
82 }
83
beginboost::network::utils::base64_stateful_buffer::state84 const_iterator_type begin() const { return data.begin(); }
85
endboost::network::utils::base64_stateful_buffer::state86 const_iterator_type end() const { return data.begin() + size; }
87
88 data_type data;
89 std::size_t size;
90
91 template <typename InputIterator, typename OutputIterator, typename State>
92 friend OutputIterator encode(InputIterator begin, InputIterator end,
93 OutputIterator output, State& rest);
94 template <typename State, typename OutputIterator>
95 friend OutputIterator encode_rest(OutputIterator output, State& rest);
96 };
97
98 template <typename InputIterator, typename OutputIterator, typename State>
99 OutputIterator encode(InputIterator begin, InputIterator end,
100 OutputIterator output, State& rest) {
101 typedef typename iterator_value<InputIterator>::type value_type;
102 // declare the buffer type for 1365 octet triplets; make sure that the
103 // number is divisible by three if you change it (!)
104 const std::size_t BufferSize = 4095;
105 BOOST_STATIC_ASSERT(BufferSize / 3 * 3 == BufferSize);
106 typedef boost::array<value_type, BufferSize> buffer_type;
107 // declare the encoding iterator type
108 typedef base64_from_binary<transform_width<InputIterator, 6, 8> > base64_text;
109 if (begin != end) {
110 // declare the buffer, a variable to remmeber its size and the size
111 // which can be encoded (the nearest lower size divisible by three)
112 buffer_type buffer;
113 std::size_t buffer_size = 0, encode_size = 0;
114 // if the previous state contained an incomplete octet triplet, put
115 // it to the start of the buffer to get it prepended to the input
116 if (!rest.empty()) {
117 buffer_size = rest.size;
118 rest.write(buffer.begin());
119 rest.clear();
120 }
121 // iterate over the entire input
122 while (begin != end) {
123 // fill the buffer with the input as much as possible
124 while (begin != end && buffer_size < buffer.size())
125 buffer[buffer_size++] = *begin++;
126 // if the buffer could not be filled completely, compute
127 // the size which can be encoded immediately.
128 encode_size = buffer_size / 3 * 3;
129 if (encode_size > 0) {
130 // encode the buffer part of the size divisible by three
131 base64_text base64_begin(buffer.begin()),
132 base64_end(buffer.begin() + encode_size);
133 output = std::copy(base64_begin, base64_end, output);
134 // zero the buffer size to prepare for the next iteration
135 buffer_size = 0;
136 }
137 }
138 // if the complete buffer could not be encoded, store the last
139 // incomplete octet triplet to the transiting state
140 if (buffer_size > encode_size)
141 rest.fill(buffer.begin() + encode_size, buffer.begin() + buffer_size);
142 }
143 return output;
144 }
145
146 template <typename State, typename OutputIterator>
encode_rest(OutputIterator output,State & rest)147 OutputIterator encode_rest(OutputIterator output, State& rest) {
148 typedef typename State::const_iterator_type iterator_type;
149 // declare the encoding iterator type
150 typedef base64_from_binary<transform_width<iterator_type, 6, 8> > base64_text;
151 if (!rest.empty()) {
152 // encode the incomplete octet triplet using zeros as padding
153 // (an artificial input continuation)
154 base64_text base64_begin(rest.begin()), base64_end(rest.end());
155 output = std::copy(base64_begin, base64_end, output);
156 // at least one padding '=' will be always needed - at least two
157 // bits are missing in the finally encoded 6-bit value
158 if (rest.size > 0) {
159 *output++ = '=';
160 // if the last octet was the first in the triplet (the index was,
161 // four bits are missing in the finally encoded 6-bit value;
162 // another '=' character is needed for the another two bits
163 if (rest.size == 1) *output++ = '=';
164 }
165 rest.clear();
166 }
167 return output;
168 }
169
170 template <typename InputIterator, typename OutputIterator>
encode(InputIterator begin,InputIterator end,OutputIterator output)171 OutputIterator encode(InputIterator begin, InputIterator end,
172 OutputIterator output) {
173 state<typename iterator_value<InputIterator>::type> rest;
174 output = encode(begin, end, output, rest);
175 return encode_rest(output, rest);
176 }
177
178 template <typename InputRange, typename OutputIterator>
encode(InputRange const & value,OutputIterator output)179 OutputIterator encode(InputRange const& value, OutputIterator output) {
180 return encode(boost::begin(value), boost::end(value), output);
181 }
182
183 template <typename OutputIterator>
encode(char const * value,OutputIterator output)184 OutputIterator encode(char const* value, OutputIterator output) {
185 return encode(value, value + strlen(value), output);
186 }
187
188 template <typename Char, typename InputRange>
encode(InputRange const & value)189 std::basic_string<Char> encode(InputRange const& value) {
190 std::basic_string<Char> result;
191 encode(value, std::back_inserter(result));
192 return result;
193 }
194
195 template <typename Char>
encode(char const * value)196 std::basic_string<Char> encode(char const* value) {
197 std::basic_string<Char> result;
198 encode(value, std::back_inserter(result));
199 return result;
200 }
201
202 // the function overloads for string literals encode the input without
203 // the terminating zero, which is usually expected, because the trailing
204 // zero byte is not considered a part of the string value; the overloads
205 // foran input range would wrap the string literal by Boost.Range and
206 // encodethe full memory occupated by the string literal - including the
207 // unwanted last zero byte
208
209 } // namespace base64_stateful_buffer
210
211 } // namespace utils
212 } // namespace network
213 } // namespace boost
214
215 #endif // BOOST_NETWORK_UTILS_BASE64_STATEFUL_BUFFER_HPP
216