diff --git a/docs/json-rpc.md b/docs/json-rpc.md index 00cf4d19de..387cd5ffca 100644 --- a/docs/json-rpc.md +++ b/docs/json-rpc.md @@ -57,12 +57,12 @@ struct glz::meta namespace rpc = glz::rpc; -auto main(int, char**) -> int { - rpc::server, - rpc::server_method_t<"bar", bar_params, bar_result>> +int main() { + rpc::server, + rpc::method<"bar", bar_params, bar_result>> server; - rpc::client, - rpc::client_method_t<"bar", bar_params, bar_result>> + rpc::client, + rpc::method<"bar", bar_params, bar_result>> client; // One long living callback per method for the server @@ -72,7 +72,7 @@ auto main(int, char**) -> int { // params.foo_b return foo_result{.foo_c = true, .foo_d = "new world"}; // Or return an error: - // return glz::unexpected(rpc::error(rpc::error_e::server_error_lower, "my error")); + // return glz::unexpected(rpc::error{rpc::error_e::server_error_lower, "my error"}); }); server.on<"bar">([](bar_params const& params) -> glz::expected { return bar_result{.bar_c = true, .bar_d = "new world"}; @@ -83,7 +83,7 @@ auto main(int, char**) -> int { auto [request_str, inserted] = client.request<"foo">( uuid, foo_params{.foo_a = 1337, .foo_b = "hello world"}, - [](glz::expected value, rpc::jsonrpc_id_type id) -> void { + [](glz::expected value, rpc::id_t id) -> void { // Access to value and/or id }); // request_str: R"({"jsonrpc":"2.0","method":"foo","params":{"foo_a":1337,"foo_b":"hello world"},"id":"42"})" diff --git a/include/glaze/binary/write.hpp b/include/glaze/binary/write.hpp index 191e01f455..98cc32c3e8 100644 --- a/include/glaze/binary/write.hpp +++ b/include/glaze/binary/write.hpp @@ -336,8 +336,8 @@ namespace glz using Key = typename T::first_type; constexpr uint8_t type = str_t ? 0 : (std::is_signed_v ? 0b000'01'000 : 0b000'10'000); - constexpr uint8_t byte_count = str_t ? 1 : sizeof(Key); - constexpr uint8_t tag = tag::object | type | (byte_count << 5); + constexpr uint8_t byte_cnt = str_t ? 1 : sizeof(Key); + constexpr uint8_t tag = tag::object | type | (byte_cnt << 5); dump_type(tag, args...); dump_compressed_int(1, args...); @@ -356,8 +356,8 @@ namespace glz using Key = typename T::key_type; constexpr uint8_t type = str_t ? 0 : (std::is_signed_v ? 0b000'01'000 : 0b000'10'000); - constexpr uint8_t byte_count = str_t ? 1 : sizeof(Key); - constexpr uint8_t tag = tag::object | type | (byte_count << 5); + constexpr uint8_t byte_cnt = str_t ? 1 : sizeof(Key); + constexpr uint8_t tag = tag::object | type | (byte_cnt << 5); dump_type(tag, args...); dump_compressed_int(value.size(), args...); @@ -391,8 +391,8 @@ namespace glz GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept { constexpr uint8_t type = 0; - constexpr uint8_t byte_count = 1; - constexpr uint8_t tag = tag::object | type | (byte_count << 5); + constexpr uint8_t byte_cnt = 1; + constexpr uint8_t tag = tag::object | type | (byte_cnt << 5); dump_type(tag, args...); using V = std::decay_t; @@ -480,8 +480,8 @@ namespace glz static constexpr auto N = std::tuple_size_v>; constexpr uint8_t type = 0; - constexpr uint8_t byte_count = sizeof(decltype(buffer[0])); - constexpr uint8_t tag = tag::object | type | (byte_count << 5); + constexpr uint8_t byte_cnt = sizeof(decltype(buffer[0])); + constexpr uint8_t tag = tag::object | type | (byte_cnt << 5); detail::dump_type(tag, buffer); detail::dump_compressed_int(buffer); diff --git a/include/glaze/core/common.hpp b/include/glaze/core/common.hpp index b2d83b6fa4..0755f70372 100644 --- a/include/glaze/core/common.hpp +++ b/include/glaze/core/common.hpp @@ -19,6 +19,7 @@ #include "glaze/core/meta.hpp" #include "glaze/util/bit_array.hpp" #include "glaze/util/expected.hpp" +#include "glaze/util/fixed_string.hpp" #include "glaze/util/for_each.hpp" #include "glaze/util/hash_map.hpp" #include "glaze/util/murmur.hpp" diff --git a/include/glaze/ext/jsonrpc.hpp b/include/glaze/ext/jsonrpc.hpp index f327d8bc3a..6b15d0da95 100644 --- a/include/glaze/ext/jsonrpc.hpp +++ b/include/glaze/ext/jsonrpc.hpp @@ -1,32 +1,13 @@ +// Glaze Library +// For the license information refer to glaze.hpp + #pragma once -#include #include #include #include -#include -#include -#include #include #include -#include -#include - -#if __cpp_lib_to_underlying -namespace util -{ - using std::to_underlying; -} -#else -namespace util -{ - template - [[nodiscard]] constexpr std::underlying_type_t to_underlying(T value) noexcept - { - return static_cast>(value); - } -} -#endif namespace glz::rpc { @@ -40,195 +21,122 @@ namespace glz::rpc internal = -32603, parse_error = -32700, }; - static constexpr std::array error_e_iterable{ - error_e::no_error, error_e::server_error_lower, error_e::server_error_upper, error_e::invalid_request, - error_e::method_not_found, error_e::invalid_params, error_e::internal, error_e::parse_error, - }; -} -template <> -struct glz::meta -{ - // clang-format off - static constexpr auto value{[](auto&& enum_value) -> auto { return util::to_underlying(enum_value); }}; -}; -// clang-format on + inline constexpr std::string_view code_as_sv(const error_e error_code) noexcept + { + switch (error_code) { + case error_e::no_error: + return "No error"; + case error_e::parse_error: + return "Parse error"; + case error_e::server_error_lower: + case error_e::server_error_upper: + return "Server error"; + case error_e::invalid_request: + return "Invalid request"; + case error_e::method_not_found: + return "Method not found"; + case error_e::invalid_params: + return "Invalid params"; + case error_e::internal: + return "Internal error"; + } + return "Unknown"; + } +} // jsonrpc namespace glz::rpc { - using jsonrpc_id_type = std::variant; + using id_t = std::variant; static constexpr std::string_view supported_version{"2.0"}; - namespace detail + struct error final { - inline std::string id_to_string(const jsonrpc_id_type& id) - { - return std::visit( - overload{[](const json_t::null_t&) -> std::string { return "null"; }, - [](const std::string& x) { return x; }, [](const std::int64_t& x) { return std::to_string(x); }, - [](auto&&) -> std::string { return "unknown"; }}, - id); - } + error_e code{error_e::no_error}; + std::optional data{}; // Optional detailed error information + std::string message{code_as_sv(code)}; // string reflection of member variable code - template - struct [[nodiscard]] basic_fixed_string - { - using char_type = CharType; - - char_type data_[N + 1]{}; - - constexpr basic_fixed_string() noexcept : data_{} {} - - template - requires std::same_as - constexpr basic_fixed_string(const other_char_type (&foo)[N + 1]) noexcept - { - std::copy_n(foo, N + 1, data_); - } - - [[nodiscard]] constexpr std::basic_string_view view() const noexcept { return {&data_[0], N}; } - - constexpr operator std::basic_string_view() const noexcept { return {&data_[0], N}; } - - template - constexpr auto operator==(const basic_fixed_string& r) const noexcept - { - return N == M && view() == r.view(); - } - }; - - template - basic_fixed_string(char_type const (&str)[N]) -> basic_fixed_string; - } + // TODO: remove all these constructors when MSVC is fixed + error() = default; + error(error_e code) : code(code) {} + error(error_e code, const std::optional& data) : code(code), data(data) {} + error(error_e code, const std::optional& data, const std::string& message) + : code(code), data(data), message(message) + {} + error(const error&) = default; + error(error&&) = default; + error& operator=(const error&) = default; + error& operator=(error&&) = default; - struct error - { static error invalid(const parse_error& pe, auto& buffer) { std::string format_err{format_error(pe, buffer)}; - return error(rpc::error_e::invalid_request, format_err.empty() ? glz::json_t{} : format_err); + return {error_e::invalid_request, format_err.empty() ? std::nullopt : std::optional{format_err}, + std::string(code_as_sv(error_e::invalid_request))}; } static error version(std::string_view presumed_version) { - return error(error_e::invalid_request, "Invalid version: " + std::string(presumed_version) + - " only supported version is " + std::string(rpc::supported_version)); + return {error_e::invalid_request, + "Invalid version: " + std::string(presumed_version) + " only supported version is " + + std::string(rpc::supported_version), + std::string(code_as_sv(error_e::invalid_request))}; } static error method(std::string_view presumed_method) { - return error(error_e::method_not_found, "Method: \"" + std::string(presumed_method) + "\" not found"); - } - - error() = default; - - explicit error(error_e code, glz::json_t&& data = {}) - : code(util::to_underlying(code)), message(code_as_string(code)), data(std::move(data)) - {} - - [[nodiscard]] auto get_code() const noexcept -> std::optional - { - const auto it{std::find_if(std::cbegin(error_e_iterable), std::cend(error_e_iterable), - [this](error_e err_val) { return util::to_underlying(err_val) == this->code; })}; - if (it == std::cend(error_e_iterable)) { - return std::nullopt; - } - return *it; - } - [[nodiscard]] auto get_raw_code() const noexcept -> int { return code; } - [[nodiscard]] auto get_message() const noexcept -> const std::string& { return message; } - [[nodiscard]] auto get_data() const noexcept -> const glz::json_t& { return data; } - - operator bool() const noexcept - { - const auto my_code{get_code()}; - return my_code.has_value() && my_code.value() != rpc::error_e::no_error; - } - - bool operator==(const rpc::error_e err) const noexcept - { - const auto my_code{get_code()}; - return my_code.has_value() && my_code.value() == err; + return {error_e::method_not_found, "Method: '" + std::string(presumed_method) + "' not found", + std::string(code_as_sv(error_e::method_not_found))}; } - private: - std::underlying_type_t code{util::to_underlying(error_e::no_error)}; - std::string message{code_as_string(error_e::no_error)}; // string reflection of member variable code - glz::json_t data{}; // Optional detailed error information + operator bool() const noexcept { return code != rpc::error_e::no_error; } - static std::string_view code_as_string(error_e error_code) - { - switch (error_code) { - case error_e::no_error: - return "No error"; - case error_e::parse_error: - return "Parse error"; - case error_e::server_error_lower: - case error_e::server_error_upper: - return "Server error"; - case error_e::invalid_request: - return "Invalid request"; - case error_e::method_not_found: - return "Method not found"; - case error_e::invalid_params: - return "Invalid params"; - case error_e::internal: - return "Internal error"; - } - return "Unknown"; - } + bool operator==(const rpc::error_e err) const noexcept { return code == err; } - public: struct glaze { using T = error; - static constexpr auto value{glz::object("code", &T::code, "message", &T::message, "data", &T::data)}; + static constexpr auto value = glz::object("code", &T::code, "message", &T::message, "data", &T::data); }; }; template struct request_t { - using params_t = params_type; - using jsonrpc_id_t = jsonrpc_id_type; - - request_t() = default; - request_t(jsonrpc_id_t&& id, std::string_view&& method, params_t&& params) - : id(std::move(id)), version(rpc::supported_version), method(method), params(std::move(params)) - {} - - jsonrpc_id_t id{}; - std::string version{}; - std::string method{}; - params_t params{}; + id_t id{}; + std::string_view method{}; + params_type params{}; + std::string_view version{rpc::supported_version}; struct glaze { using T = request_t; - static constexpr auto value{ - glz::object("jsonrpc", &T::version, "method", &T::method, "params", &T::params, "id", &T::id)}; + static constexpr auto value = glz::object("jsonrpc", &T::version, // + "method", &T::method, // + "params", &T::params, // + "id", &T::id); }; }; + + template + request_t(id_t&&, std::string_view, params_type&&) -> request_t>; + using generic_request_t = request_t; template struct response_t { using result_t = result_type; - using jsonrpc_id_t = jsonrpc_id_type; response_t() = default; - explicit response_t(rpc::error&& err) : version(rpc::supported_version), error(std::move(err)) {} - response_t(jsonrpc_id_t&& id, result_t&& result) - : id(std::move(id)), version(rpc::supported_version), result(std::move(result)) - {} - response_t(jsonrpc_id_t&& id, rpc::error&& err) - : id(std::move(id)), version(rpc::supported_version), error(std::move(err)) - {} + explicit response_t(rpc::error&& err) : error(std::move(err)) {} + response_t(id_t&& id, result_t&& result) : id(std::move(id)), result(std::move(result)) {} + response_t(id_t&& id, rpc::error&& err) : id(std::move(id)), error(std::move(err)) {} - jsonrpc_id_t id{}; - std::string version{}; + id_t id{}; std::optional result{}; // todo can this be instead expected std::optional error{}; + std::string version{rpc::supported_version}; + struct glaze { using T = response_t; @@ -238,79 +146,59 @@ namespace glz::rpc }; using generic_response_t = response_t; - template - struct server_method_t + template + struct method { static constexpr std::string_view name_v{name}; using params_t = params_type; using result_t = result_type; - using jsonrpc_id_t = jsonrpc_id_type; - using request_t = rpc::request_t; - using response_t = rpc::response_t; - std::function(const params_t&)> callback{ - [](const auto&) { return glz::unexpected{rpc::error(rpc::error_e::internal, "Not implemented")}; }}; }; - template - struct client_method_t - { - static constexpr std::string_view name_v{name}; - using params_t = params_type; - using result_t = result_type; - using jsonrpc_id_t = jsonrpc_id_type; - using request_t = rpc::request_t; - using response_t = rpc::response_t; - using callback_t = std::function&, const jsonrpc_id_type&)>; - std::unordered_map pending_requests; - }; namespace concepts { - template - concept method_type = requires(method_t meth) { - method_t::name_v; + template + concept method_type = requires(T) { + T::name_v; { - std::same_as + std::same_as }; - typename method_t::params_t; - typename method_t::jsonrpc_id_t; - typename method_t::request_t; - typename method_t::response_t; + typename T::params_t; + typename T::result_t; }; - template - concept server_method_type = - requires(method_t meth) { - requires method_type; - { - meth.callback - }; - requires std::same_as< - decltype(meth.callback), - std::function(typename method_t::params_t const&)>>; - }; - - template - concept client_method_type = - requires(method_t meth) { - requires method_type; - { - meth.pending_requests - }; - requires std::same_as< - decltype(meth.pending_requests), - std::unordered_map const&, - jsonrpc_id_type const&)>>>; - }; - template concept call_return_type = requires { requires glz::is_any_of>>; }; } + template + struct server_method_t + { + static constexpr std::string_view name_v = Method::name_v; + using params_t = typename Method::params_t; + using result_t = typename Method::result_t; + using request_t = rpc::request_t; + using response_t = rpc::response_t; + std::function(const params_t&)> callback{[](const auto&) { + return glz::unexpected{rpc::error{rpc::error_e::internal, "Not implemented"}}; + }}; + }; + + template + struct client_method_t + { + static constexpr std::string_view name_v = Method::name_v; + using params_t = typename Method::params_t; + using result_t = typename Method::result_t; + using request_t = rpc::request_t; + using response_t = rpc::response_t; + using callback_t = std::function&, const id_t&)>; + std::unordered_map pending_requests; + }; + namespace detail { - template + template inline constexpr void set_callback(glz::tuplet::tuple& methods, const auto& callback) { constexpr bool method_found = ((method_type::name_v == name) || ...); @@ -349,7 +237,7 @@ namespace glz::rpc static constexpr std::size_t index = std::numeric_limits::max(); }; - template + template auto get_request_map(glz::tuplet::tuple& methods) -> map_t& { constexpr bool method_found = ((method_type::name_v == name) || ...); @@ -374,15 +262,14 @@ namespace glz::rpc } } - template + template struct server { using raw_response_t = response_t; - server() = default; - glz::tuplet::tuple methods{}; + glz::tuplet::tuple...> methods{}; - template + template constexpr void on(const auto& callback) // std::function(params_t const&)> { detail::set_callback(methods, callback); @@ -412,12 +299,12 @@ namespace glz::rpc if (auto parse_err{glz::validate_json(json_request)}) { return return_helper( - raw_response_t{rpc::error(rpc::error_e::parse_error, format_error(parse_err, json_request))}); + raw_response_t{rpc::error{error_e::parse_error, format_error(parse_err, json_request)}}); } auto batch_requests{glz::read_json>(json_request)}; if (batch_requests.has_value() && batch_requests.value().empty()) { - return return_helper(raw_response_t{rpc::error(rpc::error_e::invalid_request)}); + return return_helper(raw_response_t{rpc::error{error_e::invalid_request}}); } if (batch_requests.has_value()) { return return_helper(batch_request(batch_requests.value())); @@ -438,7 +325,7 @@ namespace glz::rpc if (!request.has_value()) { // Failed, but let's try to extract the `id` - auto id{glz::get_as_json(json_request)}; + auto id{glz::get_as_json(json_request)}; if (!id.has_value()) { return raw_response_t{rpc::error::invalid(request.error(), json_request)}; } @@ -498,15 +385,10 @@ namespace glz::rpc } }; - template + template struct client { - // Create client with default queue size of 100 - client() = default; - // Create client given the queue size to store request ids - explicit client(std::size_t max_queue_size) : queue_size(max_queue_size) {} - std::size_t const queue_size{100}; - glz::tuplet::tuple methods{}; + glz::tuplet::tuple...> methods{}; rpc::error call(std::string_view json_response) { @@ -518,7 +400,7 @@ namespace glz::rpc auto& res{response.value()}; - rpc::error return_v; + rpc::error return_v{}; bool id_found = methods.any([&json_response, &res, &return_v](auto&& method) -> bool { using meth_t = std::remove_reference_t; @@ -551,7 +433,13 @@ namespace glz::rpc if (!id_found) [[unlikely]] { if (!return_v) { - return_v = rpc::error(rpc::error_e::internal, "id: " + detail::id_to_string(res.id) + " not found"); + if (std::holds_alternative(res.id)) { + return_v = rpc::error{error_e::internal, + "id: '" + std::string(std::get(res.id)) + "' not found"}; + } + else { + return_v = rpc::error{error_e::internal, "id: " + glz::write_json(res.id) + " not found"}; + } } } @@ -562,8 +450,8 @@ namespace glz::rpc // const&)> where result_t is the result type declared for the given method name returns the request string and // whether the callback was inserted into the queue if the callback was not inserted into the queue it can mean // that the input was a notification or more serious, conflicting id, the provided id should be unique! - template - [[nodiscard]] auto request(jsonrpc_id_type&& id, auto&& params, auto&& callback) -> std::pair + template + [[nodiscard]] std::pair request(id_t&& id, auto&& params, auto&& callback) { constexpr bool method_found = ((method_type::name_v == method_name) || ...); static_assert(method_found, "Method not declared in client."); @@ -577,8 +465,7 @@ namespace glz::rpc ...); static_assert(method_params_match, "Method name and given params type do not match."); - rpc::request_t req(std::forward(id), method_name.view(), - std::forward(params)); + rpc::request_t req{std::forward(id), method_name.view(), std::forward(params)}; if (std::holds_alternative(id)) { return {glz::write_json(std::move(req)), false}; @@ -601,27 +488,27 @@ namespace glz::rpc return {glz::write_json(std::move(req)), inserted}; } - template + template [[nodiscard]] auto notify(auto&& params) -> std::string { auto placebo{[](auto&, auto&) {}}; return request(glz::json_t::null_t{}, params, std::move(placebo)).first; } - template + template [[nodiscard]] const auto& get_request_map() const { constexpr auto idx = detail::index_of_name::index; - using method_element = std::tuple_element_t>; + using method_element = std::tuple_element_t...>>; using request_map_t = decltype(method_element().pending_requests); return detail::get_request_map(methods); } - template + template [[nodiscard]] auto& get_request_map() { constexpr auto idx = detail::index_of_name::index; - using method_element = std::tuple_element_t>; + using method_element = std::tuple_element_t...>>; using request_map_t = decltype(method_element().pending_requests); return detail::get_request_map(methods); } diff --git a/include/glaze/json/schema.hpp b/include/glaze/json/schema.hpp index b13c8dc810..20889db2cb 100644 --- a/include/glaze/json/schema.hpp +++ b/include/glaze/json/schema.hpp @@ -68,7 +68,8 @@ namespace glz "const", &T::constant, // "minLength", &T::min_length, // "maxLength", &T::max_length, // - "pattern", &T::pattern, "minimum", &T::minimum, // + "pattern", &T::pattern, // + "minimum", &T::minimum, // "maximum", &T::maximum, // "exclusiveMinimum", &T::exclusive_minimum, // "exclusiveMaximum", &T::exclusive_maximum, // @@ -327,9 +328,8 @@ namespace glz to_json_schema::template op(def, defs); } auto ref_val = schema{join_v, name_v>}; - // clang-format off - if constexpr (std::tuple_size_v > 2) { - // clang-format on + static constexpr auto Size = std::tuple_size_v; + if constexpr (Size > 2) { using additional_data_type = decltype(glz::tuplet::get<2>(item)); if constexpr (std::is_convertible_v) { ref_val.description = glz::tuplet::get<2>(item); diff --git a/include/glaze/thread/threadpool.hpp b/include/glaze/thread/threadpool.hpp index acecbf5af7..7567b2e388 100644 --- a/include/glaze/thread/threadpool.hpp +++ b/include/glaze/thread/threadpool.hpp @@ -5,9 +5,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -15,7 +15,7 @@ namespace glz { // A simple threadpool - struct pool + struct pool final { pool() : pool(concurrency()) {} @@ -23,12 +23,18 @@ namespace glz void n_threads(const size_t n) { - for (size_t i = threads.size(); i < n; ++i) { + finish_work(); // finish any active work + std::lock_guard lock(mtx); + closed = false; + + threads.clear(); + threads.reserve(n); + for (size_t i = 0; i < n; ++i) { threads.emplace_back(std::thread(&pool::worker, this, i)); } } - size_t concurrency() const { return std::thread::hardware_concurrency(); } + size_t concurrency() const noexcept { return std::thread::hardware_concurrency(); } template std::future>> emplace_back(F&& func) @@ -39,10 +45,10 @@ namespace glz auto promise = std::make_shared>(); - queue.emplace(last_index++, [=, f = std::forward(func)](const size_t /*thread_number*/) { + queue.emplace_back([promise, f = std::move(func)](const size_t /*thread_number*/) { #if __cpp_exceptions try { - if constexpr (std::is_void::value) { + if constexpr (std::is_void_v) { f(); } else { @@ -53,7 +59,7 @@ namespace glz promise->set_exception(std::current_exception()); } #else - if constexpr (std::is_void::value) { + if constexpr (std::is_void_v) { f(); } else { @@ -67,6 +73,7 @@ namespace glz return promise->get_future(); } + // Takes a function whose input is the thread number (size_t) template requires std::invocable, size_t> std::future, size_t>> emplace_back(F&& func) @@ -77,10 +84,10 @@ namespace glz auto promise = std::make_shared>(); - queue.emplace(last_index++, [=, f = std::forward(func)](const size_t thread_number) { + queue.emplace_back([promise, f = std::move(func)](const size_t thread_number) { #if __cpp_exceptions try { - if constexpr (std::is_void::value) { + if constexpr (std::is_void_v) { f(thread_number); } else { @@ -91,7 +98,7 @@ namespace glz promise->set_exception(std::current_exception()); } #else - if constexpr (std::is_void::value) { + if constexpr (std::is_void_v) { f(thread_number); } else { @@ -105,7 +112,7 @@ namespace glz return promise->get_future(); } - bool computing() const { return (working != 0); } + bool computing() const noexcept { return (working != 0); } void wait() { @@ -116,7 +123,19 @@ namespace glz size_t size() const { return threads.size(); } - ~pool() + ~pool() { finish_work(); } + + private: + std::vector threads; + // using std::deque for the queue causes random function call issues + std::list> queue; + std::atomic working = 0; + std::atomic closed = false; + std::mutex mtx; + std::condition_variable work_cv; + std::condition_variable done_cv; + + void finish_work() { // Close the queue and finish all the remaining work std::unique_lock lock(mtx); @@ -124,47 +143,37 @@ namespace glz work_cv.notify_all(); lock.unlock(); - for (auto& t : threads) + for (auto& t : threads) { if (t.joinable()) t.join(); + } } - private: - std::vector threads; - std::unordered_map> queue; - std::atomic front_index{}; - std::atomic last_index{}; - std::atomic working = 0; - bool closed = false; - std::mutex mtx; - std::condition_variable work_cv; - std::condition_variable done_cv; - void worker(const size_t thread_number) { while (true) { // Wait for work - std::unique_lock lock(mtx); - work_cv.wait(lock, [this]() { return closed || !(front_index == last_index); }); + std::unique_lock lock(mtx); + work_cv.wait(lock, [this]() { return closed || !queue.empty(); }); if (queue.empty()) { if (closed) { return; } - continue; } + else { + // Grab work + ++working; + auto work = std::move(queue.front()); + queue.pop_front(); + lock.unlock(); - // Grab work - ++working; - auto work = queue.find(front_index++); - lock.unlock(); - - work->second(thread_number); + work(thread_number); - lock.lock(); - queue.erase(work); + lock.lock(); - // Notify that work is finished - --working; - done_cv.notify_all(); + // Notify that work is finished + --working; + done_cv.notify_all(); + } } } }; diff --git a/include/glaze/util/fixed_string.hpp b/include/glaze/util/fixed_string.hpp new file mode 100644 index 0000000000..ba6b93958d --- /dev/null +++ b/include/glaze/util/fixed_string.hpp @@ -0,0 +1,40 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include +#include + +namespace glz +{ + template + struct [[nodiscard]] basic_fixed_string + { + using char_type = CharType; + + char_type data_[N + 1]{}; + + constexpr basic_fixed_string() noexcept : data_{} {} + + template + requires std::same_as + constexpr basic_fixed_string(const other_char_type (&foo)[N + 1]) noexcept + { + std::copy_n(foo, N + 1, data_); + } + + [[nodiscard]] constexpr std::basic_string_view view() const noexcept { return {&data_[0], N}; } + + constexpr operator std::basic_string_view() const noexcept { return {&data_[0], N}; } + + template + constexpr auto operator==(const basic_fixed_string& r) const noexcept + { + return N == M && view() == r.view(); + } + }; + + template + basic_fixed_string(char_type const (&str)[N]) -> basic_fixed_string; +} diff --git a/tests/binary_test/binary_test.cpp b/tests/binary_test/binary_test.cpp index d3e277a95a..822c155083 100644 --- a/tests/binary_test/binary_test.cpp +++ b/tests/binary_test/binary_test.cpp @@ -14,8 +14,6 @@ #include "glaze/binary/read.hpp" #include "glaze/binary/write.hpp" -using namespace glz; - struct my_struct { int i = 287; @@ -206,9 +204,9 @@ void write_tests() { bool b = true; std::vector out; - write_binary(b, out); + glz::write_binary(b, out); bool b2{}; - expect(!read_binary(b2, out)); + expect(!glz::read_binary(b2, out)); expect(b == b2); } }; @@ -217,9 +215,9 @@ void write_tests() { float f = 1.5f; std::vector out; - write_binary(f, out); + glz::write_binary(f, out); float f2{}; - expect(!read_binary(f2, out)); + expect(!glz::read_binary(f2, out)); expect(f == f2); } }; @@ -228,9 +226,9 @@ void write_tests() { std::string s = "Hello World"; std::vector out; - write_binary(s, out); + glz::write_binary(s, out); std::string s2{}; - expect(!read_binary(s2, out)); + expect(!glz::read_binary(s2, out)); expect(s == s2); } }; @@ -239,9 +237,9 @@ void write_tests() { std::array arr = {1.2f, 3434.343f, 0.f}; std::vector out; - write_binary(arr, out); + glz::write_binary(arr, out); std::array arr2{}; - expect(!read_binary(arr2, out)); + expect(!glz::read_binary(arr2, out)); expect(arr == arr2); } }; @@ -250,9 +248,9 @@ void write_tests() { std::vector v = {1.2f, 3434.343f, 0.f}; std::vector out; - write_binary(v, out); + glz::write_binary(v, out); std::vector v2; - expect(!read_binary(v2, out)); + expect(!glz::read_binary(v2, out)); expect(v == v2); } }; @@ -262,9 +260,9 @@ void write_tests() s.i = 5; s.hello = "Wow!"; std::vector out; - write_binary(s, out); + glz::write_binary(s, out); my_struct s2{}; - expect(!read_binary(s2, out)); + expect(!glz::read_binary(s2, out)); expect(s.i == s2.i); expect(s.hello == s2.hello); }; @@ -273,38 +271,38 @@ void write_tests() std::vector out; std::optional op_int{}; - write_binary(op_int, out); + glz::write_binary(op_int, out); std::optional new_op{}; - expect(!read_binary(new_op, out)); + expect(!glz::read_binary(new_op, out)); expect(op_int == new_op); op_int = 10; out.clear(); - write_binary(op_int, out); - expect(!read_binary(new_op, out)); + glz::write_binary(op_int, out); + expect(!glz::read_binary(new_op, out)); expect(op_int == new_op); out.clear(); std::shared_ptr sh_float = std::make_shared(5.55f); - write_binary(sh_float, out); + glz::write_binary(sh_float, out); std::shared_ptr out_flt; - expect(!read_binary(out_flt, out)); + expect(!glz::read_binary(out_flt, out)); expect(*sh_float == *out_flt); out.clear(); std::unique_ptr uni_dbl = std::make_unique(5.55); - write_binary(uni_dbl, out); + glz::write_binary(uni_dbl, out); std::shared_ptr out_dbl; - expect(!read_binary(out_dbl, out)); + expect(!glz::read_binary(out_dbl, out)); expect(*uni_dbl == *out_dbl); }; @@ -314,11 +312,11 @@ void write_tests() std::map str_map{{"a", 1}, {"b", 10}, {"c", 100}, {"d", 1000}}; - write_binary(str_map, out); + glz::write_binary(str_map, out); std::map str_read; - expect(!read_binary(str_read, out)); + expect(!glz::read_binary(str_read, out)); for (auto& item : str_map) { expect(str_read[item.first] == item.second); @@ -327,10 +325,10 @@ void write_tests() out.clear(); std::map dbl_map{{1, 5.55}, {3, 7.34}, {8, 44.332}, {0, 0.000}}; - write_binary(dbl_map, out); + glz::write_binary(dbl_map, out); std::map dbl_read{}; - expect(!read_binary(dbl_read, out)); + expect(!glz::read_binary(dbl_read, out)); for (auto& item : dbl_map) { expect(dbl_read[item.first] == item.second); diff --git a/tests/jsonrpc_test/jsonrpc_test.cpp b/tests/jsonrpc_test/jsonrpc_test.cpp index 31bae0d41d..c4c8302cff 100644 --- a/tests/jsonrpc_test/jsonrpc_test.cpp +++ b/tests/jsonrpc_test/jsonrpc_test.cpp @@ -10,26 +10,26 @@ using ut::operator""_test; using ut::operator/; ut::suite valid_vector_test_cases_server = [] { - using request_vec = std::vector; + using vec_t = std::vector; - rpc::server> server; + rpc::server> server; - server.on<"add">([](request_vec const& vec) -> glz::expected { + server.on<"add">([](vec_t const& vec) -> glz::expected { int sum{std::reduce(std::cbegin(vec), std::cend(vec))}; return sum; }); const std::array valid_requests = { - std::make_pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": 1})", - R"({"jsonrpc": "2.0","result": 6,"id": 1})"), - std::make_pair( + std::pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": 1})", + R"({"jsonrpc": "2.0","result": 6,"id": 1})"), + std::pair( // No id is valid R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3]})", ""), - std::make_pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": null})", ""), - std::make_pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": 2.0})", - R"({"jsonrpc": "2.0","result": 6, "id": 2})"), - std::make_pair(R"({"jsonrpc": "2.0","method": "add","params": [1, 2, 3],"id": "some_client_22"})", - R"({"jsonrpc": "2.0","result": 6, "id": "some_client_22"})")}; + std::pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": null})", ""), + std::pair(R"({"jsonrpc": "2.0","method": "add", "params": [1, 2, 3],"id": 2.0})", + R"({"jsonrpc": "2.0","result": 6, "id": 2})"), + std::pair(R"({"jsonrpc": "2.0","method": "add","params": [1, 2, 3],"id": "some_client_22"})", + R"({"jsonrpc": "2.0","result": 6, "id": "some_client_22"})")}; std::string raw_json; std::string resulting_request; @@ -50,33 +50,33 @@ ut::suite valid_vector_test_cases_server = [] { }; ut::suite vector_test_cases = [] { - using request_vec = std::vector; + using vec_t = std::vector; - rpc::server> server; - rpc::client> client; + rpc::server> server; + rpc::client> client; - server.on<"summer">([](request_vec const& vec) -> glz::expected { + server.on<"summer">([](vec_t const& vec) -> glz::expected { int sum{std::reduce(std::cbegin(vec), std::cend(vec))}; return sum; }); ut::test("sum_result = 6") = [&server, &client] { bool called{}; - auto request_str{client.request<"summer">( - 1, std::vector{1, 2, 3}, [&called](glz::expected value, rpc::jsonrpc_id_type id) -> void { - called = true; - ut::expect(value.has_value()); - ut::expect(value.value() == 6); - ut::expect(std::holds_alternative(id)); - ut::expect(std::get(id) == std::int64_t{1}); - })}; + auto request_str{client.request<"summer">(1, std::vector{1, 2, 3}, + [&called](glz::expected value, rpc::id_t id) -> void { + called = true; + ut::expect(value.has_value()); + ut::expect(value.value() == 6); + ut::expect(std::holds_alternative(id)); + ut::expect(std::get(id) == std::int64_t{1}); + })}; ut::expect(request_str.first == R"({"jsonrpc":"2.0","method":"summer","params":[1,2,3],"id":1})"); [[maybe_unused]] auto& requests = client.get_request_map<"summer">(); ut::expect(requests.size() == 1); ut::expect(requests.contains(1)); // the id is 1 - server.on<"summer">([](request_vec const& vec) -> glz::expected { + server.on<"summer">([](vec_t const& vec) -> glz::expected { ut::expect(vec == std::vector{1, 2, 3}); int sum{std::reduce(std::cbegin(vec), std::cend(vec))}; return sum; @@ -138,21 +138,19 @@ struct glz::meta }; ut::suite struct_test_cases = [] { - rpc::server, rpc::server_method_t<"bar", bar_params, bar_result>> - server; - rpc::client, rpc::client_method_t<"bar", bar_params, bar_result>> - client; + rpc::server, rpc::method<"bar", bar_params, bar_result>> server; + rpc::client, rpc::method<"bar", bar_params, bar_result>> client; ut::test("valid foo request") = [&server, &client] { bool called{}; auto request_str{ client.request<"foo">("42", foo_params{.foo_a = 1337, .foo_b = "hello world"}, - [&called](glz::expected value, rpc::jsonrpc_id_type id) -> void { + [&called](glz::expected value, rpc::id_t id) -> void { called = true; ut::expect(value.has_value()); ut::expect(value.value() == foo_result{.foo_c = true, .foo_d = "new world"}); - ut::expect(std::holds_alternative(id)); - ut::expect(std::get(id) == std::string{"42"}); + ut::expect(std::holds_alternative(id)); + ut::expect(std::get(id) == "42"); })}; ut::expect(request_str.first == R"({"jsonrpc":"2.0","method":"foo","params":{"foo_a":1337,"foo_b":"hello world"},"id":"42"})"); @@ -176,12 +174,12 @@ ut::suite struct_test_cases = [] { bool called{}; auto request_str{client.request<"bar">( "bar-uuid", bar_params{.bar_a = 1337, .bar_b = "hello world"}, - [&called](glz::expected const& value, rpc::jsonrpc_id_type const& id) -> void { + [&called](glz::expected const& value, rpc::id_t const& id) -> void { called = true; ut::expect(value.has_value()); ut::expect(value.value() == bar_result{.bar_c = true, .bar_d = "new world"}); - ut::expect(std::holds_alternative(id)); - ut::expect(std::get(id) == std::string{"bar-uuid"}); + ut::expect(std::holds_alternative(id)); + ut::expect(std::get(id) == "bar-uuid"); })}; ut::expect(request_str.first == R"({"jsonrpc":"2.0","method":"bar","params":{"bar_a":1337,"bar_b":"hello world"},"id":"bar-uuid"})"); @@ -205,12 +203,12 @@ ut::suite struct_test_cases = [] { bool called{}; auto request_str{ client.request<"foo">("42", foo_params{.foo_a = 1337, .foo_b = "hello world"}, - [&called](glz::expected value, rpc::jsonrpc_id_type id) -> void { + [&called](glz::expected value, rpc::id_t id) -> void { called = true; ut::expect(!value.has_value()); ut::expect(value.error() == rpc::error{rpc::error_e::server_error_lower, "my error"}); - ut::expect(std::holds_alternative(id)); - ut::expect(std::get(id) == std::string{"42"}); + ut::expect(std::holds_alternative(id)); + ut::expect(std::get(id) == "42"); })}; ut::expect(request_str.first == @@ -219,7 +217,7 @@ ut::suite struct_test_cases = [] { server.on<"foo">([](foo_params const& params) -> glz::expected { ut::expect(params.foo_a == 1337); ut::expect(params.foo_b == "hello world"); - return glz::unexpected(rpc::error(rpc::error_e::server_error_lower, "my error")); + return glz::unexpected(rpc::error{rpc::error_e::server_error_lower, "my error"}); }); std::string response = server.call(request_str.first); @@ -243,7 +241,7 @@ ut::suite struct_test_cases = [] { glz::write_json(response_vec) == R"([{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"Invalid version: 42.0 only supported version is 2.0"},"id":"uuid"}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::invalid_request); + ut::expect(response_vec.at(0).error->code == rpc::error_e::invalid_request); }; ut::test("server method not found") = [&server] { @@ -255,9 +253,9 @@ ut::suite struct_test_cases = [] { ut::expect(response_vec.size() == 1); ut::expect( glz::write_json(response_vec) == - R"([{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: \"invalid_method_name\" not found"},"id":"uuid"}])"); + R"([{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: 'invalid_method_name' not found"},"id":"uuid"}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::method_not_found); + ut::expect(response_vec.at(0).error->code == rpc::error_e::method_not_found); }; ut::test("server invalid json") = [&server] { @@ -268,11 +266,12 @@ ut::suite struct_test_cases = [] { R"({"jsonrpc":"2.0","method":"invalid_method_name","params":{},"id:"uuid"}")"); ut::expect(response_vec.size() == 1); [[maybe_unused]] auto dbg{glz::write_json(response_vec)}; + auto s = glz::write_json(response_vec); ut::expect( - glz::write_json(response_vec) == + s == R"([{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":"1:66: syntax_error\n {\"jsonrpc\":\"2.0\",\"method\":\"invalid_method_name\",\"params\":{},\"id:\"uuid\"}\"\n ^\n"},"id":null}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::parse_error); + ut::expect(response_vec.at(0).error->code == rpc::error_e::parse_error); }; ut::test("server invalid json batch") = [&server] { @@ -282,11 +281,12 @@ ut::suite struct_test_cases = [] { auto response_vec = server.call>>( R"([{"jsonrpc":"2.0","method":"invalid_method_name","params":{},"id":"uuid"},{"jsonrpc":"2.0","method":"invalid_method_name","params":]")"); ut::expect(response_vec.size() == 1); + auto s = glz::write_json(response_vec); ut::expect( - glz::write_json(response_vec) == + s == R"([{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":"1:132: syntax_error\n [{\"jsonrpc\":\"2.0\",\"method\":\"invalid_method_name\",\"params\":{},\"id\":\"uuid\"},{\"jsonrpc\":\"2.0\",\"method\":\"invalid_method_name\",\"params\":]\"\n ^\n"},"id":null}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::parse_error); + ut::expect(response_vec.at(0).error->code == rpc::error_e::parse_error); }; ut::test("server invalid json batch empty array") = [&server] { @@ -295,10 +295,10 @@ ut::suite struct_test_cases = [] { auto response_vec = server.call>>(R"([])"); ut::expect(response_vec.size() == 1); - ut::expect(glz::write_json(response_vec) == - R"([{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":null},"id":null}])"); + auto s = glz::write_json(response_vec); + ut::expect(s == R"([{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request"},"id":null}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::invalid_request); + ut::expect(response_vec.at(0).error->code == rpc::error_e::invalid_request); }; ut::test("server invalid json illformed batch one item") = [&server] { @@ -311,7 +311,7 @@ ut::suite struct_test_cases = [] { glz::write_json(response_vec) == R"([{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:1: syntax_error\n 1\n ^\n"},"id":null}])"); ut::expect(response_vec.at(0).error.has_value()); - ut::expect(response_vec.at(0).error->get_code().value() == rpc::error_e::invalid_request); + ut::expect(response_vec.at(0).error->code == rpc::error_e::invalid_request); }; ut::test("server invalid json illformed batch three items") = [&server] { @@ -325,7 +325,7 @@ ut::suite struct_test_cases = [] { R"([{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:1: syntax_error\n 1\n ^\n"},"id":null},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:1: syntax_error\n 2\n ^\n"},"id":null},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:1: syntax_error\n 3\n ^\n"},"id":null}])"); for (auto& response : response_vec) { ut::expect(response.error.has_value()); - ut::expect(response.error->get_code().value() == glz::rpc::error_e::invalid_request); + ut::expect(response.error->code == glz::rpc::error_e::invalid_request); } }; @@ -349,14 +349,14 @@ ut::suite struct_test_cases = [] { R"([ {"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"42"}, {"jsonrpc":"2.0","result":{"bar_c":false,"bar_d":""},"id":"bar-uuid"}, -{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: \"invalid_method_name\" not found"},"id":"2"}, +{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: 'invalid_method_name' not found"},"id":"2"}, {"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:9: unknown_key\n {\"foo\": \"boo\"}\n ^\n"},"id":null}, {"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"4222222"}, {"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:21: unknown_key\n {\"jsonrpc\":\"2.0\",\"invalid_method_key\":\"foo\",\"params\":{},\"id\":\"4222222\"}\n ^\n"},"id":"4222222"} ])"}; ut::expect( response == - R"([{"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"42"},{"jsonrpc":"2.0","result":{"bar_c":false,"bar_d":""},"id":"bar-uuid"},{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: \"invalid_method_name\" not found"},"id":"2"},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:9: unknown_key\n {\"foo\": \"boo\"}\n ^\n"},"id":null},{"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"4222222"},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:21: unknown_key\n {\"jsonrpc\":\"2.0\",\"invalid_method_key\":\"foo\",\"params\":{},\"id\":\"4222222\"}\n ^\n"},"id":"4222222"}])"); + R"([{"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"42"},{"jsonrpc":"2.0","result":{"bar_c":false,"bar_d":""},"id":"bar-uuid"},{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method: 'invalid_method_name' not found"},"id":"2"},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:9: unknown_key\n {\"foo\": \"boo\"}\n ^\n"},"id":null},{"jsonrpc":"2.0","result":{"foo_c":false,"foo_d":""},"id":"4222222"},{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request","data":"1:21: unknown_key\n {\"jsonrpc\":\"2.0\",\"invalid_method_key\":\"foo\",\"params\":{},\"id\":\"4222222\"}\n ^\n"},"id":"4222222"}])"); }; "server weird id values"_test = [&server] { @@ -371,7 +371,7 @@ ut::suite struct_test_cases = [] { ut::expect(response_vec.size() == 2); for (auto& response : response_vec) { ut::expect(response.error.has_value()); - ut::expect(response.error->get_code().value() == glz::rpc::error_e::invalid_request); + ut::expect(response.error->code == glz::rpc::error_e::invalid_request); } }; "server invalid jsonrpc value"_test = [&server] { @@ -386,7 +386,7 @@ ut::suite struct_test_cases = [] { ut::expect(response_vec.size() == 2); for (auto& response : response_vec) { ut::expect(response.error.has_value()); - ut::expect(response.error->get_code().value() == glz::rpc::error_e::invalid_request); + ut::expect(response.error->code == glz::rpc::error_e::invalid_request); } }; "client request map"_test = [&client] { @@ -460,7 +460,7 @@ ut::suite struct_test_cases = [] { for (auto& response : response_vec) { ut::expect(response.error.has_value()); if (response.error.has_value()) { - ut::expect(response.error->get_code().value() == glz::rpc::error_e::invalid_params); + ut::expect(response.error->code == glz::rpc::error_e::invalid_params); } } };