#pragma once #ifndef BASECODEC_HPP #define BASECODEC_HPP #include #include #include #include #include #include #include #include #include //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& data) { std::ostringstream oss; oss << std::uppercase << std::hex; for (auto byte : data) oss << std::setw(2) << std::setfill('0') << static_cast(byte); return oss.str(); } // Decode from hex, returns success status; outputs bytes in 'out' inline bool b16decode(const std::string& s, std::vector& out, bool casefold = false) { std::string str = s; if (casefold) { for (auto& c : str) c = std::toupper(static_cast(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((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& _b85chars2() { static std::vector 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& 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 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& out) { const auto& alph = _b85alphabet(); static std::array dec; static bool init = false; if (!init) { dec.fill(-1); for (size_t i = 0; i < alph.size(); ++i) dec[static_cast(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(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& 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& out) { const auto& alph = b58_alphabet(); std::unordered_map 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 tmp; while (num > 0) { tmp.insert(tmp.begin(), static_cast(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(input.begin(), input.end())); } inline bool decodeBase58(const std::string& input, std::string& output) { std::vector 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 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 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 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 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(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 barray(input.begin(), input.end()); int leading_zeros = 0; for (auto b : barray) { if (b != 0) break; leading_zeros++; } int n = static_cast(leading_zeros / (charset.size() - 1)); int r = static_cast(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(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 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 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(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 decode_table; for (size_t i = 0; i < alphabet.size(); ++i) decode_table[alphabet[i]] = static_cast(i); int v = -1; uint32_t b = 0; int n = 0; std::vector 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