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 |
