// The Construct // // Copyright (C) The Construct Developers, Authors & Contributors // Copyright (C) 2016-2020 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. namespace ircd::b58 { [[gnu::visibility("internal")]] extern const string_view dict; static thread_local char conv_tmp_buf[64_KiB]; } decltype(ircd::b58::dict) ircd::b58::dict { "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"_sv }; // // Conversion convenience suite // ircd::string_view ircd::b58::fromb64(const mutable_buffer &out, const string_view &in) { if(unlikely(b64::decode_size(in) > size(conv_tmp_buf))) throw error { "String too large for conversion at this time." }; return b58::encode(out, b64::decode(conv_tmp_buf, in)); } ircd::string_view ircd::b58::tob64_unpadded(const mutable_buffer &out, const string_view &in) { if(unlikely(b58::decode_size(in) > size(conv_tmp_buf))) throw error { "String too large for conversion at this time." }; return b64::encode_unpadded(out, b58::decode(conv_tmp_buf, in)); } ircd::string_view ircd::b58::tob64(const mutable_buffer &out, const string_view &in) { if(unlikely(b58::decode_size(in) > size(conv_tmp_buf))) throw error { "String too large for conversion at this time." }; return b64::encode(out, b58::decode(conv_tmp_buf, in)); } // // Base58 decode // ircd::const_buffer ircd::b58::decode(const mutable_buffer &buf, const string_view &in) { auto p(begin(in)); size_t zeroes(0); for(; p != end(in) && *p == '1'; ++p) ++zeroes; const mutable_buffer out { data(buf) + zeroes, std::min(b58::decode_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(dict.find(*p)); if(unlikely(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 const_buffer { begin(buf), it + length }; } // // Base58 encode // ircd::string_view ircd::b58::encode(const mutable_buffer &buf, const const_buffer &in) noexcept { 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(b58::encode_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 string_view { begin(buf), std::transform(it, it + length, it, [] (const uint8_t &in) { return dict.at(in); }) }; }