// 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. namespace ircd::lex { struct to_float_policy; struct to_double_policy; struct to_long_double_policy; struct from_float_policy; struct from_double_policy; struct from_long_double_policy; template using rule_to = spirit::qi::rule; template using rule_from = spirit::karma::rule; 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); } //TODO: XXX try removing once karma rules are optimized for globals #define RULE_INIT_PRIO \ __attribute__((init_priority(65535))), apply_to=variable(is_global) #pragma GCC visibility push(internal) namespace ircd::lex { 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 clang attribute push(RULE_INIT_PRIO) extern const rule_to to_bool, is_bool; extern const rule_to to_int8_t, is_int8_t; extern const rule_to to_uint8_t, is_uint8_t; extern const rule_to to_short, is_short; extern const rule_to to_ushort, is_ushort; extern const rule_to to_int, is_int; extern const rule_to to_uint, is_uint; extern const rule_to to_long, is_long; extern const rule_to to_ulong, is_ulong; extern const rule_to to_float, is_float; extern const rule_to to_double, is_double; extern const rule_to to_long_double, is_long_double; #pragma clang attribute pop } #pragma GCC visibility pop 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 { constexpr bool truncation { false }; mutable_buffer buf { 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, 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(std::nothrow, start, stop, rule); } template [[noreturn, gnu::noinline, gnu::cold]] void ircd::lex::throw_error(const rule &r, const exception &e) { throw bad_lex_cast { "Invalid lexical conversion of %s (%s).", r.name(), demangle(typeid(T).name()), }; } // // bool // decltype(ircd::lex::from_bool) ircd::lex::from_bool { spirit::karma::bool_ ,"boolean" }; decltype(ircd::lex::to_bool) ircd::lex::to_bool { spirit::expect[spirit::qi::bool_] ,"boolean" }; decltype(ircd::lex::is_bool) ircd::lex::is_bool { &spirit::qi::bool_ ,"boolean" }; template<> ircd::string_view ircd::lex_cast(bool i, const mutable_buffer &buf) { return lex::cast(buf, i); } template<> bool ircd::lex_cast(const string_view &s) { const bool literal[2] { s == "true", s == "false", }; // Handle the common cases early in this frame before call. if(likely(literal[0] | literal[1])) return literal[0]; return lex::cast(s); } template<> bool ircd::lex_castable(const string_view &s) noexcept { const bool literal[2] { s == "true", s == "false", }; // Handle the common cases early in this frame before call. if(likely(literal[0] | literal[1])) return true; return lex::test(s); } // // int8_t // decltype(ircd::lex::from_int8_t) ircd::lex::from_int8_t { spirit::karma::short_ ,"signed byte" }; decltype(ircd::lex::to_int8_t) ircd::lex::to_int8_t { spirit::expect[spirit::qi::short_] ,"signed byte" }; decltype(ircd::lex::is_int8_t) ircd::lex::is_int8_t { &spirit::qi::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 { return lex::test(s); } // // uint8_t // decltype(ircd::lex::from_uint8_t) ircd::lex::from_uint8_t { spirit::karma::ushort_ ,"unsigned byte" }; decltype(ircd::lex::to_uint8_t) ircd::lex::to_uint8_t { spirit::expect[spirit::qi::ushort_] ,"unsigned byte" }; decltype(ircd::lex::is_uint8_t) ircd::lex::is_uint8_t { &spirit::qi::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 { return lex::test(s); } // // short // decltype(ircd::lex::from_short) ircd::lex::from_short { spirit::karma::short_ ,"signed short integer" }; decltype(ircd::lex::to_short) ircd::lex::to_short { spirit::expect[spirit::qi::short_] ,"signed short integer" }; decltype(ircd::lex::is_short) ircd::lex::is_short { &spirit::qi::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 { return lex::test(s); } // // ushort // decltype(ircd::lex::from_ushort) ircd::lex::from_ushort { spirit::karma::ushort_ ,"unsigned short integer" }; decltype(ircd::lex::to_ushort) ircd::lex::to_ushort { spirit::expect[spirit::qi::ushort_] ,"unsigned short integer" }; decltype(ircd::lex::is_ushort) ircd::lex::is_ushort { &spirit::qi::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 { return lex::test(s); } // // signed // decltype(ircd::lex::from_int) ircd::lex::from_int { spirit::karma::int_ ,"signed integer" }; decltype(ircd::lex::to_int) ircd::lex::to_int { spirit::expect[spirit::qi::int_] ,"signed integer" }; decltype(ircd::lex::is_int) ircd::lex::is_int { &spirit::qi::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 { return lex::test(s); } // // unsigned // decltype(ircd::lex::from_uint) ircd::lex::from_uint { spirit::karma::uint_ ,"unsigned integer" }; decltype(ircd::lex::to_uint) ircd::lex::to_uint { spirit::expect[spirit::qi::uint_] ,"unsigned integer" }; decltype(ircd::lex::is_uint) ircd::lex::is_uint { &spirit::qi::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) noexcept { return lex::test(s); } // // long // decltype(ircd::lex::from_long) ircd::lex::from_long { spirit::karma::long_ ,"long integer" }; decltype(ircd::lex::to_long) ircd::lex::to_long { spirit::expect[spirit::qi::long_] ,"long integer" }; decltype(ircd::lex::is_long) ircd::lex::is_long { &spirit::qi::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 { return lex::test(s); } // // ulong // decltype(ircd::lex::from_ulong) ircd::lex::from_ulong { spirit::karma::ulong_ ,"long unsigned integer" }; decltype(ircd::lex::to_ulong) ircd::lex::to_ulong { spirit::expect[spirit::qi::ulong_] ,"long unsigned integer" }; decltype(ircd::lex::is_ulong) ircd::lex::is_ulong { &spirit::qi::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) noexcept { 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::from_float) ircd::lex::from_float { spirit::karma::real_generator{} ,"single floating point precision" }; decltype(ircd::lex::to_float) ircd::lex::to_float { spirit::expect[spirit::qi::real_parser{}] ,"single floating point precision" }; decltype(ircd::lex::is_float) ircd::lex::is_float { &spirit::qi::real_parser{} ,"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 { 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::from_double) ircd::lex::from_double { spirit::karma::real_generator{} ,"double floating point precision" }; decltype(ircd::lex::to_double) ircd::lex::to_double { spirit::expect[spirit::qi::real_parser{}] ,"double floating point precision" }; decltype(ircd::lex::is_double) ircd::lex::is_double { &spirit::qi::real_parser{} ,"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 { 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::from_long_double) ircd::lex::from_long_double { spirit::karma::real_generator{} ,"long double floating point precision" }; decltype(ircd::lex::to_long_double) ircd::lex::to_long_double { spirit::expect[spirit::qi::real_parser{}] ,"long double floating point precision" }; decltype(ircd::lex::is_long_double) ircd::lex::is_long_double { &spirit::qi::real_parser{} ,"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 { return lex::test(s); } // // hours // template<> ircd::string_view ircd::lex_cast(hours i, const mutable_buffer &buf) { return lex::cast(buf, i.count()); } template<> ircd::hours 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); } // // minutes // template<> ircd::string_view ircd::lex_cast(minutes i, const mutable_buffer &buf) { return lex::cast(buf, i.count()); } template<> ircd::minutes 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); } // // seconds // template<> ircd::string_view ircd::lex_cast(seconds i, const mutable_buffer &buf) { return lex::cast(buf, i.count()); } template<> ircd::seconds 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); } // // 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); }