Skip to content

Commit 153d203

Browse files
committed
Add the first version of the stream manipulators performing the BASE64 encoding
1 parent 9328c7e commit 153d203

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed
+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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

Comments
 (0)