140 lines
3.7 KiB
C++
140 lines
3.7 KiB
C++
#pragma once
|
|
// base36_codec.hpp
|
|
#ifndef BASE36_CODEC_HPP
|
|
#define BASE36_CODEC_HPP
|
|
|
|
#include <string>
|
|
#include <cctype>
|
|
#include <limits>
|
|
#include <charconv>
|
|
|
|
//By ChatGPT: 1:1 converted from base36 library source code
|
|
namespace basecodec
|
|
{
|
|
// Base36 alphabet
|
|
inline constexpr const char* base36_alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
// Python: line 17
|
|
// def dumps(number):
|
|
inline bool encodeBase36(int64_t number, std::string& result) noexcept
|
|
{
|
|
result.clear();
|
|
|
|
// Python: line 23
|
|
if (number < 0)
|
|
{
|
|
std::string positive;
|
|
if (!encodeBase36(-number, positive))
|
|
return false;
|
|
result = '-' + positive;
|
|
return true;
|
|
}
|
|
|
|
// Python: line 26
|
|
if (number == 0)
|
|
{
|
|
result = "0";
|
|
return true;
|
|
}
|
|
|
|
std::string value;
|
|
while (number != 0)
|
|
{
|
|
int remainder = number % 36;
|
|
number /= 36;
|
|
value.insert(value.begin(), base36_alphabet[remainder]);
|
|
}
|
|
|
|
result = std::move(value);
|
|
return true;
|
|
}
|
|
|
|
inline bool encodeBase36FromDecimalString(const std::string& decimalStr, std::string& base36Out) noexcept
|
|
{
|
|
uint64_t number = 0;
|
|
auto [ptr, ec] = std::from_chars(decimalStr.data(), decimalStr.data() + decimalStr.size(), number, 10);
|
|
if (ec != std::errc())
|
|
return false;
|
|
|
|
static const char alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
std::string result;
|
|
do
|
|
{
|
|
result.insert(result.begin(), alphabet[number % 36]);
|
|
number /= 36;
|
|
}
|
|
while (number != 0);
|
|
|
|
base36Out = result;
|
|
return true;
|
|
}
|
|
|
|
// Python: line 35
|
|
// def loads(value):
|
|
inline bool decodeBase36(const std::string& input, int64_t& number) noexcept
|
|
{
|
|
// Validate input: allow optional leading '-', rest must be base36 chars
|
|
size_t start = 0;
|
|
bool negative = false;
|
|
if (!input.empty() && input[0] == '-')
|
|
{
|
|
negative = true;
|
|
start = 1;
|
|
}
|
|
|
|
if (start == input.size())
|
|
return false; // "-" alone is invalid
|
|
|
|
number = 0;
|
|
for (size_t i = start; i < input.size(); ++i)
|
|
{
|
|
char c = std::tolower(input[i]);
|
|
int digit;
|
|
if (c >= '0' && c <= '9')
|
|
digit = c - '0';
|
|
else if (c >= 'a' && c <= 'z')
|
|
digit = c - 'a' + 10;
|
|
else
|
|
return false; // invalid character
|
|
#pragma push_macro("max")
|
|
#undef max
|
|
if (number > (std::numeric_limits<int64_t>::max() - digit) / 36)
|
|
return false; // overflow
|
|
#pragma pop_macro("max")
|
|
number = number * 36 + digit;
|
|
}
|
|
|
|
if (negative)
|
|
number = -number;
|
|
return true;
|
|
}
|
|
|
|
inline bool decodeBase36ToDecimalString(const std::string& base36Str, std::string& decimalOut) noexcept
|
|
{
|
|
uint64_t number = 0;
|
|
for (char c : base36Str)
|
|
{
|
|
int digit = 0;
|
|
if (c >= '0' && c <= '9')
|
|
digit = c - '0';
|
|
else if (c >= 'a' && c <= 'z')
|
|
digit = c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'Z')
|
|
digit = c - 'A' + 10; // case-insensitive
|
|
else
|
|
return false; // invalid character
|
|
if (digit >= 36)
|
|
return false;
|
|
number = number * 36 + digit;
|
|
}
|
|
|
|
// Convert number to string using std::to_string
|
|
decimalOut = std::to_string(number);
|
|
return true;
|
|
}
|
|
|
|
} // namespace basecodec
|
|
|
|
#endif // BASE36_CODEC_HPP
|