From 72d52860f60a97c1d3e293e39592cc7c661e0ae7 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 5 Apr 2025 19:59:21 +0100 Subject: [PATCH] Refactor visitors into separate files This also includes some small improvements that I don't remember right now --- src/main.cpp | 222 ++++---------------------------- src/meson.build | 2 + src/parser.cpp | 34 +++++ src/parser.hpp | 8 +- src/visitors/debug_visitor.cpp | 88 +++++++++++++ src/visitors/debug_visitor.hpp | 25 ++++ src/visitors/find_t_visitor.cpp | 91 +++++++++++++ src/visitors/find_t_visitor.hpp | 55 ++++++++ 8 files changed, 323 insertions(+), 202 deletions(-) create mode 100644 src/visitors/debug_visitor.cpp create mode 100644 src/visitors/debug_visitor.hpp create mode 100644 src/visitors/find_t_visitor.cpp create mode 100644 src/visitors/find_t_visitor.hpp diff --git a/src/main.cpp b/src/main.cpp index 9b8cfec..74967fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,12 @@ #include "parser.hpp" -#include "split.hpp" +#include "visitors/debug_visitor.hpp" +#include "visitors/find_t_visitor.hpp" #include #include #include -#include #include -#include #include -#include -#include -#include -#include -#include #include #include @@ -27,186 +21,6 @@ std::string load_file (const std::string& path) { ); } -bool is_printable(std::string_view s) { - return std::find_if(s.cbegin(), s.cend(), - [](unsigned char c){ return not std::isprint(c); } - ) == s.cend(); -} - -struct PrintVisitor { - PrintVisitor(int depth=0, bool skip_first=true) : - depth(depth), first_line(skip_first), skip_first(skip_first) {} - - void print_tabs(int extra=0) { - if (!first_line) { - for (int z = 0; z < depth+extra; ++z) std::cout << '\t'; - } - else { - first_line = false; - } - } - - void reset() { - first_line = skip_first; - } - - void operator() (long long value) { - print_tabs(); - std::cout << value << '\n'; - } - - void operator() (const duck::TorrentStringType& value) { - constexpr std::size_t max_size = 41; - - print_tabs(); - - std::string_view str{value}; - std::string_view ellip; - if (is_printable(value)) { - if (value.size() > max_size + 3) { - str = str.substr(0, max_size); - ellip = "..."; - } - } - else { - str = ""; - } - - std::cout << "\"" << str << ellip << "\"[" << value.size() << "]\n"; - } - - void operator() (const std::vector& value) { - print_tabs(); - std::cout << "[\n"; - - PrintVisitor new_visitor(depth+1, false); - for (const auto& item : value) { - boost::apply_visitor(new_visitor, item); - } - - print_tabs(); - std::cout << "]\n"; - } - - void operator() (const std::map& value) { - print_tabs(); - std::cout << "{\n"; - - PrintVisitor new_visitor(depth+1); - for (const auto& item : value) { - print_tabs(1); - std::cout << item.first << " => "; - - new_visitor.reset(); - boost::apply_visitor(new_visitor, item.second); - } - print_tabs(); - std::cout << "}\n"; - } - - int depth; - bool first_line; - bool skip_first; -}; - -template -struct FindTVisitor : boost::static_visitor { - FindTVisitor (std::string_view search_path) { - auto split = dincore::split(search_path, '/', true, true); - search.reserve(split.size()); - for (const auto& itm : split) { - search.emplace_back(itm.data(), itm.size()); - } - } - - void reset() { - search_error = false; - search_match_count = 0; - } - - bool is_path_found() const { - return search.size() == search_match_count; - } - - T operator() (long long value) { - if constexpr (std::is_same::value) - return value; - search_error = true; - return {}; - } - - T operator() (const duck::TorrentStringType& value) { - if constexpr (std::is_same::value) { - if (is_path_found()) - return value; - } - search_error = true; - return {}; - } - - T operator() (const std::vector&) { - search_error = true; - return {}; - } - - T operator() (const std::map& value) { - if (is_path_found()) { - search_error = true; - return {}; - } - - auto it_found = value.find(search[search_match_count]); - if (value.end() == it_found) - return {}; - - ++search_match_count; - return boost::apply_visitor(*this, it_found->second); - } - - std::vector search; - std::size_t search_match_count{0}; - bool search_error{false}; -}; - -std::vector> collect_hashes (std::string_view hashes) { - typedef std::array array_type; - - if (hashes.empty()) - return {}; - - constexpr std::size_t hash_size = 20; - constexpr std::size_t uint_count = std::tuple_size(); - static_assert(sizeof(array_type::value_type) * uint_count == hash_size); - - if (hashes.size() % hash_size != 0) - throw std::runtime_error("Bad pieces array size " + std::to_string(hashes.size())); - - std::vector retval(hashes.size() / hash_size); - const char* src = hashes.data(); - for (std::size_t z = 0; z < retval.size(); ++z, src+=hash_size) { - //std::copy_n(src, hash_size, reinterpret_cast(retval[z].data())); - for (std::size_t n = 0; n < uint_count; ++n) { - char* const out_uint = reinterpret_cast(retval[z].data() + n); - for (std::size_t u = 0; u < sizeof(std::uint32_t); ++u) { - out_uint[u] = src[n * sizeof(std::uint32_t) + sizeof(std::uint32_t) - u - 1]; - } - } - } - return retval; -} - -template -T find_item(std::string_view path, const std::vector& values) { - FindTVisitor visitor(path); - for (const auto& value : values) { - visitor.reset(); - T found = boost::apply_visitor(visitor, value); - if (not visitor.search_error) - return found; - } - return {}; -} - SHA1::MessageDigest to_hash160_digest (const std::array& arr) { SHA1::MessageDigest hash_found; std::copy_n(arr.begin(), 5, hash_found.hash); @@ -215,9 +29,6 @@ SHA1::MessageDigest to_hash160_digest (const std::array& arr) } //unnamed namespace int main(int argc, const char* argv[]) { - using duck::parse_torrent; - typedef duck::TorrentStringType torrent_string; - if (argc != 2) { std::cerr << "Wrong number of parameters. Usage:\n" << argv[0] << " \n"; @@ -226,23 +37,27 @@ int main(int argc, const char* argv[]) { std::string full_torrent = load_file(argv[1]); std::cout << "Loaded file into string of size " << full_torrent.size() << '\n'; - std::vector values = parse_torrent(full_torrent); + std::vector values = duck::parse_torrent(full_torrent); for (const auto& value : values) { - PrintVisitor visitor; + duck::DebugVisitor visitor; boost::apply_visitor(visitor, value); } - auto source_hashes = find_item("/info/pieces", values); - std::cout << "Got source_hashes with size " << source_hashes.size() << '\n'; - auto hashes = collect_hashes(source_hashes); + auto hashes = duck::collect_hashes(values); std::cout << "Got " << hashes.size() << " hashes\n"; - std::string file_name = std::string{find_item("/info/name", values)}; - std::cout << "Found file name \"" << file_name << "\"\n"; + const auto file_count = duck::find_int("/info/files/[[size]]", values); + if (file_count) + std::cout << "Input has " << file_count << " file entries\n"; + else + std::cout << "Input seems to contain a single file only\n"; - { - const auto piece_length = find_item("/info/piece length", values); + if (0 == file_count) { + std::string file_name = std::string{duck::find_string("/info/name", values)}; + std::cout << "Found file name \"" << file_name << "\"\n"; + + const auto piece_length = duck::find_int("/info/piece length", values); std::ifstream istream(file_name, std::ios::in|std::ios::binary); if (istream.is_open()) { @@ -267,4 +82,11 @@ int main(int argc, const char* argv[]) { std::cout << "Error: opening \"" << file_name << "\" failed\n"; } } + else { + std::string search("/info/files/[["); + for (std::int_fast32_t z = 0; z < static_cast(file_count); ++z) { + const auto path = duck::find_string(search + std::to_string(z) + "]]/path/[[0]]", values); + std::cout << "path index " << z << '\t' << path << '\n'; + } + } } diff --git a/src/meson.build b/src/meson.build index 65789b9..cc19b39 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,6 +2,8 @@ executable(meson.project_name(), 'main.cpp', 'parser.cpp', 'split.cpp', + 'visitors/debug_visitor.cpp', + 'visitors/find_t_visitor.cpp', dependencies: [ boost_dep, libstriezel_dep, diff --git a/src/parser.cpp b/src/parser.cpp index 65443b1..aca2c40 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3,12 +3,15 @@ #endif #include "parser.hpp" +#include "visitors/find_t_visitor.hpp" #include #include #if !defined(NDEBUG) # include #endif +#include +#include namespace boost::spirit::x3::traits { template @@ -128,4 +131,35 @@ std::vector parse_torrent (std::string_view binary_data) { return retval; } + +std::vector> collect_hashes (std::string_view hashes) { + typedef std::array array_type; + + if (hashes.empty()) + return {}; + + constexpr std::size_t hash_size = 20; + constexpr std::size_t uint_count = std::tuple_size(); + static_assert(sizeof(array_type::value_type) * uint_count == hash_size); + + if (hashes.size() % hash_size != 0) + throw std::runtime_error("Bad pieces array size " + std::to_string(hashes.size())); + + std::vector retval(hashes.size() / hash_size); + const char* src = hashes.data(); + for (std::size_t z = 0; z < retval.size(); ++z, src+=hash_size) { + //std::copy_n(src, hash_size, reinterpret_cast(retval[z].data())); + for (std::size_t n = 0; n < uint_count; ++n) { + char* const out_uint = reinterpret_cast(retval[z].data() + n); + for (std::size_t u = 0; u < sizeof(std::uint32_t); ++u) { + out_uint[u] = src[n * sizeof(std::uint32_t) + sizeof(std::uint32_t) - u - 1]; + } + } + } + return retval; +} + +std::vector> collect_hashes (const std::vector& values) { + return collect_hashes(find_string("/info/pieces", values)); +} } //namespace duck diff --git a/src/parser.hpp b/src/parser.hpp index 5bfd91c..c3eb10b 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -4,12 +4,15 @@ #include #include #include +#include +#include namespace duck { typedef std::string_view TorrentStringType; +typedef long long int TorrentIntType; struct TorrentValue : public boost::spirit::x3::variant< - long long, + TorrentIntType, TorrentStringType, boost::spirit::x3::forward_ast< std::vector >, boost::spirit::x3::forward_ast< std::map > @@ -19,5 +22,6 @@ struct TorrentValue : public boost::spirit::x3::variant< }; std::vector parse_torrent (std::string_view binary_data); - +std::vector> collect_hashes (std::string_view hashes); +std::vector> collect_hashes (const std::vector& values); } //namespace duck diff --git a/src/visitors/debug_visitor.cpp b/src/visitors/debug_visitor.cpp new file mode 100644 index 0000000..6d03fc2 --- /dev/null +++ b/src/visitors/debug_visitor.cpp @@ -0,0 +1,88 @@ +#include "debug_visitor.hpp" + +#include +#include +#include +#include +#include + +namespace duck { +namespace { +bool is_printable(std::string_view s) { + return std::find_if(s.cbegin(), s.cend(), + [](unsigned char c){ return not std::isprint(c); } + ) == s.cend(); +} + +} //unnamed namespace + +DebugVisitor::DebugVisitor(int depth, bool skip_first) : + m_depth(depth), m_first_line(skip_first), m_skip_first(skip_first) {} + +void DebugVisitor::reset() { + m_first_line = m_skip_first; +} + +void DebugVisitor::operator() (TorrentIntType value) { + print_tabs(); + std::cout << value << '\n'; +} + +void DebugVisitor::operator() (const TorrentStringType& value) { + constexpr std::size_t max_size = 41; + + print_tabs(); + + std::string_view str{value}; + std::string_view ellip; + if (is_printable(value)) { + if (value.size() > max_size + 3) { + str = str.substr(0, max_size); + ellip = "..."; + } + } + else { + str = ""; + } + + std::cout << "\"" << str << ellip << "\"[" << value.size() << "]\n"; +} + +void DebugVisitor::operator() (const std::vector& value) { + print_tabs(); + std::cout << "[\n"; + + DebugVisitor new_visitor(m_depth+1, false); + for (const auto& item : value) { + boost::apply_visitor(new_visitor, item); + } + + print_tabs(); + std::cout << "]\n"; +} + +void DebugVisitor::operator() (const std::map& value) { + print_tabs(); + std::cout << "{\n"; + + DebugVisitor new_visitor(m_depth+1); + for (const auto& item : value) { + print_tabs(1); + std::cout << item.first << " => "; + + new_visitor.reset(); + boost::apply_visitor(new_visitor, item.second); + } + print_tabs(); + std::cout << "}\n"; +} + +void DebugVisitor::print_tabs(int extra) { + if (!m_first_line) { + for (int z = 0; z < m_depth+extra; ++z) std::cout << '\t'; + } + else { + m_first_line = false; + } +} +} //namespace duck diff --git a/src/visitors/debug_visitor.hpp b/src/visitors/debug_visitor.hpp new file mode 100644 index 0000000..2ce049e --- /dev/null +++ b/src/visitors/debug_visitor.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "../parser.hpp" + +#include + +namespace duck { +class DebugVisitor { +public: + DebugVisitor(int depth=0, bool skip_first=true); + + void reset(); + void operator() (TorrentIntType value); + void operator() (const TorrentStringType& value); + void operator() (const std::vector& value); + void operator() (const std::map& value); + +private: + void print_tabs(int extra=0); + + int m_depth; + bool m_first_line; + bool m_skip_first; +}; +} //namespace duck diff --git a/src/visitors/find_t_visitor.cpp b/src/visitors/find_t_visitor.cpp new file mode 100644 index 0000000..e16ae97 --- /dev/null +++ b/src/visitors/find_t_visitor.cpp @@ -0,0 +1,91 @@ +#include "find_t_visitor.hpp" +#include "split.hpp" + +#include +#include +#include + +namespace duck::detail { +template +FindTVisitor::FindTVisitor (std::string_view search_path) { + auto split = dincore::split(search_path, '/', true, true); + m_search.reserve(split.size()); + for (const auto& itm : split) { + m_search.emplace_back(itm.data(), itm.size()); + } +} + +template +void FindTVisitor::reset() { + m_search_error = false; + m_search_match_count = 0; +} + +template +bool FindTVisitor::has_error() const noexcept { + return m_search_error; +} + +template +T FindTVisitor::operator() (TorrentIntType value) { + if constexpr (std::is_same::value) + return value; + m_search_error = true; + return {}; +} + +template +T FindTVisitor::operator() (const TorrentStringType& value) { + if constexpr (std::is_same::value) { + if (is_path_found()) + return value; + } + 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]]") + return static_cast(values.size()); + } + + std::regex reg_index(R"(^\[\[(\d+)\]\]$)"); + std::smatch match; + const bool matched = std::regex_match(m_search[m_search_match_count], match, reg_index); + if (matched) { + ++m_search_match_count; + const auto index = static_cast(std::stoul(match[1])); + if (index < values.size()) + return boost::apply_visitor(*this, values[index]); + } + return {}; +} + +template +T FindTVisitor::operator() (const std::map& value) { + if (is_path_found()) { + m_search_error = true; + return {}; + } + + auto it_found = value.find(m_search[m_search_match_count]); + if (value.end() == it_found) + return {}; + + ++m_search_match_count; + return boost::apply_visitor(*this, it_found->second); +} + +template +bool FindTVisitor::is_path_found() const { + return m_search.size() == m_search_match_count; +} + +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 new file mode 100644 index 0000000..aa529aa --- /dev/null +++ b/src/visitors/find_t_visitor.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "../parser.hpp" +#include +#include +#include +#include +#include + +namespace duck { +namespace detail { +template +class FindTVisitor : boost::static_visitor { +public: + FindTVisitor (std::string_view search_path); + + void reset(); + 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); + +private: + bool is_path_found() const; + + std::vector m_search; + std::size_t m_search_match_count{0}; + bool m_search_error{false}; +}; +} //namespace detail + +typedef detail::FindTVisitor FindStringVisitor; +typedef detail::FindTVisitor FindIntVisitor; + +template +inline T find_item(std::string_view path, const std::vector& values) { + detail::FindTVisitor visitor(path); + for (const auto& value : values) { + visitor.reset(); + T found = boost::apply_visitor(visitor, value); + if (not visitor.has_error()) + return found; + } + return {}; +} + +inline TorrentIntType find_int(std::string_view path, const std::vector& values) { + return find_item(path, values); +} + +inline TorrentStringType find_string(std::string_view path, const std::vector& values) { + return find_item(path, values); +} +} //namespace duck