annotate base-n/include/basen.hpp @ 116:d15cb1151d76

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