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