Mercurial > hg > piper-cpp
comparison base-n/include/basen.hpp @ 75:81e1c48e97f9
Rearrange and rename to Piper C++ structure
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 10 Oct 2016 16:31:09 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
74:d45cfa25aaad | 75:81e1c48e97f9 |
---|---|
1 /** | |
2 * base-n, 1.0 | |
3 * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com) | |
4 * | |
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: | |
11 * | |
12 * The above copyright notice and this permission notice shall be included in | |
13 * all copies or substantial portions of the Software. | |
14 * | |
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 | |
21 * SOFTWARE. | |
22 **/ | |
23 #ifndef BASEN_HPP | |
24 #define BASEN_HPP | |
25 | |
26 #include <algorithm> | |
27 #include <cctype> | |
28 #include <cassert> | |
29 #include <cstring> | |
30 | |
31 namespace bn | |
32 { | |
33 | |
34 template<class Iter1, class Iter2> | |
35 void encode_b16(Iter1 start, Iter1 end, Iter2 out); | |
36 | |
37 template<class Iter1, class Iter2> | |
38 void encode_b32(Iter1 start, Iter1 end, Iter2 out); | |
39 | |
40 template<class Iter1, class Iter2> | |
41 void encode_b64(Iter1 start, Iter1 end, Iter2 out); | |
42 | |
43 template<class Iter1, class Iter2> | |
44 void decode_b16(Iter1 start, Iter1 end, Iter2 out); | |
45 | |
46 template<class Iter1, class Iter2> | |
47 void decode_b32(Iter1 start, Iter1 end, Iter2 out); | |
48 | |
49 template<class Iter1, class Iter2> | |
50 void decode_b64(Iter1 start, Iter1 end, Iter2 out); | |
51 | |
52 namespace impl | |
53 { | |
54 | |
55 const int ERROR = -1; | |
56 | |
57 namespace { | |
58 | |
59 char extract_partial_bits(char value, unsigned int start_bit, unsigned int bits_count) | |
60 { | |
61 assert(start_bit + bits_count < 8); | |
62 // shift extracted bits to the beginning of the byte | |
63 char t1 = value >> (8 - bits_count - start_bit); | |
64 // mask out bits on the left | |
65 char t2 = t1 & ~(-1U << bits_count); | |
66 return t2; | |
67 } | |
68 | |
69 char extract_overlapping_bits(char previous, char next, unsigned int start_bit, unsigned int bits_count) | |
70 { | |
71 assert(start_bit + bits_count < 16); | |
72 int bits_count_in_previous = 8 - start_bit; | |
73 int bits_count_in_next = bits_count - bits_count_in_previous; | |
74 char t1 = previous << bits_count_in_next; | |
75 char t2 = next >> (8 - bits_count_in_next) & ~(-1U << bits_count_in_next) ; | |
76 return (t1 | t2) & ~(-1U << bits_count); | |
77 } | |
78 | |
79 } | |
80 | |
81 struct b16_conversion_traits | |
82 { | |
83 static size_t group_length() | |
84 { | |
85 return 4; | |
86 } | |
87 | |
88 static char encode(unsigned int index) | |
89 { | |
90 const char* const dictionary = "0123456789ABCDEF"; | |
91 assert(index < strlen(dictionary)); | |
92 return dictionary[index]; | |
93 } | |
94 | |
95 static char decode(char c) | |
96 { | |
97 if (c >= '0' && c <= '9') { | |
98 return c - '0'; | |
99 } else if (c >= 'A' && c <= 'F') { | |
100 return c - 'A' + 10; | |
101 } | |
102 return ERROR; | |
103 } | |
104 }; | |
105 | |
106 struct b32_conversion_traits | |
107 { | |
108 static size_t group_length() | |
109 { | |
110 return 5; | |
111 } | |
112 | |
113 static char encode(unsigned int index) | |
114 { | |
115 const char * dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | |
116 assert(index < strlen(dictionary)); | |
117 return dictionary[index]; | |
118 } | |
119 | |
120 static char decode(char c) | |
121 { | |
122 if (c >= 'A' && c <= 'Z') { | |
123 return c - 'A'; | |
124 } else if (c >= '2' && c <= '7') { | |
125 return c - '2' + 26; | |
126 } | |
127 return ERROR; | |
128 } | |
129 }; | |
130 | |
131 struct b64_conversion_traits | |
132 { | |
133 static size_t group_length() | |
134 { | |
135 return 6; | |
136 } | |
137 | |
138 static char encode(unsigned int index) | |
139 { | |
140 const char* const dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
141 assert(index < strlen(dictionary)); | |
142 return dictionary[index]; | |
143 } | |
144 | |
145 static char decode(char c) | |
146 { | |
147 const int alph_len = 26; | |
148 if (c >= 'A' && c <= 'Z') { | |
149 return c - 'A'; | |
150 } else if (c >= 'a' && c <= 'z') { | |
151 return c - 'a' + alph_len * 1; | |
152 } else if (c >= '0' && c <= '9') { | |
153 return c - '0' + alph_len * 2; | |
154 } else if (c == '+') { | |
155 return c - '+' + alph_len * 2 + 10; | |
156 } else if (c == '/') { | |
157 return c - '/' + alph_len * 2 + 11; | |
158 } | |
159 return ERROR; | |
160 } | |
161 }; | |
162 | |
163 template<class ConversionTraits, class Iter1, class Iter2> | |
164 void decode(Iter1 start, Iter1 end, Iter2 out) | |
165 { | |
166 Iter1 iter = start; | |
167 int output_current_bit = 0; | |
168 char buffer = 0; | |
169 | |
170 while (iter != end) { | |
171 if (std::isspace(*iter)) { | |
172 ++iter; | |
173 continue; | |
174 } | |
175 char value = ConversionTraits::decode(*iter); | |
176 if (value == ERROR) { | |
177 // malformed data, but let's go on... | |
178 ++iter; | |
179 continue; | |
180 } | |
181 unsigned int bits_in_current_byte = std::min<int>(output_current_bit + ConversionTraits::group_length(), 8) - output_current_bit; | |
182 if (bits_in_current_byte == ConversionTraits::group_length()) { | |
183 // the value fits within current byte, so we can extract it directly | |
184 buffer |= value << (8 - output_current_bit - ConversionTraits::group_length()); | |
185 output_current_bit += ConversionTraits::group_length(); | |
186 // check if we filled up current byte completely; in such case we flush output and continue | |
187 if (output_current_bit == 8) { | |
188 *out++ = buffer; | |
189 buffer = 0; | |
190 output_current_bit = 0; | |
191 } | |
192 } else { | |
193 // the value spans across the current and the next byte | |
194 int bits_in_next_byte = ConversionTraits::group_length() - bits_in_current_byte; | |
195 // fill the current byte and flush it to our output | |
196 buffer |= value >> bits_in_next_byte; | |
197 *out++ = buffer; | |
198 buffer = 0; | |
199 // save the remainder of our value in the buffer; it will be flushed | |
200 // during next iterations | |
201 buffer |= value << (8 - bits_in_next_byte); | |
202 output_current_bit = bits_in_next_byte; | |
203 } | |
204 ++iter; | |
205 } | |
206 } | |
207 | |
208 template<class ConversionTraits, class Iter1, class Iter2> | |
209 void encode(Iter1 start, Iter1 end, Iter2 out) | |
210 { | |
211 Iter1 iter = start; | |
212 int start_bit = 0; | |
213 bool has_backlog = false; | |
214 char backlog = 0; | |
215 | |
216 while (has_backlog || iter != end) { | |
217 if (!has_backlog) { | |
218 if (start_bit + ConversionTraits::group_length() < 8) { | |
219 // the value fits within single byte, so we can extract it | |
220 // directly | |
221 char v = extract_partial_bits(*iter, start_bit, ConversionTraits::group_length()); | |
222 *out++ = ConversionTraits::encode(v); | |
223 // since we know that start_bit + ConversionTraits::group_length() < 8 we don't need to go | |
224 // to the next byte | |
225 start_bit += ConversionTraits::group_length(); | |
226 } else { | |
227 // our bits are spanning across byte border; we need to keep the | |
228 // starting point and move over to next byte. | |
229 backlog = *iter++; | |
230 has_backlog = true; | |
231 } | |
232 } else { | |
233 // encode value which is made from bits spanning across byte | |
234 // boundary | |
235 char v; | |
236 if (iter == end) | |
237 v = extract_overlapping_bits(backlog, 0, start_bit, ConversionTraits::group_length()); | |
238 else | |
239 v = extract_overlapping_bits(backlog, *iter, start_bit, ConversionTraits::group_length()); | |
240 *out++ = ConversionTraits::encode(v); | |
241 has_backlog = false; | |
242 start_bit = (start_bit + ConversionTraits::group_length()) % 8; | |
243 } | |
244 } | |
245 } | |
246 | |
247 } // impl | |
248 | |
249 using namespace bn::impl; | |
250 | |
251 template<class Iter1, class Iter2> | |
252 void encode_b16(Iter1 start, Iter1 end, Iter2 out) | |
253 { | |
254 encode<b16_conversion_traits>(start, end, out); | |
255 } | |
256 | |
257 template<class Iter1, class Iter2> | |
258 void encode_b32(Iter1 start, Iter1 end, Iter2 out) | |
259 { | |
260 encode<b32_conversion_traits>(start, end, out); | |
261 } | |
262 | |
263 template<class Iter1, class Iter2> | |
264 void encode_b64(Iter1 start, Iter1 end, Iter2 out) | |
265 { | |
266 encode<b64_conversion_traits>(start, end, out); | |
267 } | |
268 | |
269 template<class Iter1, class Iter2> | |
270 void decode_b16(Iter1 start, Iter1 end, Iter2 out) | |
271 { | |
272 decode<b16_conversion_traits>(start, end, out); | |
273 } | |
274 | |
275 template<class Iter1, class Iter2> | |
276 void decode_b32(Iter1 start, Iter1 end, Iter2 out) | |
277 { | |
278 decode<b32_conversion_traits>(start, end, out); | |
279 } | |
280 | |
281 template<class Iter1, class Iter2> | |
282 void decode_b64(Iter1 start, Iter1 end, Iter2 out) | |
283 { | |
284 decode<b64_conversion_traits>(start, end, out); | |
285 } | |
286 | |
287 } // bn | |
288 | |
289 #endif // BASEN_HPP | |
290 |