annotate ext/base-n/include/basen.hpp @ 218:ea8994465322

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