From 5af2ae76e14b87eaef8878902e0cc71c9be83daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20dos=20Santos=20Oliveira?= Date: Wed, 28 Jul 2021 20:40:50 -0300 Subject: [PATCH 1/4] Add json::partial::copy_and_look_ahead() Updates #26 --- include/trial/protocol/json/detail/error.ipp | 3 + include/trial/protocol/json/error.hpp | 3 +- .../json/partial/copy_and_look_ahead.hpp | 176 ++++++++++++++++++ test/json/CMakeLists.txt | 1 + test/json/copy_and_look_ahead_suite.cpp | 51 +++++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 include/trial/protocol/json/partial/copy_and_look_ahead.hpp create mode 100644 test/json/copy_and_look_ahead_suite.cpp diff --git a/include/trial/protocol/json/detail/error.ipp b/include/trial/protocol/json/detail/error.ipp index f5ab29d..6098def 100644 --- a/include/trial/protocol/json/detail/error.ipp +++ b/include/trial/protocol/json/detail/error.ipp @@ -58,6 +58,9 @@ public: case insufficient_tokens: return "algorithm used requires more tokens than available"; + + case field_not_found: + return "request field(s) not found"; } return "trial.protocol.json error"; } diff --git a/include/trial/protocol/json/error.hpp b/include/trial/protocol/json/error.hpp index 77437ad..ea97e91 100644 --- a/include/trial/protocol/json/error.hpp +++ b/include/trial/protocol/json/error.hpp @@ -35,7 +35,8 @@ enum errc expected_end_array, expected_end_object, - insufficient_tokens + insufficient_tokens, + field_not_found }; const std::error_category& error_category(); diff --git a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp new file mode 100644 index 0000000..89b98be --- /dev/null +++ b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp @@ -0,0 +1,176 @@ +#ifndef TRIAL_PROTOCOL_JSON_PARTIAL_COPY_AND_LOOK_AHEAD_HPP +#define TRIAL_PROTOCOL_JSON_PARTIAL_COPY_AND_LOOK_AHEAD_HPP + +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Vinícius dos Santos Oliveira +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +namespace trial +{ +namespace protocol +{ +namespace json +{ +namespace partial +{ + +namespace detail { +template +struct CopyAndLookAheadCollector +{ + using view_type = typename basic_reader::view_type; + using size_type = typename basic_reader::size_type; + + CopyAndLookAheadCollector(view_type key) + : pattern{key} + {} + + void push_back(CharT v) + { + if (!process) return; + + if (!pattern.starts_with(v)) { + process = false; + return; + } + + pattern.remove_prefix(1); + } + + void append(const CharT *data, size_type size) + { + if (!process) return; + + view_type run{data, size}; + if (!pattern.starts_with(run)) { + process = false; + return; + } + + pattern.remove_prefix(size); + } + + bool current_value_matches_pattern() + { + return process && pattern.size() == 0; + } + + bool process = true; + view_type pattern; +}; +} // namespace detail + +template +auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader_, + typename basic_reader::view_type key, + F&& on_value, std::error_code &ec) + -> decltype(on_value(const_cast&>(reader_)), true) +{ + assert(!ec); + + if (reader_.symbol() != json::token::symbol::begin_object) { + ec = errc::field_not_found; + return false; + } + + basic_reader reader(reader_); + + if (!reader.next()) { + ec = reader.error(); + return false; + } + + for (;;) { + if (reader.symbol() == json::token::symbol::end_object) { + ec = errc::field_not_found; + return false; + } + + // Key + assert(reader.symbol() == json::token::symbol::key); + detail::CopyAndLookAheadCollector collector{/*pattern=*/key}; + { + auto ec = reader.string(collector); + assert(!ec); (void)ec; + } + if (!reader.next()) { + ec = reader.error(); + return false; + } + + // Value + if (!collector.current_value_matches_pattern()) { + skip(reader, ec); + if (ec) + return false; + continue; + } + + on_value(reader); + return true; + } +} + +template +auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader, + typename basic_reader::view_type key, + F&& on_value) + -> decltype(on_value(const_cast&>(reader)), true) +{ + std::error_code ec; + bool ret = copy_and_look_ahead(reader, key, std::forward(on_value), ec); + if (ec) + throw json::error(ec); + return ret; +} + +template +bool copy_and_look_ahead(const basic_reader &reader, + const typename basic_reader::view_type key, + const typename basic_reader::view_type value, + std::error_code &ec) +{ + bool ret = false; + auto on_value = [&value,&ec,&ret](basic_reader &reader) { + if (reader.symbol() != json::token::symbol::string) + return; + + detail::CopyAndLookAheadCollector collector{/*pattern=*/value}; + { + auto ec = reader.string(collector); + assert(!ec); (void)ec; + } + ret = collector.current_value_matches_pattern(); + if (!ret) + ec = errc::field_not_found; + }; + copy_and_look_ahead(reader, key, on_value, ec); + return ret; +} + +template +bool copy_and_look_ahead(const basic_reader &reader, + const typename basic_reader::view_type key, + const typename basic_reader::view_type value) +{ + std::error_code ec; + bool ret = copy_and_look_ahead(reader, key, value, ec); + if (ec) + throw json::error(ec); + return ret; +} + +} // namespace partial +} // namespace json +} // namespace protocol +} // namespace trial + +#endif // TRIAL_PROTOCOL_JSON_PARTIAL_COPY_AND_LOOK_AHEAD_HPP diff --git a/test/json/CMakeLists.txt b/test/json/CMakeLists.txt index 875b4b2..d8c77ec 100644 --- a/test/json/CMakeLists.txt +++ b/test/json/CMakeLists.txt @@ -19,6 +19,7 @@ trial_add_test(json_writer_suite writer_suite.cpp) trial_add_test(json_iarchive_suite iarchive_suite.cpp) trial_add_test(json_oarchive_suite oarchive_suite.cpp) trial_add_test(json_partial_skip_suite skip_suite.cpp) +trial_add_test(json_partial_copy_and_look_ahead_suite copy_and_look_ahead_suite.cpp) # Tree processing trial_add_test(json_parse_suite parse_suite.cpp) diff --git a/test/json/copy_and_look_ahead_suite.cpp b/test/json/copy_and_look_ahead_suite.cpp new file mode 100644 index 0000000..3f3dcdf --- /dev/null +++ b/test/json/copy_and_look_ahead_suite.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Vinícius dos Santos Oliveira +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +using namespace trial::protocol; +namespace token = json::token; + +void test_int() +{ + const json::reader reader("{\"foo\": 42"); + int value = 0; + auto on_value = [&](json::reader& reader) { + value = reader.value(); + }; + auto ret = json::partial::copy_and_look_ahead(reader, "foo", on_value); + TRIAL_PROTOCOL_TEST_EQUAL(ret, true); + TRIAL_PROTOCOL_TEST_EQUAL(value, 42); +} + +void test_string() +{ + const json::reader reader("{\"foo\": \"bar\""); + std::error_code ec; + auto ret = json::partial::copy_and_look_ahead(reader, "foo", "bar2", ec); + TRIAL_PROTOCOL_TEST_EQUAL(ret, false); + TRIAL_PROTOCOL_TEST_EQUAL(ec, json::errc::field_not_found); + ret = json::partial::copy_and_look_ahead(reader, "foo", "bar"); + TRIAL_PROTOCOL_TEST_EQUAL(ret, true); +} + +//----------------------------------------------------------------------------- +// main +//----------------------------------------------------------------------------- + +int main() +{ + test_int(); + test_string(); + + return boost::report_errors(); +} + From d963d4972d6d3c93f6b697ab8eaa2263a54d4f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20dos=20Santos=20Oliveira?= Date: Fri, 6 Aug 2021 08:38:13 -0300 Subject: [PATCH 2/4] Fix naming conventions --- .../trial/protocol/json/partial/copy_and_look_ahead.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp index 89b98be..391e5c1 100644 --- a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp +++ b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp @@ -24,12 +24,12 @@ namespace partial namespace detail { template -struct CopyAndLookAheadCollector +struct copy_and_look_ahead_collector { using view_type = typename basic_reader::view_type; using size_type = typename basic_reader::size_type; - CopyAndLookAheadCollector(view_type key) + copy_and_look_ahead_collector(view_type key) : pattern{key} {} @@ -96,7 +96,7 @@ auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader_, // Key assert(reader.symbol() == json::token::symbol::key); - detail::CopyAndLookAheadCollector collector{/*pattern=*/key}; + detail::copy_and_look_ahead_collector collector{/*pattern=*/key}; { auto ec = reader.string(collector); assert(!ec); (void)ec; @@ -143,7 +143,8 @@ bool copy_and_look_ahead(const basic_reader &reader, if (reader.symbol() != json::token::symbol::string) return; - detail::CopyAndLookAheadCollector collector{/*pattern=*/value}; + detail::copy_and_look_ahead_collector + collector{/*pattern=*/value}; { auto ec = reader.string(collector); assert(!ec); (void)ec; From 76c94643aaa2e7243a322ad8703b4319548bd2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20dos=20Santos=20Oliveira?= Date: Sat, 7 Aug 2021 03:41:53 -0300 Subject: [PATCH 3/4] Pass reader by value in partial::copy_and_look_ahead() --- .../json/partial/copy_and_look_ahead.hpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp index 391e5c1..beb06a2 100644 --- a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp +++ b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp @@ -69,20 +69,18 @@ struct copy_and_look_ahead_collector } // namespace detail template -auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader_, +auto/*=bool*/ copy_and_look_ahead(basic_reader reader, typename basic_reader::view_type key, F&& on_value, std::error_code &ec) - -> decltype(on_value(const_cast&>(reader_)), true) + -> decltype(on_value(reader), true) { assert(!ec); - if (reader_.symbol() != json::token::symbol::begin_object) { + if (reader.symbol() != json::token::symbol::begin_object) { ec = errc::field_not_found; return false; } - basic_reader reader(reader_); - if (!reader.next()) { ec = reader.error(); return false; @@ -120,20 +118,21 @@ auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader_, } template -auto/*=bool*/ copy_and_look_ahead(const basic_reader &reader, +auto/*=bool*/ copy_and_look_ahead(basic_reader reader, typename basic_reader::view_type key, F&& on_value) -> decltype(on_value(const_cast&>(reader)), true) { std::error_code ec; - bool ret = copy_and_look_ahead(reader, key, std::forward(on_value), ec); + bool ret = copy_and_look_ahead(std::move(reader), key, + std::forward(on_value), ec); if (ec) throw json::error(ec); return ret; } template -bool copy_and_look_ahead(const basic_reader &reader, +bool copy_and_look_ahead(basic_reader reader, const typename basic_reader::view_type key, const typename basic_reader::view_type value, std::error_code &ec) @@ -153,17 +152,17 @@ bool copy_and_look_ahead(const basic_reader &reader, if (!ret) ec = errc::field_not_found; }; - copy_and_look_ahead(reader, key, on_value, ec); + copy_and_look_ahead(std::move(reader), key, on_value, ec); return ret; } template -bool copy_and_look_ahead(const basic_reader &reader, +bool copy_and_look_ahead(basic_reader reader, const typename basic_reader::view_type key, const typename basic_reader::view_type value) { std::error_code ec; - bool ret = copy_and_look_ahead(reader, key, value, ec); + bool ret = copy_and_look_ahead(std::move(reader), key, value, ec); if (ec) throw json::error(ec); return ret; From f2c01e43d6294a027be4177ae61005531cc85abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20dos=20Santos=20Oliveira?= Date: Sat, 14 Aug 2021 10:25:57 -0300 Subject: [PATCH 4/4] Add errc::expected_begin_object --- include/trial/protocol/json/detail/error.ipp | 3 +++ include/trial/protocol/json/error.hpp | 1 + include/trial/protocol/json/partial/copy_and_look_ahead.hpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/trial/protocol/json/detail/error.ipp b/include/trial/protocol/json/detail/error.ipp index 6098def..209e658 100644 --- a/include/trial/protocol/json/detail/error.ipp +++ b/include/trial/protocol/json/detail/error.ipp @@ -59,6 +59,9 @@ public: case insufficient_tokens: return "algorithm used requires more tokens than available"; + case expected_begin_object: + return "algorithm only works on objects"; + case field_not_found: return "request field(s) not found"; } diff --git a/include/trial/protocol/json/error.hpp b/include/trial/protocol/json/error.hpp index ea97e91..0e8bf8d 100644 --- a/include/trial/protocol/json/error.hpp +++ b/include/trial/protocol/json/error.hpp @@ -36,6 +36,7 @@ enum errc expected_end_object, insufficient_tokens, + expected_begin_object, field_not_found }; diff --git a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp index beb06a2..92ee526 100644 --- a/include/trial/protocol/json/partial/copy_and_look_ahead.hpp +++ b/include/trial/protocol/json/partial/copy_and_look_ahead.hpp @@ -77,7 +77,7 @@ auto/*=bool*/ copy_and_look_ahead(basic_reader reader, assert(!ec); if (reader.symbol() != json::token::symbol::begin_object) { - ec = errc::field_not_found; + ec = errc::expected_begin_object; return false; }