Files
UNSWebServerCore_WindowsDLL/UOHash/basecodec.hpp
T
2026-06-30 17:46:34 +08:00

509 lines
15 KiB
C++

#pragma once
#ifndef BASECODEC_HPP
#define BASECODEC_HPP
#include <string>
#include <vector>
#include <cctype>
#include <cstdint>
#include <array>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <unordered_map>
//By ChatGPT: 1:1 converted from cpython library source code
//base58 from base58 library
namespace basecodec
{
// -------------------------
// Base16 (hex) encoding/decoding
// Converted from Python base16 implementation:
// def b16encode(s): return binascii.hexlify(s).upper()
// def b16decode(s, casefold=False): ... binascii.unhexlify(s)
// Python source around lines 290-320 in base64.py
// -------------------------
// Encode to hex, always succeeds
inline std::string b16encode(const std::vector<uint8_t>& data)
{
std::ostringstream oss;
oss << std::uppercase << std::hex;
for (auto byte : data)
oss << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
return oss.str();
}
// Decode from hex, returns success status; outputs bytes in 'out'
inline bool b16decode(const std::string& s, std::vector<uint8_t>& out, bool casefold = false)
{
std::string str = s;
if (casefold)
{
for (auto& c : str)
c = std::toupper(static_cast<unsigned char>(c));
}
if (str.size() % 2 != 0)
return false; // invalid length
out.clear();
out.reserve(str.size() / 2);
for (size_t i = 0; i < str.size(); i += 2)
{
auto val = [&] (char c) -> int
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
};
int high = val(str[i]);
int low = val(str[i + 1]);
if (high < 0 || low < 0)
{
out.clear();
return false; // non-hex digit
}
out.push_back(static_cast<uint8_t>((high << 4) | low));
}
return true;
}
// -------------------------
// Base85 encoding/decoding (Z85-compatible)
// Converted from Python base85 implementation:
// def b85encode(b, pad=False): ... _85encode(...)
// def b85decode(b): ...
// Python source around lines 550-650 in base64.py
// -------------------------
// Base85 alphabet as per Python _b85alphabet
inline const std::string& _b85alphabet()
{
static const std::string alphabet =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
return alphabet;
}
// Precomputed table of 85x85 two-character combos
inline const std::vector<std::string>& _b85chars2()
{
static std::vector<std::string> table;
if (table.empty())
{
const auto& alph = _b85alphabet();
table.reserve(85 * 85);
for (char a : alph)
for (char b : alph)
table.emplace_back(std::string{a, b});
}
return table;
}
// Encode bytes to Z85 string, always succeeds
inline std::string b85encode(const std::vector<uint8_t>& data, bool pad = false)
{
const auto& alph = _b85alphabet();
const auto& table2 = _b85chars2();
size_t len = data.size();
size_t padding = (4 - (len % 4)) % 4;
std::vector<uint8_t> bytes = data;
if (padding)
bytes.insert(bytes.end(), padding, 0);
std::string result;
result.reserve((bytes.size() / 4) * 5);
for (size_t i = 0; i < bytes.size(); i += 4)
{
uint32_t acc =
(uint32_t(bytes[i]) << 24) |
(uint32_t(bytes[i + 1]) << 16) |
(uint32_t(bytes[i + 2]) << 8) |
uint32_t(bytes[i + 3]);
uint32_t idx1 = acc / 614125; // 85^3
uint32_t idx2 = (acc / 85) % 7225; // 85^2
uint32_t idx3 = acc % 85;
result += table2[idx1];
result += table2[idx2];
result.push_back(alph[idx3]);
}
if (padding && !pad)
result.resize(result.size() - padding);
return result;
}
// Decode Z85 string to bytes, returns success status; outputs bytes in 'out'
inline bool b85decode(const std::string& s, std::vector<uint8_t>& out)
{
const auto& alph = _b85alphabet();
static std::array<int, 256> dec;
static bool init = false;
if (!init)
{
dec.fill(-1);
for (size_t i = 0; i < alph.size(); ++i)
dec[static_cast<unsigned char>(alph[i])] = int(i);
init = true;
}
size_t len = s.size();
size_t padding = (5 - (len % 5)) % 5;
std::string str = s;
str.append(padding, alph[0]);
out.clear();
out.reserve((str.size() / 5) * 4);
for (size_t i = 0; i < str.size(); i += 5)
{
uint32_t acc = 0;
for (size_t j = 0; j < 5; ++j)
{
int v = dec[static_cast<unsigned char>(str[i + j])];
if (v < 0)
{
out.clear();
return false; // bad base85 char
}
acc = acc * 85 + uint32_t(v);
}
out.push_back(uint8_t((acc >> 24) & 0xFF));
out.push_back(uint8_t((acc >> 16) & 0xFF));
out.push_back(uint8_t((acc >> 8) & 0xFF));
out.push_back(uint8_t(acc & 0xFF));
}
if (padding)
out.resize(out.size() - padding);
return true;
}
// -------------------------
// Base58 encoding/decoding (bitcoin-compatible)
// Converted from Python base58 implementation (base58.py)
// -------------------------
inline const std::string& b58_alphabet()
{
static const std::string alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
return alphabet;
}
inline std::string b58encode(const std::vector<uint8_t>& data)
{
const auto& alph = b58_alphabet();
std::string result;
uint64_t num = 0;
for (uint8_t b : data) num = (num << 8) | b;
while (num > 0)
{
result.insert(result.begin(), alph[num % 58]);
num /= 58;
}
for (uint8_t b : data)
{
if (b == 0x00)
result.insert(result.begin(), alph[0]);
else
break;
}
return result;
}
inline bool b58decode(const std::string& s, std::vector<uint8_t>& out)
{
const auto& alph = b58_alphabet();
std::unordered_map<char, int> index;
for (size_t i = 0; i < alph.size(); ++i)
index[alph[i]] = int(i);
uint64_t num = 0;
for (char c : s)
{
if (index.find(c) == index.end())
return false;
num = num * 58 + index[c];
}
std::vector<uint8_t> tmp;
while (num > 0)
{
tmp.insert(tmp.begin(), static_cast<uint8_t>(num & 0xFF));
num >>= 8;
}
for (char c : s)
{
if (c == alph[0])
tmp.insert(tmp.begin(), 0x00);
else
break;
}
out = tmp;
return true;
}
inline std::string encodeBase58(const std::string& input) noexcept
{
return b58encode(std::vector<uint8_t>(input.begin(), input.end()));
}
inline bool decodeBase58(const std::string& input, std::string& output)
{
std::vector<uint8_t> data;
if (!b58decode(input, data)) return false;
output.assign(data.begin(), data.end());
return true;
}
// -------------------------
// Convenience string-based interface
// -------------------------
// Encode std::string (raw bytes) to Base16 string
// Returns encoded string
inline std::string encodeBase16(const std::string& input) noexcept
{
std::vector<uint8_t> data(input.begin(), input.end());
return b16encode(data);
}
// Decode Base16 string to std::string (raw bytes)
// Returns success flag, output in 'output'
inline bool decodeBase16(const std::string& input, std::string& output, bool casefold = false)
{
std::vector<uint8_t> data;
if (!b16decode(input, data, casefold))
return false;
output.assign(data.begin(), data.end());
return true;
}
// Encode std::string (raw bytes) to Base85 string
// Returns encoded string
inline std::string encodeBase85(const std::string& input, bool pad = false) noexcept
{
std::vector<uint8_t> data(input.begin(), input.end());
return b85encode(data, pad);
}
// Decode Base85 string to std::string (raw bytes)
// Returns success flag, output in 'output'
inline bool decodeBase85(const std::string& input, std::string& output)
{
std::vector<uint8_t> data;
if (!b85decode(input, data))
return false;
output.assign(data.begin(), data.end());
return true;
}
// -------------------------
// Base62 encoding/decoding (Python source: base62.py)
// -------------------------
inline const std::string& b62_charset_default()
{
static const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return charset;
}
inline int b62_char_value(char ch, const std::string& charset)
{
auto pos = charset.find(ch);
if (pos == std::string::npos)
return -1;
return static_cast<int>(pos);
}
inline std::string b62encode(uint64_t num, const std::string& charset = b62_charset_default())
{
// From encode() function
std::string result;
if (num == 0)
return "0";
while (num > 0)
{
result.insert(result.begin(), charset[num % 62]);
num /= 62;
}
return result;
}
inline std::string encodeBase62(const std::string& input) noexcept
{
// From encodebytes() function
const std::string& charset = b62_charset_default();
std::vector<uint8_t> barray(input.begin(), input.end());
int leading_zeros = 0;
for (auto b : barray)
{
if (b != 0)
break;
leading_zeros++;
}
int n = static_cast<int>(leading_zeros / (charset.size() - 1));
int r = static_cast<int>(leading_zeros % (charset.size() - 1));
std::string zero_padding(n, '0');
zero_padding += std::string(n, charset.back());
if (r)
zero_padding += "0" + std::string(1, charset[r]);
if (leading_zeros == static_cast<int>(barray.size()))
return zero_padding;
uint64_t value = 0;
for (uint8_t b : barray)
value = (value << 8) | b;
return zero_padding + b62encode(value, charset);
}
inline uint64_t b62decode(const std::string& s, const std::string& charset = b62_charset_default())
{
// From decode() function
uint64_t value = 0;
for (char ch : s)
{
int v = b62_char_value(ch, charset);
if (v < 0)
return 0; // indicates failure
value = value * 62 + v;
}
return value;
}
inline bool decodeBase62(const std::string& input, std::string& output)
{
// From decodebytes() function
const std::string& charset = b62_charset_default();
size_t i = 0;
std::vector<uint8_t> result;
while (i + 1 < input.size() && input[i] == '0')
{
int count = b62_char_value(input[i + 1], charset);
if (count < 0)
return false;
result.insert(result.end(), count, 0x00);
i += 2;
}
if (i >= input.size())
{
output.assign(result.begin(), result.end());
return true;
}
uint64_t decoded = b62decode(input.substr(i), charset);
std::vector<uint8_t> temp;
while (decoded > 0)
{
temp.push_back(decoded & 0xFF);
decoded >>= 8;
}
std::reverse(temp.begin(), temp.end());
result.insert(result.end(), temp.begin(), temp.end());
output.assign(result.begin(), result.end());
return true;
}
// -------------------------
// Base91 encoding/decoding (Python source: encode/decode functions)
// -------------------------
inline const std::string& b91_alphabet()
{
static const std::string alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[\\]^_`{|}~\"";
return alphabet;
}
inline bool encodeBase91(const std::string& input, std::string& output) noexcept
{
// From encode(bindata)
const std::string& alphabet = b91_alphabet();
uint32_t b = 0;
int n = 0;
output.clear();
for (uint8_t byte : input)
{
b |= static_cast<uint32_t>(byte) << n;
n += 8;
if (n > 13)
{
uint32_t v = b & 8191;
if (v > 88)
{
b >>= 13;
n -= 13;
}
else
{
v = b & 16383;
b >>= 14;
n -= 14;
}
output += alphabet[v % 91];
output += alphabet[v / 91];
}
}
if (n)
{
output += alphabet[b % 91];
if (n > 7 || b > 90)
output += alphabet[b / 91];
}
return true;
}
inline bool decodeBase91(const std::string& input, std::string& output) noexcept
{
// From decode(encoded_str)
const std::string& alphabet = b91_alphabet();
std::unordered_map<char, int> decode_table;
for (size_t i = 0; i < alphabet.size(); ++i)
decode_table[alphabet[i]] = static_cast<int>(i);
int v = -1;
uint32_t b = 0;
int n = 0;
std::vector<uint8_t> result;
for (char c : input)
{
if (decode_table.find(c) == decode_table.end())
continue;
int val = decode_table[c];
if (v < 0)
v = val;
else
{
v += val * 91;
b |= v << n;
n += (v & 8191) > 88 ? 13 : 14;
while (n >= 8)
{
result.push_back(b & 255);
b >>= 8;
n -= 8;
}
v = -1;
}
}
if (v != -1)
result.push_back((b | (v << n)) & 255);
output.assign(result.begin(), result.end());
return true;
}
} // namespace basecodec
#endif // BASECODEC_HPP