d8ae572b |
/* Copyright (C) CJ Affiliate
*
* You may use, distribute and modify this code under the
* terms of the GNU General Public License version 2 or
* later.
*
* You should have received a copy of the license with this
* file. If not, you will find a copy in the "LICENSE" file
* at https://github.com/cjdev/dual-control.
*/
|
c28f345d |
#include <bitset>
|
ad3e76cc |
#include <unordered_map>
|
c28f345d |
#include <memory>
|
83a56114 |
#include <iostream>
|
960833b0 |
|
d8ae572b |
#include "base32.h"
|
ad3e76cc |
static const std::vector<char> alphabet = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
|
d8ae572b |
namespace
{
|
379cc146 |
class invalid_input_size : public std::exception
{
private:
using string_size = std::string::size_type;
public:
string_size received_size;
|
88c14201 |
invalid_input_size (string_size received_size) :
|
379cc146 |
received_size (received_size)
{}
};
|
d8ae572b |
class base32_impl : public base32_ifc
{
private:
|
83a56114 |
uint64_t calculate_padding_bytes (uint64_t extra_bytes) const
|
d8ae572b |
{
auto padding_chars = 0;
switch (extra_bytes) {
case 1:
padding_chars = 6;
break;
case 2:
padding_chars = 4;
break;
case 3:
padding_chars = 3;
break;
case 4:
padding_chars = 1;
break;
}
|
ad3e76cc |
|
d8ae572b |
return padding_chars;
}
|
ad3e76cc |
|
83a56114 |
std::string &pad_string (uint64_t extra_bytes, std::string &input) const
|
d8ae572b |
{
auto padding_chars = calculate_padding_bytes (extra_bytes);
|
ad3e76cc |
|
d8ae572b |
if (padding_chars > 0) {
auto replace_end = input.end();
auto replace_start = replace_end - padding_chars;
auto replace_count = replace_end - replace_start;
|
960833b0 |
|
d8ae572b |
input.replace (replace_start, replace_end, replace_count, '=');
|
ad3e76cc |
}
|
d8ae572b |
return input;
|
ad3e76cc |
}
|
d8ae572b |
public:
|
83a56114 |
std::string encode (const std::vector<uint8_t> data) const override
|
d8ae572b |
{
std::string result;
|
ad3e76cc |
|
d8ae572b |
uint8_t tmp = 0;
auto data_size = data.size();
auto extra_bytes = data_size % 5;
auto begin = data.begin();
auto end = data.end();
auto leftover = (5 - extra_bytes) % 5;
for (auto cur = begin; cur+5 < (end + leftover + 1); cur+=5) {
std::vector<uint8_t> batch;
if (cur+5 < end) {
batch = std::vector<uint8_t> (cur, cur+5);
} else {
batch = std::vector<uint8_t> (cur, end);
for (int x = 0; x < leftover; x++) {
batch.push_back (0);
}
}
std::bitset<40> everything;
|
960833b0 |
|
d8ae572b |
for (int x = 0, y = 32; x < 5; x+=1, y=32-x*8) {
uint64_t item = batch[x];
everything |= item << y;
}
|
960833b0 |
|
d8ae572b |
std::bitset<40> mask = 31;
int offset = 35;
mask <<= offset;
for (/* see above */; offset >= 0; mask >>= 5, offset -= 5) {
auto idx = ((everything & mask) >> offset).to_ullong();
result.push_back (alphabet[idx]);
}
|
960833b0 |
}
|
d8ae572b |
result = pad_string (extra_bytes, result);
return result;
|
ad3e76cc |
}
|
d8ae572b |
private:
|
f04cd01b |
using reverse_map = std::unordered_map <char, unsigned char>;
using character_type = std::string::value_type;
|
ad3e76cc |
|
f04cd01b |
reverse_map construct_lookup_table() const
|
d8ae572b |
{
std::unordered_map<char, unsigned char> lookup_table;
|
ad3e76cc |
|
d8ae572b |
for (int i = 0; i < alphabet.size(); i += 1) {
lookup_table[alphabet[i]] = i;
}
return lookup_table;
|
ad3e76cc |
}
|
d8ae572b |
std::pair<uint8_t, uint8_t> split_value (uint8_t item, uint8_t pos,
|
83a56114 |
uint8_t width) const
|
d8ae572b |
{
auto shift = width-pos;
uint8_t part2_mask = (1 << (shift)) - 1;
uint8_t part2 = item & part2_mask;
item >>= shift;
return { item, part2 };
}
|
ad3e76cc |
|
d8ae572b |
void set_vector_at_bit (std::vector<uint8_t> &data, uint8_t item,
std::vector<uint8_t>::size_type byte_offset, uint8_t bit_offset_from_start,
|
83a56114 |
uint8_t width) const
|
d8ae572b |
{
bool need_to_split = bit_offset_from_start + width > 8;
uint8_t bit_offset_from_end = 7 - bit_offset_from_start;
if (need_to_split) {
uint8_t split_pos = bit_offset_from_end + 1;
auto pieces = split_value (item, split_pos, width);
data[byte_offset] |= pieces.first;
uint8_t leftover_bits = width - split_pos;
uint8_t second_part_shift = 8 - leftover_bits;
data[byte_offset+1] |= pieces.second << second_part_shift;
} else {
uint8_t last_bit = (bit_offset_from_end - width) + 1;
data[byte_offset] |= item << last_bit;
}
|
960833b0 |
}
|
ad3e76cc |
|
83a56114 |
std::string::size_type calculate_decoded_size (std::string input) const
|
d8ae572b |
{
std::string::size_type input_size = input.size();
std::string::size_type first_equals = input.find_first_of ('=');
|
ad3e76cc |
|
379cc146 |
if (input_size == 0 || (input_size % 8 != 0)) {
|
88c14201 |
throw invalid_input_size (input_size);
|
379cc146 |
}
|
d8ae572b |
if (first_equals != std::string::npos) {
input_size = first_equals;
}
|
ad3e76cc |
|
d8ae572b |
return (input_size * 5) / 8;
|
379cc146 |
|
d8ae572b |
}
|
960833b0 |
|
0d8b9a17 |
uint8_t get_next_input (reverse_map &lookup_table, std::string &input,
std::string::size_type idx) const
{
|
f04cd01b |
character_type character = input[idx];
|
0d8b9a17 |
if (character != '='
&& lookup_table.find (character) == lookup_table.end()) {
|
f04cd01b |
throw invalid_data_exception ();
}
return lookup_table[input[idx]];
}
|
d8ae572b |
public:
|
ad3e76cc |
|
83a56114 |
std::vector<uint8_t> decode (std::string input) const override
|
d8ae572b |
{
auto lookup_table = construct_lookup_table();
auto input_size = calculate_decoded_size (input);
|
960833b0 |
|
d8ae572b |
std::vector<uint8_t> result (input_size, 0);
|
ad3e76cc |
|
d8ae572b |
unsigned long long bits_written = 0;
|
ad3e76cc |
|
d8ae572b |
for (std::string::size_type idx = 0; idx < input.size(); idx++) {
uint8_t start_bit = bits_written % 8;
std::vector<unsigned char>::size_type current_byte = bits_written/8;
|
ad3e76cc |
|
0d8b9a17 |
uint8_t val = get_next_input (lookup_table, input, idx);
|
d8ae572b |
set_vector_at_bit (result, val, current_byte, start_bit, 5);
|
ad3e76cc |
|
d8ae572b |
bits_written += 5;
}
|
960833b0 |
|
d8ae572b |
return result;
|
960833b0 |
}
|
d8ae572b |
};
|
960833b0 |
}
|
ad3e76cc |
template class std::vector<unsigned char>;
|
d8ae572b |
|
c28f345d |
base32::base32 ():
|
d8ae572b |
delegate_ (std::make_shared<base32_impl> ())
{}
|
0d8b9a17 |
|