// 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. /////////////////////////////////////////////////////////////////////////////// // // util/util.h // size_t ircd::util::size(std::ostream &s) { const auto cur(s.tellp()); s.seekp(0, std::ios::end); const auto ret(s.tellp()); s.seekp(cur, std::ios::beg); return ret; } /////////////////////////////////////////////////////////////////////////////// // // util/blackwhite.h // bool ircd::util::blackwhite::list::match(const string_view &a) const { const auto matcher { [&a](const string_view &b) noexcept { return a != b; } }; const bool black { this->black && !this->white? !tokens(this->black, delim, matcher): false }; const bool white { !black && this->white? !tokens(this->white, delim, matcher): false }; return !black && (white || !this->white); } /////////////////////////////////////////////////////////////////////////////// // // util/env.h // ircd::string_view ircd::util::getenv(const string_view &key) { thread_local char keystr[128]; if(unlikely(size(key) >= sizeof(keystr))) throw error { "getenv(): variable key is too long." }; // Ensure the key is null terminated for the std:: call. const size_t len { strlcpy(keystr, key) }; const string_view var { std::getenv(keystr) }; return var; } bool ircd::util::for_each_env(const string_view &prefix, const env_closure &closure) { return for_each_env([&prefix, &closure] (const auto &key, const auto &val) { if(!startswith(key, prefix)) return true; return closure(key, val); }); } bool ircd::util::for_each_env(const env_closure &closure) { assert(environ); for(char **env(environ); *env; ++env) { const auto &[key, val] { split(*env, '=') }; if(!closure(key, val)) return false; } return true; } /////////////////////////////////////////////////////////////////////////////// // // util/pretty.h // // // Human readable time suite // namespace ircd { inline namespace util { using pretty_time_formats = std::array; using pretty_time_element = std::tuple; template static string_view pretty(const mutable_buffer &, const T &, const uint &fmt); [[gnu::visibility("internal")]] extern const std::array pretty_time_unit; }} decltype(ircd::util::pretty_time_unit) ircd::util::pretty_time_unit {{ // fmt=0 fmt=1 { { "nanoseconds", "ns" }, 1000.0L }, { { "microseconds", "us" }, 1000.0L }, { { "milliseconds", "ms" }, 1000.0L }, { { "seconds", "s" }, 60.0L }, { { "minutes", "m" }, 60.0L }, { { "hours", "h" }, 24.0L }, { { "days", "d" }, 7.0L }, { { "weeks", "w" }, 4.0L }, { { "months", "M" }, 12.0L }, }}; template inline ircd::string_view ircd::util::pretty(const mutable_buffer &out, const T &dur, const uint &fmt) { static const auto &unit { pretty_time_unit }; const string_view &fmtstr { fmt == 1? "%.2lf%s"_sv: "%.2lf %s"_sv }; size_t pos(i); long double val(dur.count()); for(; val > std::get<1>(unit.at(pos)) && pos < unit.size() - 1; ++pos) val /= std::get<1>(unit.at(pos)); return fmt::sprintf { out, fmtstr, val, std::get<0>(unit.at(pos)).at(fmt) }; } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const nanoseconds &val, const uint &fmt) { return pretty<0>(out, val, fmt); } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const microseconds &val, const uint &fmt) { return pretty<1>(out, val, fmt); } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const milliseconds &val, const uint &fmt) { return pretty<2>(out, val, fmt); } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const seconds &val, const uint &fmt) { return pretty<3>(out, val, fmt); } // // Human readable space suite // decltype(ircd::util::pretty_fmt) ircd::util::pretty_fmt { "%.2lf %s (%lu)"_sv, "%.2lf %s"_sv, "%.2lf%s"_sv, string_view{}, }; std::string ircd::util::pretty(const human_readable_size &value, const uint &fmt) { return pretty(value, pretty_fmt[fmt]); } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const human_readable_size &value, const uint &fmt) { return pretty(out, pretty_fmt[fmt], value); } std::string ircd::util::pretty(const human_readable_size &value, const string_view &fmt) { return util::string(64, [&value, &fmt] (const mutable_buffer &out) { return pretty(out, fmt, value); }); } ircd::string_view ircd::util::pretty(const mutable_buffer &out, const string_view &fmt, const human_readable_size &value) try { return fmt::sprintf { out, fmt, std::get(value), std::get(value), std::get(value) }; } catch(const std::out_of_range &e) { return fmt::sprintf { out, "%lu", std::get(value) }; } ircd::human_readable_size ircd::util::si(const uint64_t &value) { static const std::array unit { " ", "K", "M", "G", "T", "P", "E" }; auto pos(0); long double v(value); for(; v > 1000.0L; v /= 1000.0L, ++pos); return { value, v, unit.at(pos) }; } ircd::human_readable_size ircd::util::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.0L; v /= 1024.0L, ++pos); return { value, v, unit.at(pos) }; } /////////////////////////////////////////////////////////////////////////////// // // util/string.h // /// Close over the common pattern to write directly into a post-C++11 standard /// string through the data() member requiring a const_cast. Closure returns /// a view of the data actually written to the buffer. std::string ircd::util::string(const size_t &size, const string_closure_view &closure) { return string(size, [&closure] (const mutable_buffer &buffer) { return ircd::size(closure(buffer)); }); } /// Close over the common pattern to write directly into a post-C++11 standard /// string through the data() member requiring a const_cast. Closure returns /// the final size of the data written into the buffer. std::string ircd::util::string(const size_t &size, const string_closure_size &closure) { const size_t alloc_size { size & ~SHRINK_TO_FIT }; std::string ret(alloc_size, char{}); const mutable_buffer buf { mutable_cast(ret.data()), ret.size() }; const size_t consumed { closure(buf) }; assert(consumed <= buffer::size(buf)); data(buf)[consumed] = '\0'; ret.resize(consumed); if(size & SHRINK_TO_FIT) ret.shrink_to_fit(); return ret; } std::string ircd::util::string(const const_buffer &buf) { return string(data(buf), size(buf)); } std::string ircd::util::string(const char *const &buf, const size_t &size) { return std::string{buf, size}; } std::string ircd::util::string(const uint8_t *const &buf, const size_t &size) { return string(reinterpret_cast(buf), size); } /////////////////////////////////////////////////////////////////////////////// // // util/timer.h // ircd::util::timer::timer(const std::function &func) :timer{} { func(); stop(); } void ircd::util::timer::stop() { const auto now { !stopped()? clock::now(): start }; const auto elapsed { duration_cast(now - start) }; accumulator += elapsed; start = clock::time_point::min(); } void ircd::util::timer::cont() { const auto now { clock::now() }; const auto elapsed { !stopped()? duration_cast(now - start): decltype(accumulator)(0) }; accumulator += elapsed; start = now; } std::string ircd::util::timer::pretty(const int &fmt) const { return util::pretty(at(), fmt); } ircd::string_view ircd::util::timer::pretty(const mutable_buffer &out, const int &fmt) const { return util::pretty(out, at(), fmt); } /////////////////////////////////////////////////////////////////////////////// // // util/u2a.h // std::string ircd::util::u2a(const const_buffer &in) { return string(size(in) * 2, [&in] (const mutable_buffer &out) { return u2a(out, in); }); } ircd::string_view ircd::util::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", uint8_t(in[i])); *p++ = tmp[0]; *p++ = tmp[1]; } return { data(out), p }; } ircd::const_buffer ircd::util::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 }; } /////////////////////////////////////////////////////////////////////////////// // // util/unwind.h // ircd::util::unwind_defer::~unwind_defer() noexcept { [[clang::always_destroy]] static ios::descriptor descriptor { "ircd.unwind" }; ircd::dispatch { descriptor, ios::defer, std::move(func) }; } /////////////////////////////////////////////////////////////////////////////// // // util/what.h // /// Get what() from exception_ptr /// ircd::string_view ircd::util::what(const std::exception_ptr eptr) noexcept try { if(likely(eptr)) std::rethrow_exception(eptr); return {}; } catch(const std::exception &e) { return e.what(); } catch(...) { return {}; }