mirror of
https://github.com/matrix-construct/construct
synced 2024-11-25 08:12:37 +01:00
549 lines
10 KiB
C++
549 lines
10 KiB
C++
// Matrix Construct
|
|
//
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
//
|
|
// 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<string_view, 2>;
|
|
using pretty_time_element = std::tuple<pretty_time_formats, long double>;
|
|
|
|
template<size_t i,
|
|
class T>
|
|
static string_view pretty(const mutable_buffer &, const T &, const uint &fmt);
|
|
|
|
[[gnu::visibility("internal")]]
|
|
extern const std::array<pretty_time_element, 9> 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<size_t i,
|
|
class T>
|
|
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<long double>(value),
|
|
std::get<const string_view &>(value),
|
|
std::get<uint64_t>(value)
|
|
};
|
|
}
|
|
catch(const std::out_of_range &e)
|
|
{
|
|
return fmt::sprintf
|
|
{
|
|
out, "%lu",
|
|
std::get<uint64_t>(value)
|
|
};
|
|
}
|
|
|
|
ircd::human_readable_size
|
|
ircd::util::si(const uint64_t &value)
|
|
{
|
|
static const std::array<string_view, 7> 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<string_view, 7> 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<const char *>(buf), size);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// util/timer.h
|
|
//
|
|
|
|
ircd::util::timer::timer(const std::function<void ()> &func)
|
|
:timer{}
|
|
{
|
|
func();
|
|
stop();
|
|
}
|
|
|
|
void
|
|
ircd::util::timer::stop()
|
|
{
|
|
const auto now
|
|
{
|
|
!stopped()?
|
|
clock::now():
|
|
start
|
|
};
|
|
|
|
const auto elapsed
|
|
{
|
|
duration_cast<decltype(accumulator)>(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<decltype(accumulator)>(now - start):
|
|
decltype(accumulator)(0)
|
|
};
|
|
|
|
accumulator += elapsed;
|
|
start = now;
|
|
}
|
|
|
|
std::string
|
|
ircd::util::timer::pretty(const int &fmt)
|
|
const
|
|
{
|
|
return util::pretty(at<nanoseconds>(), fmt);
|
|
}
|
|
|
|
ircd::string_view
|
|
ircd::util::timer::pretty(const mutable_buffer &out,
|
|
const int &fmt)
|
|
const
|
|
{
|
|
return util::pretty(out, at<nanoseconds>(), 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 {};
|
|
}
|