annotate json/base-n/include/basen.hpp @ 60:8a4bcb3dc3a6

Replace exceptions throughout the JSON-handling and adapter code with string-arg error handling. No longer need exception handling enabled in Emscripten (with its consequent runtime overhead - though we still need to check whether this error handling regime is actually faster).
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 20 Sep 2016 16:35:47 +0100
parents eb004c1b9579
children
rev   line source
c@5 1 /**
c@5 2 * base-n, 1.0
c@5 3 * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com)
c@5 4 *
c@5 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
c@5 6 * of this software and associated documentation files (the "Software"), to deal
c@5 7 * in the Software without restriction, including without limitation the rights
c@5 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
c@5 9 * copies of the Software, and to permit persons to whom the Software is
c@5 10 * furnished to do so, subject to the following conditions:
c@5 11 *
c@5 12 * The above copyright notice and this permission notice shall be included in
c@5 13 * all copies or substantial portions of the Software.
c@5 14 *
c@5 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
c@5 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
c@5 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
c@5 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
c@5 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
c@5 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
c@5 21 * SOFTWARE.
c@5 22 **/
c@5 23 #ifndef BASEN_HPP
c@5 24 #define BASEN_HPP
c@5 25
c@5 26 #include <algorithm>
c@5 27 #include <cctype>
c@5 28 #include <cassert>
c@5 29 #include <cstring>
c@5 30
c@5 31 namespace bn
c@5 32 {
c@5 33
c@5 34 template<class Iter1, class Iter2>
c@5 35 void encode_b16(Iter1 start, Iter1 end, Iter2 out);
c@5 36
c@5 37 template<class Iter1, class Iter2>
c@5 38 void encode_b32(Iter1 start, Iter1 end, Iter2 out);
c@5 39
c@5 40 template<class Iter1, class Iter2>
c@5 41 void encode_b64(Iter1 start, Iter1 end, Iter2 out);
c@5 42
c@5 43 template<class Iter1, class Iter2>
c@5 44 void decode_b16(Iter1 start, Iter1 end, Iter2 out);
c@5 45
c@5 46 template<class Iter1, class Iter2>
c@5 47 void decode_b32(Iter1 start, Iter1 end, Iter2 out);
c@5 48
c@5 49 template<class Iter1, class Iter2>
c@5 50 void decode_b64(Iter1 start, Iter1 end, Iter2 out);
c@5 51
c@5 52 namespace impl
c@5 53 {
c@5 54
c@38 55 const int ERROR = -1;
c@38 56
c@5 57 namespace {
c@5 58
c@5 59 char extract_partial_bits(char value, unsigned int start_bit, unsigned int bits_count)
c@5 60 {
c@5 61 assert(start_bit + bits_count < 8);
c@38 62 // shift extracted bits to the beginning of the byte
c@5 63 char t1 = value >> (8 - bits_count - start_bit);
c@38 64 // mask out bits on the left
c@38 65 char t2 = t1 & ~(-1U << bits_count);
c@5 66 return t2;
c@5 67 }
c@5 68
c@5 69 char extract_overlapping_bits(char previous, char next, unsigned int start_bit, unsigned int bits_count)
c@5 70 {
c@5 71 assert(start_bit + bits_count < 16);
c@5 72 int bits_count_in_previous = 8 - start_bit;
c@5 73 int bits_count_in_next = bits_count - bits_count_in_previous;
c@5 74 char t1 = previous << bits_count_in_next;
c@38 75 char t2 = next >> (8 - bits_count_in_next) & ~(-1U << bits_count_in_next) ;
c@38 76 return (t1 | t2) & ~(-1U << bits_count);
c@5 77 }
c@5 78
c@5 79 }
c@5 80
c@5 81 struct b16_conversion_traits
c@5 82 {
c@5 83 static size_t group_length()
c@5 84 {
c@5 85 return 4;
c@5 86 }
c@5 87
c@5 88 static char encode(unsigned int index)
c@5 89 {
c@5 90 const char* const dictionary = "0123456789ABCDEF";
c@5 91 assert(index < strlen(dictionary));
c@5 92 return dictionary[index];
c@5 93 }
c@5 94
c@5 95 static char decode(char c)
c@5 96 {
c@5 97 if (c >= '0' && c <= '9') {
c@5 98 return c - '0';
c@5 99 } else if (c >= 'A' && c <= 'F') {
c@5 100 return c - 'A' + 10;
c@5 101 }
c@38 102 return ERROR;
c@5 103 }
c@5 104 };
c@5 105
c@5 106 struct b32_conversion_traits
c@5 107 {
c@5 108 static size_t group_length()
c@5 109 {
c@5 110 return 5;
c@5 111 }
c@5 112
c@5 113 static char encode(unsigned int index)
c@5 114 {
c@5 115 const char * dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
c@5 116 assert(index < strlen(dictionary));
c@5 117 return dictionary[index];
c@5 118 }
c@5 119
c@5 120 static char decode(char c)
c@5 121 {
c@5 122 if (c >= 'A' && c <= 'Z') {
c@5 123 return c - 'A';
c@5 124 } else if (c >= '2' && c <= '7') {
c@5 125 return c - '2' + 26;
c@5 126 }
c@38 127 return ERROR;
c@5 128 }
c@5 129 };
c@5 130
c@5 131 struct b64_conversion_traits
c@5 132 {
c@5 133 static size_t group_length()
c@5 134 {
c@5 135 return 6;
c@5 136 }
c@5 137
c@5 138 static char encode(unsigned int index)
c@5 139 {
c@5 140 const char* const dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
c@5 141 assert(index < strlen(dictionary));
c@5 142 return dictionary[index];
c@5 143 }
c@5 144
c@5 145 static char decode(char c)
c@5 146 {
c@5 147 const int alph_len = 26;
c@5 148 if (c >= 'A' && c <= 'Z') {
c@5 149 return c - 'A';
c@5 150 } else if (c >= 'a' && c <= 'z') {
c@5 151 return c - 'a' + alph_len * 1;
c@5 152 } else if (c >= '0' && c <= '9') {
c@5 153 return c - '0' + alph_len * 2;
c@5 154 } else if (c == '+') {
c@5 155 return c - '+' + alph_len * 2 + 10;
c@5 156 } else if (c == '/') {
c@5 157 return c - '/' + alph_len * 2 + 11;
c@5 158 }
c@38 159 return ERROR;
c@5 160 }
c@5 161 };
c@5 162
c@5 163 template<class ConversionTraits, class Iter1, class Iter2>
c@5 164 void decode(Iter1 start, Iter1 end, Iter2 out)
c@5 165 {
c@5 166 Iter1 iter = start;
c@5 167 int output_current_bit = 0;
c@5 168 char buffer = 0;
c@5 169
c@5 170 while (iter != end) {
c@5 171 if (std::isspace(*iter)) {
c@5 172 ++iter;
c@5 173 continue;
c@5 174 }
c@5 175 char value = ConversionTraits::decode(*iter);
c@38 176 if (value == ERROR) {
c@5 177 // malformed data, but let's go on...
c@5 178 ++iter;
c@5 179 continue;
c@5 180 }
c@5 181 unsigned int bits_in_current_byte = std::min<int>(output_current_bit + ConversionTraits::group_length(), 8) - output_current_bit;
c@5 182 if (bits_in_current_byte == ConversionTraits::group_length()) {
c@5 183 // the value fits within current byte, so we can extract it directly
c@5 184 buffer |= value << (8 - output_current_bit - ConversionTraits::group_length());
c@5 185 output_current_bit += ConversionTraits::group_length();
c@5 186 // check if we filled up current byte completely; in such case we flush output and continue
c@5 187 if (output_current_bit == 8) {
c@5 188 *out++ = buffer;
c@5 189 buffer = 0;
c@5 190 output_current_bit = 0;
c@5 191 }
c@5 192 } else {
c@38 193 // the value spans across the current and the next byte
c@5 194 int bits_in_next_byte = ConversionTraits::group_length() - bits_in_current_byte;
c@38 195 // fill the current byte and flush it to our output
c@5 196 buffer |= value >> bits_in_next_byte;
c@5 197 *out++ = buffer;
c@5 198 buffer = 0;
c@5 199 // save the remainder of our value in the buffer; it will be flushed
c@5 200 // during next iterations
c@5 201 buffer |= value << (8 - bits_in_next_byte);
c@5 202 output_current_bit = bits_in_next_byte;
c@5 203 }
c@5 204 ++iter;
c@5 205 }
c@5 206 }
c@5 207
c@5 208 template<class ConversionTraits, class Iter1, class Iter2>
c@5 209 void encode(Iter1 start, Iter1 end, Iter2 out)
c@5 210 {
c@5 211 Iter1 iter = start;
c@5 212 int start_bit = 0;
c@5 213 bool has_backlog = false;
c@5 214 char backlog = 0;
c@5 215
c@5 216 while (has_backlog || iter != end) {
c@5 217 if (!has_backlog) {
c@5 218 if (start_bit + ConversionTraits::group_length() < 8) {
c@5 219 // the value fits within single byte, so we can extract it
c@5 220 // directly
c@5 221 char v = extract_partial_bits(*iter, start_bit, ConversionTraits::group_length());
c@5 222 *out++ = ConversionTraits::encode(v);
c@5 223 // since we know that start_bit + ConversionTraits::group_length() < 8 we don't need to go
c@5 224 // to the next byte
c@5 225 start_bit += ConversionTraits::group_length();
c@5 226 } else {
c@5 227 // our bits are spanning across byte border; we need to keep the
c@5 228 // starting point and move over to next byte.
c@5 229 backlog = *iter++;
c@5 230 has_backlog = true;
c@5 231 }
c@5 232 } else {
c@5 233 // encode value which is made from bits spanning across byte
c@5 234 // boundary
c@5 235 char v;
c@5 236 if (iter == end)
c@5 237 v = extract_overlapping_bits(backlog, 0, start_bit, ConversionTraits::group_length());
c@5 238 else
c@5 239 v = extract_overlapping_bits(backlog, *iter, start_bit, ConversionTraits::group_length());
c@5 240 *out++ = ConversionTraits::encode(v);
c@5 241 has_backlog = false;
c@5 242 start_bit = (start_bit + ConversionTraits::group_length()) % 8;
c@5 243 }
c@5 244 }
c@5 245 }
c@5 246
c@5 247 } // impl
c@5 248
c@5 249 using namespace bn::impl;
c@5 250
c@5 251 template<class Iter1, class Iter2>
c@5 252 void encode_b16(Iter1 start, Iter1 end, Iter2 out)
c@5 253 {
c@5 254 encode<b16_conversion_traits>(start, end, out);
c@5 255 }
c@5 256
c@5 257 template<class Iter1, class Iter2>
c@5 258 void encode_b32(Iter1 start, Iter1 end, Iter2 out)
c@5 259 {
c@5 260 encode<b32_conversion_traits>(start, end, out);
c@5 261 }
c@5 262
c@5 263 template<class Iter1, class Iter2>
c@5 264 void encode_b64(Iter1 start, Iter1 end, Iter2 out)
c@5 265 {
c@5 266 encode<b64_conversion_traits>(start, end, out);
c@5 267 }
c@5 268
c@5 269 template<class Iter1, class Iter2>
c@5 270 void decode_b16(Iter1 start, Iter1 end, Iter2 out)
c@5 271 {
c@5 272 decode<b16_conversion_traits>(start, end, out);
c@5 273 }
c@5 274
c@5 275 template<class Iter1, class Iter2>
c@5 276 void decode_b32(Iter1 start, Iter1 end, Iter2 out)
c@5 277 {
c@5 278 decode<b32_conversion_traits>(start, end, out);
c@5 279 }
c@5 280
c@5 281 template<class Iter1, class Iter2>
c@5 282 void decode_b64(Iter1 start, Iter1 end, Iter2 out)
c@5 283 {
c@5 284 decode<b64_conversion_traits>(start, end, out);
c@5 285 }
c@5 286
c@5 287 } // bn
c@5 288
c@5 289 #endif // BASEN_HPP
c@5 290