3 * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com)
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * In the original file, static analysis complains about return type from decode methods
26 * not being testable against Error constant (due to difference of type sizes).
28 * Modified decode methods to return error through an out parameter instead.
42 template<class Iter1, class Iter2>
43 void encode_b16(Iter1 start, Iter1 end, Iter2 out);
45 template<class Iter1, class Iter2>
46 void encode_b32(Iter1 start, Iter1 end, Iter2 out);
48 template<class Iter1, class Iter2>
49 void encode_b64(Iter1 start, Iter1 end, Iter2 out);
51 template<class Iter1, class Iter2>
52 void decode_b16(Iter1 start, Iter1 end, Iter2 out);
54 template<class Iter1, class Iter2>
55 void decode_b32(Iter1 start, Iter1 end, Iter2 out);
57 template<class Iter1, class Iter2>
58 void decode_b64(Iter1 start, Iter1 end, Iter2 out);
67 char extract_partial_bits(char value, size_t start_bit, size_t bits_count)
69 assert(start_bit + bits_count < 8);
70 // shift extracted bits to the beginning of the byte
71 char t1 = value >> (8 - bits_count - start_bit);
72 // mask out bits on the left
73 char t2 = t1 & ~(0xff << bits_count);
77 char extract_overlapping_bits(char previous, char next, size_t start_bit, size_t bits_count)
79 assert(start_bit + bits_count < 16);
80 size_t bits_count_in_previous = 8 - start_bit;
81 size_t bits_count_in_next = bits_count - bits_count_in_previous;
82 char t1 = previous << bits_count_in_next;
83 char t2 = next >> (8 - bits_count_in_next) & ~(0xff << bits_count_in_next) ;
84 return (t1 | t2) & ~(0xff << bits_count);
89 struct b16_conversion_traits
91 static size_t group_length()
96 static char encode(unsigned int index)
98 const char* const dictionary = "0123456789ABCDEF";
99 assert(index < strlen(dictionary));
100 return dictionary[index];
104 * In the original file, error code was passed through return value, but used int -1 (out of range of char).
105 * Separated error value from return value by using out parameter.
107 static char decode(char c, bool& error)
110 if (c >= '0' && c <= '9') {
112 } else if (c >= 'A' && c <= 'F') {
120 struct b32_conversion_traits
122 static size_t group_length()
127 static char encode(unsigned int index)
129 const char * dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
130 assert(index < strlen(dictionary));
131 return dictionary[index];
135 * In the original file, error code was passed through return value, but used int -1 (out of range of char).
136 * Separated error value from return value by using out parameter.
138 static char decode(char c, bool& error)
141 if (c >= 'A' && c <= 'Z') {
143 } else if (c >= '2' && c <= '7') {
151 struct b64_conversion_traits
153 static size_t group_length()
158 static char encode(unsigned int index)
160 const char* const dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
161 assert(index < strlen(dictionary));
162 return dictionary[index];
166 * In the original file, error code was passed through return value, but used int -1 (out of range of char).
167 * Separated error value from return value by using out parameter.
169 static char decode(char c, bool& error)
172 const int alph_len = 26;
173 if (c >= 'A' && c <= 'Z') {
175 } else if (c >= 'a' && c <= 'z') {
176 return c - 'a' + alph_len * 1;
177 } else if (c >= '0' && c <= '9') {
178 return c - '0' + alph_len * 2;
179 } else if (c == '+') {
180 return c - '+' + alph_len * 2 + 10;
181 } else if (c == '/') {
182 return c - '/' + alph_len * 2 + 11;
189 template<class ConversionTraits, class Iter1, class Iter2>
190 void decode(Iter1 start, Iter1 end, Iter2 out)
193 size_t output_current_bit = 0;
196 while (iter != end) {
197 if (std::isspace(*iter)) {
203 * In the original file, error value was out of range of return type.
204 * Separated error value from return value by using out parameter.
207 char value = ConversionTraits::decode(*iter, error);
209 // malformed data, but let's go on...
213 size_t bits_in_current_byte = std::min<size_t>(output_current_bit + ConversionTraits::group_length(), 8) - output_current_bit;
214 if (bits_in_current_byte == ConversionTraits::group_length()) {
215 // the value fits within current byte, so we can extract it directly
216 buffer |= value << (8 - output_current_bit - ConversionTraits::group_length());
217 output_current_bit += ConversionTraits::group_length();
218 // check if we filled up current byte completely; in such case we flush output and continue
219 if (output_current_bit == 8) {
222 output_current_bit = 0;
225 // the value spans across the current and the next byte
226 size_t bits_in_next_byte = ConversionTraits::group_length() - bits_in_current_byte;
227 // fill the current byte and flush it to our output
228 buffer |= value >> bits_in_next_byte;
231 // save the remainder of our value in the buffer; it will be flushed
232 // during next iterations
233 buffer |= value << (8 - bits_in_next_byte);
234 output_current_bit = bits_in_next_byte;
240 template<class ConversionTraits, class Iter1, class Iter2>
241 void encode(Iter1 start, Iter1 end, Iter2 out)
244 size_t start_bit = 0;
245 bool has_backlog = false;
248 while (has_backlog || iter != end) {
250 if (start_bit + ConversionTraits::group_length() < 8) {
251 // the value fits within single byte, so we can extract it
253 char v = extract_partial_bits(*iter, start_bit, ConversionTraits::group_length());
254 *out++ = ConversionTraits::encode(v);
255 // since we know that start_bit + ConversionTraits::group_length() < 8 we don't need to go
257 start_bit += ConversionTraits::group_length();
259 // our bits are spanning across byte border; we need to keep the
260 // starting point and move over to next byte.
265 // encode value which is made from bits spanning across byte
269 v = extract_overlapping_bits(backlog, 0, start_bit, ConversionTraits::group_length());
271 v = extract_overlapping_bits(backlog, *iter, start_bit, ConversionTraits::group_length());
272 *out++ = ConversionTraits::encode(v);
274 start_bit = (start_bit + ConversionTraits::group_length()) % 8;
281 using namespace bn::impl;
283 template<class Iter1, class Iter2>
284 void encode_b16(Iter1 start, Iter1 end, Iter2 out)
286 encode<b16_conversion_traits>(start, end, out);
289 template<class Iter1, class Iter2>
290 void encode_b32(Iter1 start, Iter1 end, Iter2 out)
292 encode<b32_conversion_traits>(start, end, out);
295 template<class Iter1, class Iter2>
296 void encode_b64(Iter1 start, Iter1 end, Iter2 out)
298 encode<b64_conversion_traits>(start, end, out);
301 template<class Iter1, class Iter2>
302 void decode_b16(Iter1 start, Iter1 end, Iter2 out)
304 decode<b16_conversion_traits>(start, end, out);
307 template<class Iter1, class Iter2>
308 void decode_b32(Iter1 start, Iter1 end, Iter2 out)
310 decode<b32_conversion_traits>(start, end, out);
313 template<class Iter1, class Iter2>
314 void decode_b64(Iter1 start, Iter1 end, Iter2 out)
316 decode<b64_conversion_traits>(start, end, out);