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