#include "base32.hpp"
#include <algorithm>
#include <vector>
#include "bitutil.hpp"
NAMESPACE_SOUP
{
static const char b32_alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
std::string base32::encode(const std::string& in, bool pad)
{
return encode(in, pad, b32_alpha);
}
std::string base32::encode(const std::string& in, bool pad, const char* alpha)
{
auto chunks = bitutil::msb_first<std::string, 8, 5>(in);
for (auto& chunk : chunks)
{
chunk = alpha[(uint8_t)chunk];
}
if (pad)
{
if (auto padlen = getEncodedLength(in.length()) - chunks.length())
{
chunks.append(padlen, '=');
}
}
return chunks;
}
static int decode_char(unsigned char c)
{
if (c >= 'A' && c <= 'Z')
{
return c - 'A';
}
if (c >= '2' && c <= '7')
{
return c - '2' + 26;
}
return -1;
}
static int get_octet(int block)
{
return (block * 5) / 8;
}
static int get_offset(int block)
{
return (8 - 5 - (5 * block) % 8);
}
static unsigned char shift_left(unsigned char byte, signed char offset)
{
if (offset < 0)
{
return byte >> -offset;
}
return byte << offset;
}
static bool decode_sequence(const uint8_t* coded, size_t coded_size, size_t octet_base, std::string& out)
{
for (int block = 0; block != 8; ++block)
{
int offset = get_offset(block);
size_t octet = octet_base + get_octet(block);
SOUP_IF_UNLIKELY (block >= coded_size)
{
return false;
}
int c = decode_char(coded[block]);
SOUP_IF_UNLIKELY (c == -1)
{
return false;
}
if (out.size() == octet)
{
out.push_back(shift_left(c, offset));
}
else
{
out.at(octet) |= shift_left(c, offset);
}
if (offset < 0) {
auto overflow = shift_left(c, 8 + offset);
if (overflow != 0)
{
out.push_back(overflow);
}
}
}
return true;
}
std::string base32::decode(const std::string& in)
{
std::string out{};
if (!in.empty())
{
auto arr = (const uint8_t*)in.data();
out.reserve(getDecodedLength(in.size()));
for (size_t i = 0, j = 0; ; i += 8, j += 5)
{
if (!decode_sequence(&arr[i], in.size() - i, j, out))
{
break;
}
}
}
return out;
}
}