c@5: /** c@5: * base-n, 1.0 c@5: * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com) c@5: * c@5: * Permission is hereby granted, free of charge, to any person obtaining a copy c@5: * of this software and associated documentation files (the "Software"), to deal c@5: * in the Software without restriction, including without limitation the rights c@5: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell c@5: * copies of the Software, and to permit persons to whom the Software is c@5: * furnished to do so, subject to the following conditions: c@5: * c@5: * The above copyright notice and this permission notice shall be included in c@5: * all copies or substantial portions of the Software. c@5: * c@5: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR c@5: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, c@5: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE c@5: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER c@5: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, c@5: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE c@5: * SOFTWARE. c@5: **/ c@5: #ifndef BASEN_HPP c@5: #define BASEN_HPP c@5: c@5: #include c@5: #include c@5: #include c@5: #include c@5: c@5: namespace bn c@5: { c@5: c@5: template c@5: void encode_b16(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: template c@5: void encode_b32(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: template c@5: void encode_b64(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: template c@5: void decode_b16(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: template c@5: void decode_b32(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: template c@5: void decode_b64(Iter1 start, Iter1 end, Iter2 out); c@5: c@5: namespace impl c@5: { c@5: c@38: const int ERROR = -1; c@38: c@5: namespace { c@5: c@5: char extract_partial_bits(char value, unsigned int start_bit, unsigned int bits_count) c@5: { c@5: assert(start_bit + bits_count < 8); c@38: // shift extracted bits to the beginning of the byte c@5: char t1 = value >> (8 - bits_count - start_bit); c@38: // mask out bits on the left c@38: char t2 = t1 & ~(-1U << bits_count); c@5: return t2; c@5: } c@5: c@5: char extract_overlapping_bits(char previous, char next, unsigned int start_bit, unsigned int bits_count) c@5: { c@5: assert(start_bit + bits_count < 16); c@5: int bits_count_in_previous = 8 - start_bit; c@5: int bits_count_in_next = bits_count - bits_count_in_previous; c@5: char t1 = previous << bits_count_in_next; c@38: char t2 = next >> (8 - bits_count_in_next) & ~(-1U << bits_count_in_next) ; c@38: return (t1 | t2) & ~(-1U << bits_count); c@5: } c@5: c@5: } c@5: c@5: struct b16_conversion_traits c@5: { c@5: static size_t group_length() c@5: { c@5: return 4; c@5: } c@5: c@5: static char encode(unsigned int index) c@5: { c@5: const char* const dictionary = "0123456789ABCDEF"; c@5: assert(index < strlen(dictionary)); c@5: return dictionary[index]; c@5: } c@5: c@5: static char decode(char c) c@5: { c@5: if (c >= '0' && c <= '9') { c@5: return c - '0'; c@5: } else if (c >= 'A' && c <= 'F') { c@5: return c - 'A' + 10; c@5: } c@38: return ERROR; c@5: } c@5: }; c@5: c@5: struct b32_conversion_traits c@5: { c@5: static size_t group_length() c@5: { c@5: return 5; c@5: } c@5: c@5: static char encode(unsigned int index) c@5: { c@5: const char * dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; c@5: assert(index < strlen(dictionary)); c@5: return dictionary[index]; c@5: } c@5: c@5: static char decode(char c) c@5: { c@5: if (c >= 'A' && c <= 'Z') { c@5: return c - 'A'; c@5: } else if (c >= '2' && c <= '7') { c@5: return c - '2' + 26; c@5: } c@38: return ERROR; c@5: } c@5: }; c@5: c@5: struct b64_conversion_traits c@5: { c@5: static size_t group_length() c@5: { c@5: return 6; c@5: } c@5: c@5: static char encode(unsigned int index) c@5: { c@5: const char* const dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; c@5: assert(index < strlen(dictionary)); c@5: return dictionary[index]; c@5: } c@5: c@5: static char decode(char c) c@5: { c@5: const int alph_len = 26; c@5: if (c >= 'A' && c <= 'Z') { c@5: return c - 'A'; c@5: } else if (c >= 'a' && c <= 'z') { c@5: return c - 'a' + alph_len * 1; c@5: } else if (c >= '0' && c <= '9') { c@5: return c - '0' + alph_len * 2; c@5: } else if (c == '+') { c@5: return c - '+' + alph_len * 2 + 10; c@5: } else if (c == '/') { c@5: return c - '/' + alph_len * 2 + 11; c@5: } c@38: return ERROR; c@5: } c@5: }; c@5: c@5: template c@5: void decode(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: Iter1 iter = start; c@5: int output_current_bit = 0; c@5: char buffer = 0; c@5: c@5: while (iter != end) { c@5: if (std::isspace(*iter)) { c@5: ++iter; c@5: continue; c@5: } c@5: char value = ConversionTraits::decode(*iter); c@38: if (value == ERROR) { c@5: // malformed data, but let's go on... c@5: ++iter; c@5: continue; c@5: } c@5: unsigned int bits_in_current_byte = std::min(output_current_bit + ConversionTraits::group_length(), 8) - output_current_bit; c@5: if (bits_in_current_byte == ConversionTraits::group_length()) { c@5: // the value fits within current byte, so we can extract it directly c@5: buffer |= value << (8 - output_current_bit - ConversionTraits::group_length()); c@5: output_current_bit += ConversionTraits::group_length(); c@5: // check if we filled up current byte completely; in such case we flush output and continue c@5: if (output_current_bit == 8) { c@5: *out++ = buffer; c@5: buffer = 0; c@5: output_current_bit = 0; c@5: } c@5: } else { c@38: // the value spans across the current and the next byte c@5: int bits_in_next_byte = ConversionTraits::group_length() - bits_in_current_byte; c@38: // fill the current byte and flush it to our output c@5: buffer |= value >> bits_in_next_byte; c@5: *out++ = buffer; c@5: buffer = 0; c@5: // save the remainder of our value in the buffer; it will be flushed c@5: // during next iterations c@5: buffer |= value << (8 - bits_in_next_byte); c@5: output_current_bit = bits_in_next_byte; c@5: } c@5: ++iter; c@5: } c@5: } c@5: c@5: template c@5: void encode(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: Iter1 iter = start; c@5: int start_bit = 0; c@5: bool has_backlog = false; c@5: char backlog = 0; c@5: c@5: while (has_backlog || iter != end) { c@5: if (!has_backlog) { c@5: if (start_bit + ConversionTraits::group_length() < 8) { c@5: // the value fits within single byte, so we can extract it c@5: // directly c@5: char v = extract_partial_bits(*iter, start_bit, ConversionTraits::group_length()); c@5: *out++ = ConversionTraits::encode(v); c@5: // since we know that start_bit + ConversionTraits::group_length() < 8 we don't need to go c@5: // to the next byte c@5: start_bit += ConversionTraits::group_length(); c@5: } else { c@5: // our bits are spanning across byte border; we need to keep the c@5: // starting point and move over to next byte. c@5: backlog = *iter++; c@5: has_backlog = true; c@5: } c@5: } else { c@5: // encode value which is made from bits spanning across byte c@5: // boundary c@5: char v; c@5: if (iter == end) c@5: v = extract_overlapping_bits(backlog, 0, start_bit, ConversionTraits::group_length()); c@5: else c@5: v = extract_overlapping_bits(backlog, *iter, start_bit, ConversionTraits::group_length()); c@5: *out++ = ConversionTraits::encode(v); c@5: has_backlog = false; c@5: start_bit = (start_bit + ConversionTraits::group_length()) % 8; c@5: } c@5: } c@5: } c@5: c@5: } // impl c@5: c@5: using namespace bn::impl; c@5: c@5: template c@5: void encode_b16(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: encode(start, end, out); c@5: } c@5: c@5: template c@5: void encode_b32(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: encode(start, end, out); c@5: } c@5: c@5: template c@5: void encode_b64(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: encode(start, end, out); c@5: } c@5: c@5: template c@5: void decode_b16(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: decode(start, end, out); c@5: } c@5: c@5: template c@5: void decode_b32(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: decode(start, end, out); c@5: } c@5: c@5: template c@5: void decode_b64(Iter1 start, Iter1 end, Iter2 out) c@5: { c@5: decode(start, end, out); c@5: } c@5: c@5: } // bn c@5: c@5: #endif // BASEN_HPP c@5: