From 3e2cfb3b37a34d89b12768682ef2f3e0cdd44ad0 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sun, 6 Apr 2025 00:57:44 +0100 Subject: [PATCH] Allow searching for raw variants This makes the visitor a little bit more complicated but it allows client code to fetch raw variants. It is useful when dealing with arrays and maps, so that client code can decide to cache the intermediate array or map into a variable and then run subsequent searches on that variant rather than from the start. It should make looping a bit less expensive. --- src/main.cpp | 12 ++++-- src/visitors/find_t_visitor.cpp | 76 +++++++++++++++++++++++---------- src/visitors/find_t_visitor.hpp | 54 +++++++++++++++++++---- 3 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2e1dd73..14a16a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,7 +64,13 @@ int main(int argc, const char* argv[]) { auto hashes = duck::collect_hashes(values); std::cout << "Got " << hashes.size() << " hashes\n"; - const auto file_count = static_cast(duck::find_int("/info/files/[[size]]", values)); + const auto variant_array = duck::find_variant("/info/files", values); + if (not variant_array) { + std::cout << "No object found in torrent file at path /info/files\n"; + return 1; + } + + const auto file_count = static_cast(duck::find_int("/[[size]]", *variant_array)); if (file_count) std::cout << "Input has " << file_count << " file entries\n"; else @@ -99,13 +105,13 @@ int main(int argc, const char* argv[]) { } } else { - std::string search("/info/files/[["); + std::string search("/[["); std::size_t hash_index = 0; std::size_t match_count = 0; std::size_t read_size = 0; std::vector buff; for (std::int_fast32_t z = 0; z < file_count; ++z) { - auto file_name = std::string{duck::find_string(search + std::to_string(z) + "]]/path/[[0]]", values)}; + auto file_name = std::string{duck::find_string(search + std::to_string(z) + "]]/path/[[0]]", *variant_array)}; std::cout << "path index " << z << '\t' << file_name << '\n'; std::ifstream istream(file_name, std::ios::in|std::ios::binary); diff --git a/src/visitors/find_t_visitor.cpp b/src/visitors/find_t_visitor.cpp index b2e0abb..cfdd079 100644 --- a/src/visitors/find_t_visitor.cpp +++ b/src/visitors/find_t_visitor.cpp @@ -18,11 +18,18 @@ #include "find_t_visitor.hpp" #include "split.hpp" -#include #include #include +#include namespace duck::detail { +namespace { +template +void assert_same_value (const TorrentValue& var, const T& val) { + assert(boost::get(&var) != nullptr and *boost::get(&var) == val); +} +} //unnamed namespace + template FindTVisitor::FindTVisitor (std::string_view search_path) { auto split = dincore::split(search_path, '/', true, true); @@ -33,9 +40,12 @@ FindTVisitor::FindTVisitor (std::string_view search_path) { } template -void FindTVisitor::reset() { +void FindTVisitor::reset (const TorrentValue* curr_variant) { m_search_error = false; m_search_match_count = 0; + + m_curr_variant = curr_variant; + assert(m_curr_variant); } template @@ -44,27 +54,23 @@ bool FindTVisitor::has_error() const noexcept { } template -T FindTVisitor::operator() (TorrentIntType value) { - if constexpr (std::is_same::value) - return value; - m_search_error = true; - return {}; +auto FindTVisitor::operator() (TorrentIntType value) -> TRet { + return scalar_operator_impl(value); } template -T FindTVisitor::operator() (const TorrentStringType& value) { - if constexpr (std::is_same::value) { - if (is_path_found()) - return value; +auto FindTVisitor::operator() (const TorrentStringType& value) -> TRet { + return scalar_operator_impl(value); +} + +template +auto FindTVisitor::operator() (const std::vector& values) -> TRet { + if (is_path_found()) { + if constexpr (std::is_same::value) + return std::cref(*m_curr_variant); + else + return {}; } - m_search_error = true; - return {}; -} - -template -T FindTVisitor::operator() (const std::vector& values) { - if (is_path_found()) - return {}; if constexpr (std::is_same::value) { if (m_search[m_search_match_count] == "[[size]]") @@ -77,17 +83,24 @@ T FindTVisitor::operator() (const std::vector& values) { if (matched) { ++m_search_match_count; const auto index = static_cast(std::stoul(match[1])); - if (index < values.size()) + if (index < values.size()) { + m_curr_variant = &values[index]; return boost::apply_visitor(*this, values[index]); + } } return {}; } template -T FindTVisitor::operator() (const std::map& value) { +auto FindTVisitor::operator() (const std::map& value) -> TRet { if (is_path_found()) { - m_search_error = true; - return {}; + if constexpr (std::is_same::value) { + return std::cref(*m_curr_variant); + } + else { + m_search_error = true; + return {}; + } } auto it_found = value.find(m_search[m_search_match_count]); @@ -95,9 +108,25 @@ T FindTVisitor::operator() (const std::map& return {}; ++m_search_match_count; + m_curr_variant = &it_found->second; return boost::apply_visitor(*this, it_found->second); } +template +template +auto FindTVisitor::scalar_operator_impl (const U& value) -> TRet { + if constexpr (std::is_same::value) { + assert_same_value(*m_curr_variant, value); + return std::cref(*m_curr_variant); + } + else if constexpr (std::is_same::value) { + if (is_path_found()) + return value; + } + m_search_error = true; + return {}; +} + template bool FindTVisitor::is_path_found() const { return m_search.size() == m_search_match_count; @@ -105,4 +134,5 @@ bool FindTVisitor::is_path_found() const { template class FindTVisitor; template class FindTVisitor; +template class FindTVisitor; } //namespace duck::detail diff --git a/src/visitors/find_t_visitor.hpp b/src/visitors/find_t_visitor.hpp index 0fcf448..bb5d59f 100644 --- a/src/visitors/find_t_visitor.hpp +++ b/src/visitors/find_t_visitor.hpp @@ -23,25 +23,36 @@ #include #include #include +#include +#include namespace duck { namespace detail { template class FindTVisitor : boost::static_visitor { public: + typedef typename std::conditional::value, + std::optional< std::reference_wrapper >, + T + >::type TRet; + FindTVisitor (std::string_view search_path); - void reset(); + void reset (const TorrentValue* curr_variant); bool has_error() const noexcept; - T operator() (TorrentIntType value); - T operator() (const TorrentStringType& value); - T operator() (const std::vector&); - T operator() (const std::map& value); + TRet operator() (TorrentIntType value); + TRet operator() (const TorrentStringType& value); + TRet operator() (const std::vector&); + TRet operator() (const std::map& value); private: + template + TRet scalar_operator_impl (const U& value); + bool is_path_found() const; std::vector m_search; + const TorrentValue* m_curr_variant; std::size_t m_search_match_count{0}; bool m_search_error{false}; }; @@ -51,15 +62,24 @@ typedef detail::FindTVisitor FindStringVisitor; typedef detail::FindTVisitor FindIntVisitor; template -inline T find_item(std::string_view path, const std::vector& values) { +inline auto find_item(std::string_view path, const std::vector& values) { + typedef typename detail::FindTVisitor::TRet TRet; + detail::FindTVisitor visitor(path); for (const auto& value : values) { - visitor.reset(); - T found = boost::apply_visitor(visitor, value); + visitor.reset(&value); + TRet found = boost::apply_visitor(visitor, value); if (not visitor.has_error()) return found; } - return {}; + return TRet{}; +} + +template +inline auto find_item(std::string_view path, const TorrentValue& value) { + detail::FindTVisitor visitor(path); + visitor.reset(&value); + return boost::apply_visitor(visitor, value); } inline TorrentIntType find_int(std::string_view path, const std::vector& values) { @@ -69,4 +89,20 @@ inline TorrentIntType find_int(std::string_view path, const std::vector& values) { return find_item(path, values); } + +inline auto find_variant(std::string_view path, const std::vector& values) { + return find_item(path, values); +} + +inline TorrentIntType find_int(std::string_view path, const TorrentValue& value) { + return find_item(path, value); +} + +inline TorrentStringType find_string(std::string_view path, const TorrentValue& value) { + return find_item(path, value); +} + +inline auto find_variant(std::string_view path, const TorrentValue& value) { + return find_item(path, value); +} } //namespace duck