// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 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. /// !!! NOTE !!! /// /// Many functions implemented here need to be replaced with karma generators /// similar to ircd::fmt. Both the boost and std lexical conversions are an /// order of magnitude slower than the formal generators. Some tokenizations /// can also be replaced. /// #include // // misc util // // // Human readable time suite // ircd::string_view ircd::pretty_nanoseconds(const mutable_buffer &out, const long double &ns) { static const std::array unit { "nanoseconds", "microseconds", "milliseconds", "seconds", "minutes", "hours", "days", }; auto pos(0); long double val(ns); // nanoseconds -> microseconds if(val > 1000.0) { val /= 1000; ++pos; } else goto done; // microseconds -> milliseconds if(val > 1000.0) { val /= 1000; ++pos; } else goto done; // milliseconds -> seconds if(val > 1000.0) { val /= 1000; ++pos; } else goto done; // seconds -> minutes if(val > 60.0) { val /= 60; ++pos; } else goto done; // minutes -> hours if(val > 60.0) { val /= 60; ++pos; } else goto done; // hours -> days if(val > 12.0) { val /= 12; ++pos; } else goto done; done: return fmt::sprintf { out, "%.2lf %s", val, unit.at(pos) }; } // // Human readable space suite // std::string ircd::pretty_only(const human_readable_size &value) { return util::string(32, [&value] (const mutable_buffer &out) { return pretty_only(out, value); }); } ircd::string_view ircd::pretty_only(const mutable_buffer &out, const human_readable_size &value) try { return fmt::sprintf { out, "%.2lf %s", std::get(value), std::get(value) }; } catch(const std::out_of_range &e) { return fmt::sprintf { out, "%lu B", std::get(value) }; } std::string ircd::pretty(const human_readable_size &value) { return util::string(64, [&value] (const mutable_buffer &out) { return pretty(out, value); }); } ircd::string_view ircd::pretty(const mutable_buffer &out, const human_readable_size &value) try { return fmt::sprintf { out, "%.2lf %s (%lu)", std::get(value), std::get(value), std::get(value) }; } catch(const std::out_of_range &e) { return fmt::sprintf { out, "%lu B", std::get(value) }; } ircd::human_readable_size ircd::iec(const uint64_t &value) { static const std::array unit { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; auto pos(0); long double v(value); for(; v > 1024.0; v /= 1024.0, ++pos); return { value, v, unit.at(pos) }; } ircd::human_readable_size ircd::si(const uint64_t &value) { static const std::array unit { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; auto pos(0); long double v(value); for(; v > 1000.0; v /= 1000.0, ++pos); return { value, v, unit.at(pos) }; } // // binary <-> hex suite // std::string ircd::u2a(const const_buffer &in) { return string(size(in) * 2, [&in] (const mutable_buffer &out) { return u2a(out, in); }); } ircd::string_view ircd::u2a(const mutable_buffer &out, const const_buffer &in) { char *p(data(out)); for(size_t i(0); i < size(in) && p + 2 <= end(out); ++i) { char tmp[3]; ::snprintf(tmp, sizeof(tmp), "%02x", in[i]); *p++ = tmp[0]; *p++ = tmp[1]; } return { data(out), p }; } ircd::const_buffer ircd::a2u(const mutable_buffer &out, const const_buffer &in) { const size_t len{size(in) / 2}; for(size_t i(0); i < len; ++i) { const char gl[3] { in[i * 2], in[i * 2 + 1], '\0' }; out[i] = strtol(gl, nullptr, 16); } return { data(out), len }; } // // lex_cast // namespace ircd { /// The static lex_cast ring buffers are each LEX_CAST_BUFSIZE bytes; /// Consider increasing if some lex_cast(str) has more characters. const size_t LEX_CAST_BUFSIZE {64}; /// This is a static "ring buffer" to simplify a majority of lex_cast uses. /// If the lex_cast has binary input and string output, and no user buffer /// is supplied, the next buffer here will be used instead. The returned /// string_view of data from this buffer is only valid for several more /// calls to lex_cast before it is overwritten. thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE]; thread_local uint lex_cast_cur; [[noreturn]] static void throw_bad_lex_cast(const boost::bad_lexical_cast &, const std::type_info &); template static string_view _lex_cast(const T &i, mutable_buffer buf); template static T _lex_cast(const string_view &s); } /// Internal template providing conversions from a number to a string; /// potentially using the ring buffer if no user buffer is supplied. template ircd::string_view ircd::_lex_cast(const T &i, mutable_buffer buf) try { using array = std::array; if(!buf) { buf = lex_cast_buf[lex_cast_cur++]; lex_cast_cur %= LEX_CAST_BUFS; } assert(size(buf) >= N); auto &a(*reinterpret_cast(data(buf))); a = boost::lexical_cast(i); return { data(buf), strnlen(data(buf), size(buf)) }; } catch(const boost::bad_lexical_cast &e) { throw_bad_lex_cast(e, typeid(T)); } /// Internal template providing conversions from a string to a number; /// the native object is returned directly; no ring buffer is consumed. template T ircd::_lex_cast(const string_view &s) try { return boost::lexical_cast(data(s), size(s)); } catch(const boost::bad_lexical_cast &e) { throw_bad_lex_cast(e, typeid(T)); } void ircd::throw_bad_lex_cast(const boost::bad_lexical_cast &e, const std::type_info &t) { throw ircd::bad_lex_cast { "%s: %s", e.what(), demangle(t.name()) }; } template<> ircd::string_view ircd::lex_cast(bool i, const mutable_buffer &buf) { static const size_t MAX(8); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(int8_t i, const mutable_buffer &buf) { static const size_t MAX(8); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(uint8_t i, const mutable_buffer &buf) { static const size_t MAX(8); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(short i, const mutable_buffer &buf) { static const size_t MAX(8); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(ushort i, const mutable_buffer &buf) { static const size_t MAX(8); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(int i, const mutable_buffer &buf) { static const size_t MAX(16); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(uint i, const mutable_buffer &buf) { static const size_t MAX(16); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(long i, const mutable_buffer &buf) { static const size_t MAX(32); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(ulong i, const mutable_buffer &buf) { static const size_t MAX(32); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(double i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(long double i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i, buf); } template<> ircd::string_view ircd::lex_cast(nanoseconds i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i.count(), buf); } template<> ircd::string_view ircd::lex_cast(microseconds i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i.count(), buf); } template<> ircd::string_view ircd::lex_cast(milliseconds i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i.count(), buf); } template<> ircd::string_view ircd::lex_cast(seconds i, const mutable_buffer &buf) { static const size_t MAX(64); return _lex_cast(i.count(), buf); } template<> bool ircd::lex_cast(const string_view &s) { return s == "true"? true: s == "false"? false: _lex_cast(s); } template<> int8_t ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> uint8_t ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> short ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> unsigned short ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> int ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> unsigned int ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> long ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> unsigned long ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> double ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> long double ircd::lex_cast(const string_view &s) { return _lex_cast(s); } template<> ircd::nanoseconds ircd::lex_cast(const string_view &s) { return std::chrono::duration>(_lex_cast(s)); } template<> ircd::microseconds ircd::lex_cast(const string_view &s) { return std::chrono::duration>(_lex_cast(s)); } template<> ircd::milliseconds ircd::lex_cast(const string_view &s) { return std::chrono::duration>(_lex_cast(s)); } template<> ircd::seconds ircd::lex_cast(const string_view &s) { return std::chrono::duration>(_lex_cast(s)); } template<> bool ircd::try_lex_cast(const string_view &s) { bool i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { int8_t i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { uint8_t i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { short i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { ushort i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { int i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { unsigned int i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { long i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { unsigned long i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { double i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { long double i; return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { time_t i; //TODO: XXX return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { time_t i; //TODO: XXX return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { time_t i; //TODO: XXX return boost::conversion::try_lexical_convert(s, i); } template<> bool ircd::try_lex_cast(const string_view &s) { time_t i; //TODO: XXX return boost::conversion::try_lexical_convert(s, i); } /////////////////////////////////////////////////////////////////////////////// // // ircd/stringops.h // std::string ircd::replace(const string_view &s, const char &before, const string_view &after) { const auto occurs { std::count(begin(s), end(s), before) }; const size_t size { occurs? s.size() + (occurs * after.size()): s.size() - occurs }; return string(size, [&s, &before, &after] (const mutable_buffer &buf) { char *p{begin(buf)}; std::for_each(begin(s), end(s), [&before, &after, &p] (const char &c) { if(c == before) { memcpy(p, after.data(), after.size()); p += after.size(); } else *p++ = c; }); return std::distance(begin(buf), p); }); }