Files
libbasen/src/baseN.cpp
2024-09-18 21:38:06 +03:00

87 lines
2.8 KiB
C++

#include <algorithm>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <base/baseN.hpp>
static constexpr auto log256 = std::log(256);
namespace baseN
{
bool isValid(const char *str, uint64_t str_size, const int8_t *map) noexcept
{
std::string_view sv(str, str_size);
return std::all_of(sv.begin(), sv.end(), [map](char ch)
{ return map[(int8_t)ch] != -1; });
}
bool isValid(std::string_view str, const int8_t *map) noexcept
{
return baseN::isValid(str.data(), str.size(), map);
}
uint64_t sizeEncoded(std::span<const uint8_t> data, uint8_t base)
{
if (data.size() > std::numeric_limits<uint64_t>::max() / log256)
{
throw std::overflow_error("baseN::sizeEncoded: overflow");
}
return data.size() * log256 / std::log(base) + 1;
}
uint64_t sizeDecoded(std::string_view str, uint8_t base) noexcept
{
return str.size() * std::log(base) / log256 + 1;
}
void encode(const uint8_t *data, uint64_t data_size, char *str, uint64_t str_size, uint8_t base, const char *digits)
{
std::vector<uint8_t> dv(std::find_if(data, data + data_size, [](uint8_t item)
{ return item != 0; }),
data + data_size);
if (dv.size() == 0)
{
return;
}
std::span<char> sv(str, str_size);
auto sv_it = sv.rbegin();
auto dv_it = dv.begin();
auto quo_it = dv.begin();
auto quo_it_last = dv.end();
uint16_t div = *dv_it++;
while ((dv[0] > base || quo_it_last > dv.begin() + 1) && sv_it < sv.rend() - 1)
{
if (div < base)
{
div <<= 8;
div += *dv_it++;
}
*quo_it++ = div / base;
div %= base;
while (dv_it < quo_it_last)
{
div <<= 8;
div += *dv_it++;
*quo_it++ = div / base;
div %= base;
}
quo_it_last = quo_it;
dv_it = dv.begin();
quo_it = dv.begin();
*sv_it++ = digits[div];
div = *dv_it++;
}
*sv_it++ = digits[div];
for (uint64_t i = 0; i < data_size - dv.size() && sv_it < sv.rend(); i++)
{
*sv_it++ = digits[0];
}
}
std::string encode(std::span<const uint8_t> data, uint8_t base, const char *digits) noexcept
{
std::string str(baseN::sizeEncoded(data, base), ' ');
baseN::encode(data.data(), data.size(), str.data(), str.size(), base, digits);
str.erase(str.begin(), std::find_if(
str.begin(), str.end(), [](char ch)
{ return ch != ' '; }));
return str;
}
}