diff --git a/include/ircd/base64.h b/include/ircd/base.h similarity index 50% rename from include/ircd/base64.h rename to include/ircd/base.h index da6d4352e..e7dd4358c 100644 --- a/include/ircd/base64.h +++ b/include/ircd/base.h @@ -9,10 +9,22 @@ // full license for this software is available in the LICENSE file. #pragma once -#define HAVE_IRCD_BASE64_H +#define HAVE_IRCD_BASE_H namespace ircd { + // Binary -> Base58 encode suite + constexpr size_t b58encode_size(const size_t &); + size_t b58encode_size(const const_raw_buffer &in); + string_view b58encode(const mutable_buffer &out, const const_raw_buffer &in); + std::string b58encode(const const_raw_buffer &in); + + // Base58 -> Binary decode suite + constexpr size_t b58decode_size(const size_t &); + size_t b58decode_size(const string_view &in); + const_raw_buffer b58decode(const mutable_raw_buffer &out, const string_view &in); + std::string b58decode(const string_view &in); + // Binary -> Base64 conversion suite string_view b64encode(const mutable_buffer &out, const const_raw_buffer &in); std::string b64encode(const const_raw_buffer &in); @@ -25,3 +37,27 @@ namespace ircd const_raw_buffer b64decode(const mutable_raw_buffer &out, const string_view &in); std::string b64decode(const string_view &in); } + +inline size_t +ircd::b58decode_size(const string_view &in) +{ + return b58decode_size(size(in)); +} + +constexpr size_t +ircd::b58decode_size(const size_t &in) +{ + return in * 733UL / 1000UL + 1UL; // log(58) / log(256), rounded up +} + +inline size_t +ircd::b58encode_size(const const_raw_buffer &in) +{ + return b58encode_size(size(in)); +} + +constexpr size_t +ircd::b58encode_size(const size_t &in) +{ + return in * 138UL / 100UL + 1UL; // log(256) / log(58), rounded up +} diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 29ed7b502..e9acc1dc1 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -216,6 +216,7 @@ namespace ircd #include "localee.h" #include "color.h" #include "lex_cast.h" +#include "base.h" #include "stringops.h" #include "tokens.h" #include "params.h" diff --git a/ircd/Makefile.am b/ircd/Makefile.am index d0225b3b4..285403641 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -72,6 +72,7 @@ libircd_la_SOURCES = \ ircd.cc \ json.cc \ lexical.cc \ + base.cc \ locale.cc \ logger.cc \ m/event.cc \ diff --git a/ircd/base64.cc b/ircd/base.cc similarity index 61% rename from ircd/base64.cc rename to ircd/base.cc index 5cff71f53..21382f769 100644 --- a/ircd/base64.cc +++ b/ircd/base.cc @@ -179,3 +179,131 @@ ircd::b64decode(const mutable_raw_buffer &out, assert(size_t(len) <= size(out)); return { data(out), size_t(len) }; } + +namespace ircd +{ + const auto &b58 + { + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"s + }; +} + +std::string +ircd::b58decode(const string_view &in) +{ + std::string ret(b58decode_size(in), char{}); + const mutable_raw_buffer buf + { + reinterpret_cast(const_cast(ret.data())), ret.size() + }; + + const auto decoded + { + b58decode(buf, in) + }; + + assert(size(decoded) <= ret.size()); + ret.resize(size(decoded)); + return ret; +} + +ircd::const_raw_buffer +ircd::b58decode(const mutable_raw_buffer &buf, + const string_view &in) +{ + auto p(begin(in)); + size_t zeroes(0); + for(; p != end(in) && *p == '1'; ++p) + ++zeroes; + + const mutable_raw_buffer out + { + data(buf) + zeroes, std::min(b58decode_size(in), size(buf) - zeroes) + }; + + assert(size(out) + zeroes <= size(buf)); + memset(data(out), 0, size(out)); + + size_t length(0); + for(size_t i(0); p != end(in); ++p, length = i, i = 0) + { + auto carry(b58.find(*p)); + if(carry == std::string::npos) + throw std::out_of_range("Invalid base58 character"); + + for(auto it(rbegin(out)); (carry || i < length) && it != rend(out); ++it, i++) + { + carry += 58 * (*it); + *it = carry % 256; + carry /= 256; + } + } + + auto it(begin(buf)); + assert(it + zeroes + length <= end(buf)); + for(; it != end(buf) && zeroes; *it++ = 0, --zeroes); + memmove(it, data(out) + (size(out) - length), length); + return { begin(buf), it + length }; +} + +std::string +ircd::b58encode(const const_raw_buffer &in) +{ + std::string ret(b58encode_size(in), char{}); + const mutable_buffer buf + { + const_cast(ret.data()), ret.size() + }; + + const auto encoded + { + b58encode(buf, in) + }; + + assert(size(encoded) <= ret.size()); + ret.resize(size(encoded)); + return ret; +} + +ircd::string_view +ircd::b58encode(const mutable_buffer &buf, + const const_raw_buffer &in) +{ + auto p(begin(in)); + size_t zeroes(0); + for(; p != end(in) && *p == 0; ++p) + ++zeroes; + + const mutable_buffer out + { + data(buf) + zeroes, std::min(b58encode_size(in), size(buf) - zeroes) + }; + + assert(size(out) + zeroes <= size(buf)); + memset(data(out), 0, size(out)); + + size_t length(0); + for(size_t i(0); p != end(in); ++p, length = i, i = 0) + { + size_t carry(*p); + for(auto it(rbegin(out)); (carry || i < length) && it != rend(out); ++it, i++) + { + carry += 256 * (*it); + *it = carry % 58; + carry /= 58; + } + } + + auto it(begin(buf)); + assert(it + zeroes + length <= end(buf)); + for(; it != end(buf) && zeroes; *it++ = '1', --zeroes); + memmove(it, data(out) + (size(out) - length), length); + return + { + begin(buf), std::transform(it, it + length, it, [] + (const uint8_t &in) + { + return b58.at(in); + }) + }; +}