mirror of
https://github.com/matrix-construct/construct
synced 2025-01-14 00:34:18 +01:00
ircd: Add std::chrono lex_cast; cleanup lex_cast buffer argument related.
This commit is contained in:
parent
0be93870ea
commit
cdcd2e2fb9
2 changed files with 155 additions and 78 deletions
|
@ -38,7 +38,7 @@ namespace ircd
|
||||||
template<class T> T lex_cast(const string_view &);
|
template<class T> T lex_cast(const string_view &);
|
||||||
|
|
||||||
// User supplied destination buffer
|
// User supplied destination buffer
|
||||||
template<class T> string_view lex_cast(T, char *const &buf, const size_t &max);
|
template<class T> string_view lex_cast(T, const mutable_buffer &buf);
|
||||||
|
|
||||||
// Circular static thread_local buffer
|
// Circular static thread_local buffer
|
||||||
const size_t LEX_CAST_BUFS {256}; // plenty
|
const size_t LEX_CAST_BUFS {256}; // plenty
|
||||||
|
@ -77,6 +77,10 @@ namespace ircd
|
||||||
template<> bool try_lex_cast<uint8_t>(const string_view &);
|
template<> bool try_lex_cast<uint8_t>(const string_view &);
|
||||||
template<> bool try_lex_cast<int8_t>(const string_view &);
|
template<> bool try_lex_cast<int8_t>(const string_view &);
|
||||||
template<> bool try_lex_cast<bool>(const string_view &);
|
template<> bool try_lex_cast<bool>(const string_view &);
|
||||||
|
template<> bool try_lex_cast<seconds>(const string_view &);
|
||||||
|
template<> bool try_lex_cast<milliseconds>(const string_view &);
|
||||||
|
template<> bool try_lex_cast<microseconds>(const string_view &);
|
||||||
|
template<> bool try_lex_cast<nanoseconds>(const string_view &);
|
||||||
|
|
||||||
template<> std::string &lex_cast(std::string &); // trivial
|
template<> std::string &lex_cast(std::string &); // trivial
|
||||||
template<> std::string lex_cast(const std::string &); // trivial
|
template<> std::string lex_cast(const std::string &); // trivial
|
||||||
|
@ -93,21 +97,29 @@ namespace ircd
|
||||||
template<> uint8_t lex_cast(const string_view &);
|
template<> uint8_t lex_cast(const string_view &);
|
||||||
template<> int8_t lex_cast(const string_view &);
|
template<> int8_t lex_cast(const string_view &);
|
||||||
template<> bool lex_cast(const string_view &);
|
template<> bool lex_cast(const string_view &);
|
||||||
|
template<> seconds lex_cast(const string_view &);
|
||||||
|
template<> milliseconds lex_cast(const string_view &);
|
||||||
|
template<> microseconds lex_cast(const string_view &);
|
||||||
|
template<> nanoseconds lex_cast(const string_view &);
|
||||||
|
|
||||||
template<> string_view lex_cast(const std::string &, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(const std::string &, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(const std::string_view &, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(const std::string_view &, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(const string_view &, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(const string_view &, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(long double, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(seconds, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(double, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(milliseconds, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(ulong, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(microseconds, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(long, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(nanoseconds, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(uint, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(long double, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(int, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(double, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(ushort, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(ulong, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(short, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(long, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(uint8_t, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(uint, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(int8_t, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(int, const mutable_buffer &buf);
|
||||||
template<> string_view lex_cast(bool, char *const &buf, const size_t &max);
|
template<> string_view lex_cast(ushort, const mutable_buffer &buf);
|
||||||
|
template<> string_view lex_cast(short, const mutable_buffer &buf);
|
||||||
|
template<> string_view lex_cast(uint8_t, const mutable_buffer &buf);
|
||||||
|
template<> string_view lex_cast(int8_t, const mutable_buffer &buf);
|
||||||
|
template<> string_view lex_cast(bool, const mutable_buffer &buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a native number to a string. The returned value is a view of the
|
/// Convert a native number to a string. The returned value is a view of the
|
||||||
|
@ -117,7 +129,7 @@ template<class T>
|
||||||
ircd::string_view
|
ircd::string_view
|
||||||
ircd::lex_cast(const T &t)
|
ircd::lex_cast(const T &t)
|
||||||
{
|
{
|
||||||
return lex_cast<T>(t, nullptr, 0);
|
return lex_cast<T>(t, null_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conversion to an std::string creates a copy when the input is a
|
/// Conversion to an std::string creates a copy when the input is a
|
||||||
|
@ -192,11 +204,10 @@ template<>
|
||||||
__attribute__((warning("unnecessary lexical cast")))
|
__attribute__((warning("unnecessary lexical cast")))
|
||||||
inline ircd::string_view
|
inline ircd::string_view
|
||||||
ircd::lex_cast(const string_view &s,
|
ircd::lex_cast(const string_view &s,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
s.copy(buf, max);
|
s.copy(data(buf), size(buf));
|
||||||
return { buf, max };
|
return { data(buf), s.size() };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specialization of a string to string conversion to user's buffer;
|
/// Specialization of a string to string conversion to user's buffer;
|
||||||
|
@ -206,11 +217,10 @@ template<>
|
||||||
__attribute__((warning("unnecessary lexical cast")))
|
__attribute__((warning("unnecessary lexical cast")))
|
||||||
inline ircd::string_view
|
inline ircd::string_view
|
||||||
ircd::lex_cast(const std::string_view &s,
|
ircd::lex_cast(const std::string_view &s,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
s.copy(buf, max);
|
s.copy(data(buf), size(buf));
|
||||||
return { buf, max };
|
return { data(buf), s.size() };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specialization of a string to string conversion to user's buffer;
|
/// Specialization of a string to string conversion to user's buffer;
|
||||||
|
@ -220,11 +230,10 @@ template<>
|
||||||
__attribute__((warning("unnecessary lexical cast")))
|
__attribute__((warning("unnecessary lexical cast")))
|
||||||
inline ircd::string_view
|
inline ircd::string_view
|
||||||
ircd::lex_cast(const std::string &s,
|
ircd::lex_cast(const std::string &s,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
s.copy(buf, max);
|
s.copy(data(buf), size(buf));
|
||||||
return { buf, max };
|
return { data(buf), s.size() };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Template basis; if no specialization is matched there is no fallback here
|
/// Template basis; if no specialization is matched there is no fallback here
|
||||||
|
@ -232,8 +241,7 @@ template<class T>
|
||||||
__attribute__((error("unsupported lexical cast")))
|
__attribute__((error("unsupported lexical cast")))
|
||||||
ircd::string_view
|
ircd::string_view
|
||||||
ircd::lex_cast(T t,
|
ircd::lex_cast(T t,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
assert(0);
|
assert(0);
|
||||||
return {};
|
return {};
|
||||||
|
|
165
ircd/lexical.cc
165
ircd/lexical.cc
|
@ -161,23 +161,21 @@ ircd::token_count(const string_view &str,
|
||||||
size_t
|
size_t
|
||||||
ircd::tokens(const string_view &str,
|
ircd::tokens(const string_view &str,
|
||||||
const char &sep,
|
const char &sep,
|
||||||
char *const &buf,
|
const mutable_buffer &buf,
|
||||||
const size_t &max,
|
|
||||||
const token_view &closure)
|
const token_view &closure)
|
||||||
{
|
{
|
||||||
const char ssep[2] { sep, '\0' };
|
const char ssep[2] { sep, '\0' };
|
||||||
return tokens(str, ssep, buf, max, closure);
|
return tokens(str, ssep, buf, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ircd::tokens(const string_view &str,
|
ircd::tokens(const string_view &str,
|
||||||
const char *const &sep,
|
const char *const &sep,
|
||||||
char *const &buf,
|
const mutable_buffer &buf,
|
||||||
const size_t &max,
|
|
||||||
const token_view &closure)
|
const token_view &closure)
|
||||||
{
|
{
|
||||||
char *ptr(buf);
|
char *ptr(data(buf));
|
||||||
char *const stop(buf + max);
|
char *const stop(data(buf) + size(buf));
|
||||||
tokens(str, sep, [&closure, &ptr, &stop]
|
tokens(str, sep, [&closure, &ptr, &stop]
|
||||||
(const string_view &token)
|
(const string_view &token)
|
||||||
{
|
{
|
||||||
|
@ -191,7 +189,7 @@ ircd::tokens(const string_view &str,
|
||||||
closure(string_view(dest, token.size()));
|
closure(string_view(dest, token.size()));
|
||||||
});
|
});
|
||||||
|
|
||||||
return std::distance(buf, ptr);
|
return std::distance(data(buf), ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
@ -266,7 +264,7 @@ namespace ircd
|
||||||
thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE];
|
thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE];
|
||||||
thread_local uint lex_cast_cur;
|
thread_local uint lex_cast_cur;
|
||||||
|
|
||||||
template<size_t N, class T> static string_view _lex_cast(const T &i, char *buf, size_t max);
|
template<size_t N, class T> static string_view _lex_cast(const T &i, mutable_buffer buf);
|
||||||
template<class T> static T _lex_cast(const string_view &s);
|
template<class T> static T _lex_cast(const string_view &s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,8 +274,7 @@ template<size_t N,
|
||||||
class T>
|
class T>
|
||||||
ircd::string_view
|
ircd::string_view
|
||||||
ircd::_lex_cast(const T &i,
|
ircd::_lex_cast(const T &i,
|
||||||
char *buf,
|
mutable_buffer buf)
|
||||||
size_t max)
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using array = std::array<char, N>;
|
using array = std::array<char, N>;
|
||||||
|
@ -285,14 +282,13 @@ try
|
||||||
if(!buf)
|
if(!buf)
|
||||||
{
|
{
|
||||||
buf = lex_cast_buf[lex_cast_cur++];
|
buf = lex_cast_buf[lex_cast_cur++];
|
||||||
max = LEX_CAST_BUFSIZE;
|
|
||||||
lex_cast_cur %= LEX_CAST_BUFS;
|
lex_cast_cur %= LEX_CAST_BUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(max >= N);
|
assert(size(buf) >= N);
|
||||||
auto &a(*reinterpret_cast<array *>(buf));
|
auto &a(*reinterpret_cast<array *>(data(buf)));
|
||||||
a = boost::lexical_cast<array>(i);
|
a = boost::lexical_cast<array>(i);
|
||||||
return { buf, strnlen(buf, max) };
|
return { data(buf), strnlen(data(buf), size(buf)) };
|
||||||
}
|
}
|
||||||
catch(const boost::bad_lexical_cast &e)
|
catch(const boost::bad_lexical_cast &e)
|
||||||
{
|
{
|
||||||
|
@ -315,101 +311,122 @@ catch(const boost::bad_lexical_cast &e)
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(bool i,
|
ircd::lex_cast(bool i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(8);
|
static const size_t MAX(8);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(int8_t i,
|
ircd::lex_cast(int8_t i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(8);
|
static const size_t MAX(8);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(uint8_t i,
|
ircd::lex_cast(uint8_t i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(8);
|
static const size_t MAX(8);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(short i,
|
ircd::lex_cast(short i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(8);
|
static const size_t MAX(8);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(ushort i,
|
ircd::lex_cast(ushort i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(8);
|
static const size_t MAX(8);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(int i,
|
ircd::lex_cast(int i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(16);
|
static const size_t MAX(16);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(uint i,
|
ircd::lex_cast(uint i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(16);
|
static const size_t MAX(16);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(long i,
|
ircd::lex_cast(long i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(32);
|
static const size_t MAX(32);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(ulong i,
|
ircd::lex_cast(ulong i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(32);
|
static const size_t MAX(32);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(double i,
|
ircd::lex_cast(double i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(64);
|
static const size_t MAX(64);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> ircd::string_view
|
template<> ircd::string_view
|
||||||
ircd::lex_cast(long double i,
|
ircd::lex_cast(long double i,
|
||||||
char *const &buf,
|
const mutable_buffer &buf)
|
||||||
const size_t &max)
|
|
||||||
{
|
{
|
||||||
static const size_t MAX(64);
|
static const size_t MAX(64);
|
||||||
return _lex_cast<MAX>(i, buf, max);
|
return _lex_cast<MAX>(i, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> ircd::string_view
|
||||||
|
ircd::lex_cast(nanoseconds i,
|
||||||
|
const mutable_buffer &buf)
|
||||||
|
{
|
||||||
|
static const size_t MAX(64);
|
||||||
|
return _lex_cast<MAX>(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<MAX>(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<MAX>(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<MAX>(i.count(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> bool
|
template<> bool
|
||||||
|
@ -480,6 +497,30 @@ ircd::lex_cast(const string_view &s)
|
||||||
return _lex_cast<long double>(s);
|
return _lex_cast<long double>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> ircd::nanoseconds
|
||||||
|
ircd::lex_cast(const string_view &s)
|
||||||
|
{
|
||||||
|
return std::chrono::duration<time_t, std::ratio<1L, 1000000000L>>(_lex_cast<time_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> ircd::microseconds
|
||||||
|
ircd::lex_cast(const string_view &s)
|
||||||
|
{
|
||||||
|
return std::chrono::duration<time_t, std::ratio<1L, 1000000L>>(_lex_cast<time_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> ircd::milliseconds
|
||||||
|
ircd::lex_cast(const string_view &s)
|
||||||
|
{
|
||||||
|
return std::chrono::duration<time_t, std::ratio<1L, 1000L>>(_lex_cast<time_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> ircd::seconds
|
||||||
|
ircd::lex_cast(const string_view &s)
|
||||||
|
{
|
||||||
|
return std::chrono::duration<time_t, std::ratio<1L, 1L>>(_lex_cast<time_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
template<> bool
|
template<> bool
|
||||||
ircd::try_lex_cast<bool>(const string_view &s)
|
ircd::try_lex_cast<bool>(const string_view &s)
|
||||||
{
|
{
|
||||||
|
@ -557,6 +598,34 @@ ircd::try_lex_cast<long double>(const string_view &s)
|
||||||
return boost::conversion::try_lexical_convert(s, i);
|
return boost::conversion::try_lexical_convert(s, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> bool
|
||||||
|
ircd::try_lex_cast<ircd::nanoseconds>(const string_view &s)
|
||||||
|
{
|
||||||
|
time_t i; //TODO: XXX
|
||||||
|
return boost::conversion::try_lexical_convert(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> bool
|
||||||
|
ircd::try_lex_cast<ircd::microseconds>(const string_view &s)
|
||||||
|
{
|
||||||
|
time_t i; //TODO: XXX
|
||||||
|
return boost::conversion::try_lexical_convert(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> bool
|
||||||
|
ircd::try_lex_cast<ircd::milliseconds>(const string_view &s)
|
||||||
|
{
|
||||||
|
time_t i; //TODO: XXX
|
||||||
|
return boost::conversion::try_lexical_convert(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> bool
|
||||||
|
ircd::try_lex_cast<ircd::seconds>(const string_view &s)
|
||||||
|
{
|
||||||
|
time_t i; //TODO: XXX
|
||||||
|
return boost::conversion::try_lexical_convert(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// ircd/stringops.h
|
// ircd/stringops.h
|
||||||
|
|
Loading…
Reference in a new issue