forked from glynos/cpp-netlib
-
Notifications
You must be signed in to change notification settings - Fork 422
Expand file tree
/
Copy pathencode.hpp
More file actions
418 lines (391 loc) · 17 KB
/
Copy pathencode.hpp
File metadata and controls
418 lines (391 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
#ifndef BOOST_NETWORK_UTILS_BASE64_ENCODE_HPP
#define BOOST_NETWORK_UTILS_BASE64_ENCODE_HPP
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <algorithm>
#include <iterator>
#include <string>
namespace boost {
namespace network {
namespace utils {
// Implements a BASE64 converter working on an iterator range.
// If the input sequence does not end at the three-byte boundary, the last
// encoded value part is remembered in an encoding state to be able to
// continue with the next chunk; the BASE64 encoding processes the input
// by byte-triplets.
//
// Summarized interface:
//
// struct state<Value> {
// bool empty () const;
// void clear();
// }
//
// OutputIterator encode(InputIterator begin, InputIterator end,
// OutputIterator output, State & rest)
// OutputIterator encode_rest(OutputIterator output, State & rest)
// OutputIterator encode(InputRange const & input, OutputIterator output,
// State & rest)
// OutputIterator encode(char const * value, OutputIterator output,
// state<char> & rest)
// std::basic_string<Char> encode(InputRange const & value, State & rest)
// std::basic_string<Char> encode(char const * value, state<char> & rest)
//
// OutputIterator encode(InputIterator begin, InputIterator end,
// OutputIterator output)
// OutputIterator encode(InputRange const & input, OutputIterator output)
// OutputIterator encode(char const * value, OutputIterator output)
// std::basic_string<Char> encode(InputRange const & value)
// std::basic_string<Char> encode(char const * value) {
//
// See also http://libb64.sourceforge.net, which served as inspiration.
// See also http://tools.ietf.org/html/rfc4648 for the specification.
namespace base64 {
namespace detail {
// Picks a character from the output alphabet for another 6-bit value
// from the input sequence to encode.
template <typename Value>
char encode_value(Value value) {
static char const encoding[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
"+/";
return encoding[static_cast<unsigned int>(value)];
}
} // namespace detail
// Stores the state after processing the last chunk by the encoder. If
// the
// chunk byte-length is not divisible by three, the last (incomplete)
// value
// quantum canot be encoded right away; it has to wait for the next
// chunk
// of octets which will be processed joined (as if the trailing rest
// from
// the previous one was at its beinning).
template <typename Value>
struct state {
state() : triplet_index(0), last_encoded_value(0) {}
state(state<Value> const& source)
: triplet_index(source.triplet_index),
last_encoded_value(source.last_encoded_value) {}
bool empty() const { return triplet_index == 0; }
void clear() {
// indicate that no rest has been left in the last encoded value
// and no padding is needed for the encoded output
triplet_index = 0;
// the last encoded value, which may have been left from the last
// encoding step, must be zeroed too; it is important before the
// next encoding begins, because it works as a cyclic buffer and
// must start empty - with zero
last_encoded_value = 0;
}
protected:
// number of the octet in the incomplete quantum, which has been
// processed the last time; 0 means that the previous quantum was
// complete 3 octets, 1 that just one octet was avalable and 2 that
// two octets were available
unsigned char triplet_index;
// the value made of the previously shifted and or-ed octets which
// was not completely split to 6-bit codes, because the last quantum
// did not stop on the boundary of three octets
Value last_encoded_value;
// encoding of an input chunk needs to read and update the state
template <typename InputIterator, typename OutputIterator, typename State>
friend OutputIterator encode(InputIterator begin, InputIterator end,
OutputIterator output, State& rest);
// finishing the encoding needs to read and clear the state
template <typename OutputIterator, typename State>
friend OutputIterator encode_rest(OutputIterator output, State& rest);
};
// Encodes an input sequence to BASE64 writing it to the output iterator
// and stopping if the last input tree-octet quantum was not complete,
// in
// which case it stores the state for the later continuation, when
// another
// input chunk is ready for the encoding. The encoding must be finished
// by calling the encode_rest after processing the last chunk.
//
// std::vector<unsigned char> buffer = ...;
// std::basic_string<Char> result;
// std::back_insert_iterator<std::basic_string<Char> > appender(result);
// base64::state<unsigned char> rest;
// base64::encode(buffer.begin(), buffer.end(), appender, rest);
// ...
// base64::encode_rest(appender, rest);
template <typename InputIterator, typename OutputIterator, typename State>
OutputIterator encode(InputIterator begin, InputIterator end,
OutputIterator output, State& rest) {
typedef typename iterator_value<InputIterator>::type value_type;
// continue with the rest of the last chunk - 2 or 4 bits which
// are already shifted to the left and need to be or-ed with the
// continuing data up to the target 6 bits
value_type encoded_value = rest.last_encoded_value;
// if the previous chunk stopped at encoding the first (1) or the
// second
// (2) octet of the three-byte quantum, jump to the right place,
// otherwise start the loop with an empty encoded value buffer
switch (rest.triplet_index) {
// this loop processes the input sequence of bit-octets by bits,
// shifting the current_value (used as a cyclic buffer) left and
// or-ing next bits there, while pulling the bit-sextets from the
// high word of the current_value
for (value_type current_value;;) {
case 0:
// if the input sequence is empty or reached its end at the
// 3-byte boundary, finish with an empty encoding state
if (begin == end) {
rest.triplet_index = 0;
// the last encoded value is not interesting - it would not
// be used, because processing of the next chunk will start
// at the 3-byte boundary
rest.last_encoded_value = 0;
return output;
}
// read the first octet from the current triplet
current_value = *begin++;
// use just the upper 6 bits to encode it to the target alphabet
encoded_value = (current_value & 0xfc) >> 2;
*output++ = detail::encode_value(encoded_value);
// shift the remaining two bits up to make place for the upoming
// part of the next octet
encoded_value = (current_value & 0x03) << 4;
case 1:
// if the input sequence reached its end after the first octet
// from the quantum triplet, store the encoding state and finish
if (begin == end) {
rest.triplet_index = 1;
rest.last_encoded_value = encoded_value;
return output;
}
// read the second first octet from the current triplet
current_value = *begin++;
// combine the upper four bits (as the lower part) with the
// previous two bits to encode it to the target alphabet
encoded_value |= (current_value & 0xf0) >> 4;
*output++ = detail::encode_value(encoded_value);
// shift the remaining four bits up to make place for the
// upoming
// part of the next octet
encoded_value = (current_value & 0x0f) << 2;
case 2:
// if the input sequence reached its end after the second octet
// from the quantum triplet, store the encoding state and finish
if (begin == end) {
rest.triplet_index = 2;
rest.last_encoded_value = encoded_value;
return output;
}
// read the third octet from the current triplet
current_value = *begin++;
// combine the upper two bits (as the lower part) with the
// previous four bits to encode it to the target alphabet
encoded_value |= (current_value & 0xc0) >> 6;
*output++ = detail::encode_value(encoded_value);
// encode the remaining 6 bits to the target alphabet
encoded_value = current_value & 0x3f;
*output++ = detail::encode_value(encoded_value);
}
}
return output;
}
// 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 method must be
// always
// used at the end of encoding, if the previous chunks were encoded by
// the
// method overload accepting the encoding state.
//
// std::vector<unsigned char> buffer = ...;
// std::basic_string<Char> result;
// std::back_insert_iterator<std::basic_string<Char> > appender(result);
// base64::state<unsigned char> rest;
// base64::encode(buffer.begin(), buffer.end(), appender, rest);
// ...
// base64::encode_rest(appender, rest);
template <typename OutputIterator, typename State>
OutputIterator encode_rest(OutputIterator output, State& rest) {
if (!rest.empty()) {
// process the last part of the trailing octet (either 4 or 2 bits)
// as if the input was padded with zeros - without or-ing the next
// input value to it; it has been already shifted to the left
*output++ = detail::encode_value(rest.last_encoded_value);
// at least one padding '=' will be always needed - at least two
// bits are missing in the finally encoded 6-bit value
*output++ = '=';
// if the last octet was the first in the triplet (the index was
// 1), four bits are missing in the finally encoded 6-bit value;
// another '=' character is needed for the another two bits
if (rest.triplet_index < 2) *output++ = '=';
// clear the state all the time to make sure that another call to
// the encode_rest would not cause damage; the last encoded value,
// which may have been left there, must be zeroed too; it is
// important before the next encoding begins, because it works as
// a cyclic buffer and must start empty - with zero
rest.clear();
}
return output;
}
// Encodes a part of an input sequence specified by the pair of begin
// and
// end iterators.to BASE64 writing it to the output iterator. If its
// total
// byte-length was not divisible by three, the output will be padded by
// the
// '=' characters. If you encode an input consisting of mutiple chunks,
// use the method overload maintaining the encoding state.
//
// std::vector<unsigned char> buffer = ...;
// std::basic_string<Char> result;
// base64::encode(buffer.begin(), buffer.end(),
// std::back_inserter(result));
template <typename InputIterator, typename OutputIterator>
OutputIterator encode(InputIterator begin, InputIterator end,
OutputIterator output) {
state<typename iterator_value<InputIterator>::type> rest;
output = encode(begin, end, output, rest);
return encode_rest(output, rest);
}
// Encodes an entire input sequence to BASE64, which either supports
// begin()
// and end() methods returning boundaries of the sequence or the
// boundaries
// can be computed by the Boost::Range, writing it to the output
// iterator
// and stopping if the last input tree-octet quantum was not complete,
// in
// which case it stores the state for the later continuation, when
// another
// input chunk is ready for the encoding. The encoding must be finished
// by calling the encode_rest after processing the last chunk.
//
// 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_string<Char> result;
// std::back_insert_iterator<std::basic_string<Char> > appender(result);
// base64::state<unsigned char> rest;
// base64::encode(buffer, appender, rest);
// ...
// base64::encode_rest(appender, rest);
template <typename InputRange, typename OutputIterator, typename State>
OutputIterator encode(InputRange const& input, OutputIterator output,
State& rest) {
return encode(std::begin(input), std::end(input), output, rest);
}
// Encodes an entire string literal to BASE64, writing it to the output
// iterator and stopping if the last input tree-octet quantum was not
// complete, in which case it stores the state for the later
// continuation,
// when another input chunk is ready for the encoding. The encoding
// must
// be finished by calling the encode_rest after processing the last
// chunk.
//
// The string literal is encoded without processing its terminating zero
// character, which is the usual expectation.
//
// std::basic_string<Char> result;
// std::back_insert_iterator<std::basic_string<Char> > appender(result);
// base64::state<char> rest;
// base64::encode("ab", appender, rest);
// ...
// base64::encode_rest(appender, rest);
template <typename OutputIterator>
OutputIterator encode(char const* value, OutputIterator output,
state<char>& rest) {
return encode(value, value + strlen(value), output, rest);
}
// Encodes an entire input sequence to BASE64 writing it to the output
// iterator, which either supports begin() and end() methods returning
// boundaries of the sequence or the boundaries can be computed by the
// Boost::Range. If its total byte-length was not divisible by three,
// the output will be padded by the '=' characters. If you encode an
// input consisting of mutiple chunks, use the method overload
// maintaining
// the encoding state.
//
// 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_string<Char> result;
// base64::encode(buffer, std::back_inserter(result));
template <typename InputRange, typename OutputIterator>
OutputIterator encode(InputRange const& value, OutputIterator output) {
return encode(std::begin(value), std::end(value), output);
}
// Encodes an entire string literal to BASE64 writing it to the output
// iterator. If its total length (without the trailing zero) was not
// divisible by three, the output will be padded by the '=' characters.
// If you encode an input consisting of mutiple chunks, use the method
// overload maintaining the encoding state.
//
// The string literal is encoded without processing its terminating zero
// character, which is the usual expectation.
//
// std::basic_string<Char> result;
// base64::encode("ab", std::back_inserter(result));
template <typename OutputIterator>
OutputIterator encode(char const* value, OutputIterator output) {
return encode(value, value + strlen(value), output);
}
// Encodes an entire input sequence to BASE64 returning the result as
// string, which either supports begin() and end() methods returning
// boundaries of the sequence or the boundaries can be computed by the
// Boost::Range. If its total byte-length was not divisible by three,
// the output will be padded by the '=' characters. If you encode an
// input consisting of mutiple chunks, use other method maintaining
// the encoding state writing to an output iterator.
//
// 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_string<Char> result = base64::encode<Char>(buffer);
template <typename Char, typename InputRange>
std::basic_string<Char> encode(InputRange const& value) {
std::basic_string<Char> result;
encode(value, std::back_inserter(result));
return result;
}
// Encodes an entire string literal to BASE64 returning the result as
// string. If its total byte-length was not divisible by three, the
// output will be padded by the '=' characters. If you encode an
// input consisting of mutiple chunks, use other method maintaining
// the encoding state writing to an output iterator.
//
// The string literal is encoded without processing its terminating zero
// character, which is the usual expectation.
//
// std::basic_string<Char> result = base64::encode<Char>("ab");
template <typename Char>
std::basic_string<Char> encode(char const* value) {
std::basic_string<Char> result;
encode(value, std::back_inserter(result));
return result;
}
// The function overloads for string literals encode the input without
// the terminating zero, which is usually expected, because the trailing
// zero byte is not considered a part of the string value; the overloads
// for an input range would wrap the string literal by Boost.Range and
// encode the full memory occupated by the string literal - including
// the
// unwanted last zero byte.
} // namespace base64
} // namespace utils
} // namespace network
} // namespace boost
#endif // BOOST_NETWORK_UTILS_BASE64_ENCODE_HPP