diff --git a/include/ircd/lex_cast.h b/include/ircd/lex_cast.h index 36092786c..cce007fe6 100644 --- a/include/ircd/lex_cast.h +++ b/include/ircd/lex_cast.h @@ -18,22 +18,31 @@ namespace ircd { IRCD_EXCEPTION_HIDENAME(ircd::error, bad_lex_cast) + // Test if lex_cast will succeed template bool lex_castable(const string_view &) noexcept = delete; + // Convert from ascii to integral template T lex_cast(std::string &); template T lex_cast(const std::string &); template T lex_cast(const std::string_view &); template T lex_cast(const string_view &); - // User supplied destination buffer + // Convert from integral to ascii (user supplied destination buffer) template string_view lex_cast(T, const mutable_buffer &buf); - // Circular static thread_local buffer - extern const size_t LEX_CAST_BUFS; - extern const size_t LEX_CAST_BUFSIZE; + // Convert from integral to ascii (circular internal thread_local buffer) template string_view lex_cast(const T &t); } +// Internal convenience ring buffer related +namespace ircd +{ + constexpr size_t LEX_CAST_BUFS {256}, LEX_CAST_BUFSIZE {64}; + extern thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE]; + extern thread_local uint8_t lex_cast_buf_head; +} + +// Explicit linkages namespace ircd { template<> bool lex_castable(const string_view &) noexcept; // stub always true @@ -113,7 +122,7 @@ template inline ircd::string_view ircd::lex_cast(const T &t) { - return lex_cast(t, null_buffer); + return lex_cast(t, lex_cast_buf[lex_cast_buf_head++]); } /// Conversion to an std::string creates a copy when the input is a diff --git a/ircd/Makefile.am b/ircd/Makefile.am index 9efb5aeeb..ea8edceaa 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -283,7 +283,8 @@ js.lo: AM_CPPFLAGS := @JS_CPPFLAGS@ ${AM_CPPFLAGS} endif json.lo: AM_CPPFLAGS := ${SPIRIT_UNIT_CPPFLAGS} ${AM_CPPFLAGS} json.lo: AM_CXXFLAGS := ${SPIRIT_UNIT_CXXFLAGS} ${AM_CXXFLAGS} -lex_cast.lo: AM_CPPFLAGS := @BOOST_CPPFLAGS@ ${AM_CPPFLAGS} +lex_cast.lo: AM_CPPFLAGS := ${SPIRIT_UNIT_CPPFLAGS} ${AM_CPPFLAGS} +lex_cast.lo: AM_CXXFLAGS := ${SPIRIT_UNIT_CXXFLAGS} ${AM_CXXFLAGS} if OPENCL cl.lo: AM_CPPFLAGS := @OPENCL_CPPFLAGS@ ${AM_CPPFLAGS} endif diff --git a/ircd/lex_cast.cc b/ircd/lex_cast.cc index e6d84f25d..7ff6e0866 100644 --- a/ircd/lex_cast.cc +++ b/ircd/lex_cast.cc @@ -8,220 +8,210 @@ // 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 - -decltype(ircd::LEX_CAST_BUFS) -ircd::LEX_CAST_BUFS +namespace ircd::lex { - 64 // plenty -}; + struct to_float_policy; + struct to_double_policy; + struct to_long_double_policy; -decltype(ircd::LEX_CAST_BUFSIZE) -ircd::LEX_CAST_BUFSIZE -{ - 64 -}; + struct from_float_policy; + struct from_double_policy; + struct from_long_double_policy; -namespace ircd -{ - /// 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; + template + using rule_to = spirit::qi::rule + __attribute__((visibility("internal"))); - [[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); + template + using rule_from = spirit::karma::rule + __attribute__((visibility("internal"))); + + template + [[noreturn]] + static void throw_error(const rule &, const exception &e); + + template &r> + static bool test(const string_view &) noexcept; + + template &r> + static T cast(const string_view &); + + template &r> + static string_view cast(const mutable_buffer &, T); + + #pragma GCC visibility push(internal) + extern const rule_to to_bool; + extern const rule_to to_int8_t; + extern const rule_to to_uint8_t; + extern const rule_to to_short; + extern const rule_to to_ushort; + extern const rule_to to_int; + extern const rule_to to_uint; + extern const rule_to to_long; + extern const rule_to to_ulong; + extern const rule_to to_float; + extern const rule_to to_double; + extern const rule_to to_long_double; + #pragma GCC visibility pop + + #pragma GCC visibility push(internal) + extern const rule_from from_bool; + extern const rule_from from_int8_t; + extern const rule_from from_uint8_t; + extern const rule_from from_short; + extern const rule_from from_ushort; + extern const rule_from from_int; + extern const rule_from from_uint; + extern const rule_from from_long; + extern const rule_from from_ulong; + extern const rule_from from_float; + extern const rule_from from_double; + extern const rule_from from_long_double; + #pragma GCC visibility pop } -/// 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) +static_assert +( + ircd::LEX_CAST_BUFS == 256 && sizeof(ircd::lex_cast_buf_head) == 1, + "ircd::lex_cast ring buffer integer may not modulate as intended" +); + +/// Indicates the next buffer to be used in the ring. See below. +thread_local +decltype(ircd::lex_cast_buf_head) +ircd::lex_cast_buf_head +alignas(16); + +/// This is a static "ring buffer" to simplify a majority of lex_cast uses. +/// 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 +decltype(ircd::lex_cast_buf) +ircd::lex_cast_buf +alignas(64); + +template &rule> +[[gnu::always_inline]] +inline ircd::string_view +ircd::lex::cast(const mutable_buffer &out, + T in) try { - using array = std::array; - - if(!buf) + constexpr bool truncation { - buf = lex_cast_buf[lex_cast_cur++]; - lex_cast_cur %= LEX_CAST_BUFS; - } + false + }; - 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 + mutable_buffer buf { - "%s: %s", e.what(), demangle(t.name()) + out + }; + + const bool pass + { + ircd::generate + ( + buf, rule | spirit::eps, in + ) + }; + + assert(pass); + return string_view + { + data(out), data(buf) }; } +catch(const std::exception &e) +{ + throw_error(rule, e); + __builtin_unreachable(); +} + +template &rule> +[[gnu::always_inline]] +inline T +ircd::lex::cast(const string_view &s) +try +{ + T ret; + const char + *start(begin(s)), + *const stop(end(s)); + const bool pass + { + ircd::parse(start, stop, spirit::expect[rule], ret) + }; + + assert(pass); + return ret; +} +catch(const std::exception &e) +{ + throw_error(rule, e); + __builtin_unreachable(); +} + +template &rule> +[[gnu::always_inline]] +inline bool +ircd::lex::test(const string_view &s) +noexcept +{ + const char + *start(begin(s)), + *const stop(end(s)); + return ircd::parse(start, stop, &rule); +} + +template +[[noreturn, gnu::noinline, gnu::cold]] +void +ircd::lex::throw_error(const rule &r, + const exception &e) +{ + throw bad_lex_cast + { + "%s :%s", + e.what(), + demangle(typeid(T).name()) + }; +} + +// +// bool +// + +decltype(ircd::lex::to_bool) +ircd::lex::to_bool +{ + spirit::qi::bool_ + ,"boolean" +}; + +decltype(ircd::lex::from_bool) +ircd::lex::from_bool +{ + spirit::karma::bool_ + ,"boolean" +}; 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(float i, - const mutable_buffer &buf) -{ - static const size_t MAX(64); - 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); + return lex::cast(buf, i); } template<> bool @@ -229,223 +219,589 @@ 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<> float -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)); + lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - bool i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// int8_t +// + +decltype(ircd::lex::to_int8_t) +ircd::lex::to_int8_t +{ + spirit::qi::short_ + ,"signed byte" +}; + +decltype(ircd::lex::from_int8_t) +ircd::lex::from_int8_t +{ + spirit::karma::short_ + ,"signed byte" +}; + +template<> ircd::string_view +ircd::lex_cast(int8_t i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> int8_t +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - int8_t i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// uint8_t +// + +decltype(ircd::lex::to_uint8_t) +ircd::lex::to_uint8_t +{ + spirit::qi::ushort_ + ,"unsigned byte" +}; + +decltype(ircd::lex::from_uint8_t) +ircd::lex::from_uint8_t +{ + spirit::karma::ushort_ + ,"unsigned byte" +}; + +template<> ircd::string_view +ircd::lex_cast(uint8_t i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> uint8_t +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - uint8_t i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// short +// + +decltype(ircd::lex::to_short) +ircd::lex::to_short +{ + spirit::qi::short_ + ,"signed short integer" +}; + +decltype(ircd::lex::from_short) +ircd::lex::from_short +{ + spirit::karma::short_ + ,"signed short integer" +}; + +template<> ircd::string_view +ircd::lex_cast(short i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> short +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - short i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// ushort +// + +decltype(ircd::lex::to_ushort) +ircd::lex::to_ushort +{ + spirit::qi::ushort_ + ,"unsigned short integer" +}; + +decltype(ircd::lex::from_ushort) +ircd::lex::from_ushort +{ + spirit::karma::ushort_ + ,"unsigned short integer" +}; + +template<> ircd::string_view +ircd::lex_cast(ushort i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> ushort +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - ushort i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// signed +// + +decltype(ircd::lex::to_int) +ircd::lex::to_int +{ + spirit::qi::int_ + ,"signed integer" +}; + +decltype(ircd::lex::from_int) +ircd::lex::from_int +{ + spirit::karma::int_ + ,"signed integer" +}; + +template<> ircd::string_view +ircd::lex_cast(int i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> int +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - int i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// unsigned +// + +decltype(ircd::lex::to_uint) +ircd::lex::to_uint +{ + spirit::qi::uint_ + ,"unsigned integer" +}; + +decltype(ircd::lex::from_uint) +ircd::lex::from_uint +{ + spirit::karma::uint_ + ,"unsigned integer" +}; + +template<> ircd::string_view +ircd::lex_cast(uint i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> unsigned int +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool -ircd::lex_castable(const string_view &s) +ircd::lex_castable(const string_view &s) noexcept { - unsigned int i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// long +// + +decltype(ircd::lex::to_long) +ircd::lex::to_long +{ + spirit::qi::long_ + ,"long integer" +}; + +decltype(ircd::lex::from_long) +ircd::lex::from_long +{ + spirit::karma::long_ + ,"long integer" +}; + +template<> ircd::string_view +ircd::lex_cast(long i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> long +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - long i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// ulong +// + +decltype(ircd::lex::to_ulong) +ircd::lex::to_ulong +{ + spirit::qi::ulong_ + ,"long unsigned integer" +}; + +decltype(ircd::lex::from_ulong) +ircd::lex::from_ulong +{ + spirit::karma::ulong_ + ,"long unsigned integer" +}; + +template<> ircd::string_view +ircd::lex_cast(ulong i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> unsigned long +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool -ircd::lex_castable(const string_view &s) +ircd::lex_castable(const string_view &s) noexcept { - unsigned long i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// float +// + +struct [[gnu::visibility("internal")]] +ircd::lex::to_float_policy +:spirit::qi::real_policies +{ + static const bool + allow_leading_dot { false }, + allow_trailing_dot { false }, + expect_dot { false }; +}; + +struct [[gnu::visibility("internal")]] +ircd::lex::from_float_policy +:spirit::karma::real_policies +{ + +}; + +decltype(ircd::lex::to_float) +ircd::lex::to_float +{ + spirit::qi::real_parser{} + ,"single floating point precision" +}; + +decltype(ircd::lex::from_float) +ircd::lex::from_float +{ + spirit::karma::real_generator{} + ,"single floating point precision" +}; + +template<> ircd::string_view +ircd::lex_cast(float i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> float +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - float i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// double +// + +struct [[gnu::visibility("internal")]] +ircd::lex::to_double_policy +:spirit::qi::real_policies +{ + static const bool + allow_leading_dot { false }, + allow_trailing_dot { false }, + expect_dot { false }; +}; + +struct [[gnu::visibility("internal")]] +ircd::lex::from_double_policy +:spirit::karma::real_policies +{ + +}; + +decltype(ircd::lex::to_double) +ircd::lex::to_double +{ + spirit::qi::real_parser{} + ,"double floating point precision" +}; + +decltype(ircd::lex::from_double) +ircd::lex::from_double +{ + spirit::karma::real_generator{} + ,"double floating point precision" +}; + +template<> ircd::string_view +ircd::lex_cast(double i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> double +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - double i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// long double +// + +struct [[gnu::visibility("internal")]] +ircd::lex::to_long_double_policy +:spirit::qi::real_policies +{ + static const bool + allow_leading_dot { false }, + allow_trailing_dot { false }, + expect_dot { false }; +}; + +struct [[gnu::visibility("internal")]] +ircd::lex::from_long_double_policy +:spirit::karma::real_policies +{ + +}; + +decltype(ircd::lex::to_long_double) +ircd::lex::to_long_double +{ + spirit::qi::real_parser{} + ,"long double floating point precision" +}; + +decltype(ircd::lex::from_long_double) +ircd::lex::from_long_double +{ + spirit::karma::real_generator{} + ,"long double floating point precision" +}; + +template<> ircd::string_view +ircd::lex_cast(long double i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i); +} + +template<> long double +ircd::lex_cast(const string_view &s) +{ + return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - long double i; - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); } -template<> bool -ircd::lex_castable(const string_view &s) -noexcept +// +// seconds +// + +template<> ircd::string_view +ircd::lex_cast(seconds i, + const mutable_buffer &buf) { - time_t i; //TODO: XXX - return boost::conversion::try_lexical_convert(s, i); + return lex::cast(buf, i.count()); } -template<> bool -ircd::lex_castable(const string_view &s) -noexcept +template<> ircd::seconds +ircd::lex_cast(const string_view &s) { - time_t i; //TODO: XXX - return boost::conversion::try_lexical_convert(s, i); -} - -template<> bool -ircd::lex_castable(const string_view &s) -noexcept -{ - time_t i; //TODO: XXX - return boost::conversion::try_lexical_convert(s, i); + return std::chrono::duration> + ( + lex::cast(s) + ); } template<> bool ircd::lex_castable(const string_view &s) noexcept { - time_t i; //TODO: XXX - return boost::conversion::try_lexical_convert(s, i); + return lex::test(s); +} + +// +// milliseconds +// + +template<> ircd::string_view +ircd::lex_cast(milliseconds i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i.count()); +} + +template<> ircd::milliseconds +ircd::lex_cast(const string_view &s) +{ + return std::chrono::duration> + ( + lex::cast(s) + ); +} + +template<> bool +ircd::lex_castable(const string_view &s) +noexcept +{ + return lex::test(s); +} + +// +// microseconds +// + +template<> ircd::string_view +ircd::lex_cast(microseconds i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i.count()); +} + +template<> ircd::microseconds +ircd::lex_cast(const string_view &s) +{ + return std::chrono::duration> + ( + lex::cast(s) + ); +} + +template<> bool +ircd::lex_castable(const string_view &s) +noexcept +{ + return lex::test(s); +} + +// +// nanoseconds +// + +template<> ircd::string_view +ircd::lex_cast(nanoseconds i, + const mutable_buffer &buf) +{ + return lex::cast(buf, i.count()); +} + +template<> ircd::nanoseconds +ircd::lex_cast(const string_view &s) +{ + return std::chrono::duration> + ( + lex::cast(s) + ); +} + +template<> bool +ircd::lex_castable(const string_view &s) +noexcept +{ + return lex::test(s); }