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