Refactor visitors into separate files
This also includes some small improvements that I don't remember right now
This commit is contained in:
parent
365a888511
commit
72d52860f6
8 changed files with 323 additions and 202 deletions
222
src/main.cpp
222
src/main.cpp
|
@ -1,18 +1,12 @@
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "split.hpp"
|
#include "visitors/debug_visitor.hpp"
|
||||||
|
#include "visitors/find_t_visitor.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cctype>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ciso646>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <array>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <climits>
|
|
||||||
#include <boost/variant/apply_visitor.hpp>
|
|
||||||
#include <sha1/sha1.hpp>
|
#include <sha1/sha1.hpp>
|
||||||
#include <sha1/BufferSource.hpp>
|
#include <sha1/BufferSource.hpp>
|
||||||
|
|
||||||
|
@ -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 = "<binary_data>";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\"" << str << ellip << "\"[" << value.size() << "]\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (const std::vector<duck::TorrentValue>& 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<duck::TorrentStringType, duck::TorrentValue>& 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 <typename T>
|
|
||||||
struct FindTVisitor : boost::static_visitor<T> {
|
|
||||||
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<long long, T>::value)
|
|
||||||
return value;
|
|
||||||
search_error = true;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
T operator() (const duck::TorrentStringType& value) {
|
|
||||||
if constexpr (std::is_same<duck::TorrentStringType, T>::value) {
|
|
||||||
if (is_path_found())
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
search_error = true;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
T operator() (const std::vector<duck::TorrentValue>&) {
|
|
||||||
search_error = true;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
T operator() (const std::map<duck::TorrentStringType, duck::TorrentValue>& 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<std::string> search;
|
|
||||||
std::size_t search_match_count{0};
|
|
||||||
bool search_error{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::array<std::uint32_t, 5>> collect_hashes (std::string_view hashes) {
|
|
||||||
typedef std::array<std::uint32_t, 5> array_type;
|
|
||||||
|
|
||||||
if (hashes.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
constexpr std::size_t hash_size = 20;
|
|
||||||
constexpr std::size_t uint_count = std::tuple_size<array_type>();
|
|
||||||
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<array_type> 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<char*>(retval[z].data()));
|
|
||||||
for (std::size_t n = 0; n < uint_count; ++n) {
|
|
||||||
char* const out_uint = reinterpret_cast<char*>(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 <typename T>
|
|
||||||
T find_item(std::string_view path, const std::vector<duck::TorrentValue>& values) {
|
|
||||||
FindTVisitor<T> 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<std::uint32_t, 5>& arr) {
|
SHA1::MessageDigest to_hash160_digest (const std::array<std::uint32_t, 5>& arr) {
|
||||||
SHA1::MessageDigest hash_found;
|
SHA1::MessageDigest hash_found;
|
||||||
std::copy_n(arr.begin(), 5, hash_found.hash);
|
std::copy_n(arr.begin(), 5, hash_found.hash);
|
||||||
|
@ -215,9 +29,6 @@ SHA1::MessageDigest to_hash160_digest (const std::array<std::uint32_t, 5>& arr)
|
||||||
} //unnamed namespace
|
} //unnamed namespace
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
using duck::parse_torrent;
|
|
||||||
typedef duck::TorrentStringType torrent_string;
|
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
std::cerr << "Wrong number of parameters. Usage:\n"
|
std::cerr << "Wrong number of parameters. Usage:\n"
|
||||||
<< argv[0] << " <path_to_torrent>\n";
|
<< argv[0] << " <path_to_torrent>\n";
|
||||||
|
@ -226,23 +37,27 @@ int main(int argc, const char* argv[]) {
|
||||||
|
|
||||||
std::string full_torrent = load_file(argv[1]);
|
std::string full_torrent = load_file(argv[1]);
|
||||||
std::cout << "Loaded file into string of size " << full_torrent.size() << '\n';
|
std::cout << "Loaded file into string of size " << full_torrent.size() << '\n';
|
||||||
std::vector<duck::TorrentValue> values = parse_torrent(full_torrent);
|
std::vector<duck::TorrentValue> values = duck::parse_torrent(full_torrent);
|
||||||
|
|
||||||
for (const auto& value : values) {
|
for (const auto& value : values) {
|
||||||
PrintVisitor visitor;
|
duck::DebugVisitor visitor;
|
||||||
boost::apply_visitor(visitor, value);
|
boost::apply_visitor(visitor, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto source_hashes = find_item<torrent_string>("/info/pieces", values);
|
auto hashes = duck::collect_hashes(values);
|
||||||
std::cout << "Got source_hashes with size " << source_hashes.size() << '\n';
|
|
||||||
auto hashes = collect_hashes(source_hashes);
|
|
||||||
std::cout << "Got " << hashes.size() << " hashes\n";
|
std::cout << "Got " << hashes.size() << " hashes\n";
|
||||||
|
|
||||||
std::string file_name = std::string{find_item<torrent_string>("/info/name", values)};
|
const auto file_count = duck::find_int("/info/files/[[size]]", values);
|
||||||
std::cout << "Found file name \"" << file_name << "\"\n";
|
if (file_count)
|
||||||
|
std::cout << "Input has " << file_count << " file entries\n";
|
||||||
|
else
|
||||||
|
std::cout << "Input seems to contain a single file only\n";
|
||||||
|
|
||||||
{
|
if (0 == file_count) {
|
||||||
const auto piece_length = find_item<long long int>("/info/piece length", values);
|
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);
|
std::ifstream istream(file_name, std::ios::in|std::ios::binary);
|
||||||
|
|
||||||
if (istream.is_open()) {
|
if (istream.is_open()) {
|
||||||
|
@ -267,4 +82,11 @@ int main(int argc, const char* argv[]) {
|
||||||
std::cout << "Error: opening \"" << file_name << "\" failed\n";
|
std::cout << "Error: opening \"" << file_name << "\" failed\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
std::string search("/info/files/[[");
|
||||||
|
for (std::int_fast32_t z = 0; z < static_cast<std::int_fast32_t>(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';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ executable(meson.project_name(),
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
'parser.cpp',
|
'parser.cpp',
|
||||||
'split.cpp',
|
'split.cpp',
|
||||||
|
'visitors/debug_visitor.cpp',
|
||||||
|
'visitors/find_t_visitor.cpp',
|
||||||
dependencies: [
|
dependencies: [
|
||||||
boost_dep,
|
boost_dep,
|
||||||
libstriezel_dep,
|
libstriezel_dep,
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
#include "visitors/find_t_visitor.hpp"
|
||||||
|
|
||||||
#include <boost/spirit/home/x3.hpp>
|
#include <boost/spirit/home/x3.hpp>
|
||||||
#include <boost/fusion/adapted/std_pair.hpp>
|
#include <boost/fusion/adapted/std_pair.hpp>
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
#endif
|
#endif
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
namespace boost::spirit::x3::traits {
|
namespace boost::spirit::x3::traits {
|
||||||
template <typename Char, typename Trait>
|
template <typename Char, typename Trait>
|
||||||
|
@ -128,4 +131,35 @@ std::vector<TorrentValue> parse_torrent (std::string_view binary_data) {
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::array<std::uint32_t, 5>> collect_hashes (std::string_view hashes) {
|
||||||
|
typedef std::array<std::uint32_t, 5> array_type;
|
||||||
|
|
||||||
|
if (hashes.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
constexpr std::size_t hash_size = 20;
|
||||||
|
constexpr std::size_t uint_count = std::tuple_size<array_type>();
|
||||||
|
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<array_type> 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<char*>(retval[z].data()));
|
||||||
|
for (std::size_t n = 0; n < uint_count; ++n) {
|
||||||
|
char* const out_uint = reinterpret_cast<char*>(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<std::array<std::uint32_t, 5>> collect_hashes (const std::vector<TorrentValue>& values) {
|
||||||
|
return collect_hashes(find_string("/info/pieces", values));
|
||||||
|
}
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
|
@ -4,12 +4,15 @@
|
||||||
#include <boost/spirit/home/x3/support/ast/variant.hpp>
|
#include <boost/spirit/home/x3/support/ast/variant.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace duck {
|
namespace duck {
|
||||||
typedef std::string_view TorrentStringType;
|
typedef std::string_view TorrentStringType;
|
||||||
|
typedef long long int TorrentIntType;
|
||||||
|
|
||||||
struct TorrentValue : public boost::spirit::x3::variant<
|
struct TorrentValue : public boost::spirit::x3::variant<
|
||||||
long long,
|
TorrentIntType,
|
||||||
TorrentStringType,
|
TorrentStringType,
|
||||||
boost::spirit::x3::forward_ast< std::vector<TorrentValue> >,
|
boost::spirit::x3::forward_ast< std::vector<TorrentValue> >,
|
||||||
boost::spirit::x3::forward_ast< std::map<TorrentStringType, TorrentValue> >
|
boost::spirit::x3::forward_ast< std::map<TorrentStringType, TorrentValue> >
|
||||||
|
@ -19,5 +22,6 @@ struct TorrentValue : public boost::spirit::x3::variant<
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<TorrentValue> parse_torrent (std::string_view binary_data);
|
std::vector<TorrentValue> parse_torrent (std::string_view binary_data);
|
||||||
|
std::vector<std::array<std::uint32_t, 5>> collect_hashes (std::string_view hashes);
|
||||||
|
std::vector<std::array<std::uint32_t, 5>> collect_hashes (const std::vector<TorrentValue>& values);
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
88
src/visitors/debug_visitor.cpp
Normal file
88
src/visitors/debug_visitor.cpp
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include "debug_visitor.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <ciso646>
|
||||||
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
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 = "<binary_data>";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\"" << str << ellip << "\"[" << value.size() << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugVisitor::operator() (const std::vector<TorrentValue>& 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<TorrentStringType, TorrentValue>& 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
|
25
src/visitors/debug_visitor.hpp
Normal file
25
src/visitors/debug_visitor.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../parser.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
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<duck::TorrentValue>& value);
|
||||||
|
void operator() (const std::map<duck::TorrentStringType, duck::TorrentValue>& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void print_tabs(int extra=0);
|
||||||
|
|
||||||
|
int m_depth;
|
||||||
|
bool m_first_line;
|
||||||
|
bool m_skip_first;
|
||||||
|
};
|
||||||
|
} //namespace duck
|
91
src/visitors/find_t_visitor.cpp
Normal file
91
src/visitors/find_t_visitor.cpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#include "find_t_visitor.hpp"
|
||||||
|
#include "split.hpp"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
namespace duck::detail {
|
||||||
|
template <typename T>
|
||||||
|
FindTVisitor<T>::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 <typename T>
|
||||||
|
void FindTVisitor<T>::reset() {
|
||||||
|
m_search_error = false;
|
||||||
|
m_search_match_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool FindTVisitor<T>::has_error() const noexcept {
|
||||||
|
return m_search_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FindTVisitor<T>::operator() (TorrentIntType value) {
|
||||||
|
if constexpr (std::is_same<TorrentIntType, T>::value)
|
||||||
|
return value;
|
||||||
|
m_search_error = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FindTVisitor<T>::operator() (const TorrentStringType& value) {
|
||||||
|
if constexpr (std::is_same<TorrentStringType, T>::value) {
|
||||||
|
if (is_path_found())
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
m_search_error = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FindTVisitor<T>::operator() (const std::vector<TorrentValue>& values) {
|
||||||
|
if (is_path_found())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if constexpr (std::is_same<TorrentIntType, T>::value) {
|
||||||
|
if (m_search[m_search_match_count] == "[[size]]")
|
||||||
|
return static_cast<TorrentIntType>(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::size_t>(std::stoul(match[1]));
|
||||||
|
if (index < values.size())
|
||||||
|
return boost::apply_visitor(*this, values[index]);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FindTVisitor<T>::operator() (const std::map<TorrentStringType, TorrentValue>& 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 <typename T>
|
||||||
|
bool FindTVisitor<T>::is_path_found() const {
|
||||||
|
return m_search.size() == m_search_match_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template class FindTVisitor<TorrentStringType>;
|
||||||
|
template class FindTVisitor<TorrentIntType>;
|
||||||
|
} //namespace duck::detail
|
55
src/visitors/find_t_visitor.hpp
Normal file
55
src/visitors/find_t_visitor.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../parser.hpp"
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <ciso646>
|
||||||
|
|
||||||
|
namespace duck {
|
||||||
|
namespace detail {
|
||||||
|
template <typename T>
|
||||||
|
class FindTVisitor : boost::static_visitor<T> {
|
||||||
|
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<TorrentValue>&);
|
||||||
|
T operator() (const std::map<TorrentStringType, TorrentValue>& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_path_found() const;
|
||||||
|
|
||||||
|
std::vector<std::string> m_search;
|
||||||
|
std::size_t m_search_match_count{0};
|
||||||
|
bool m_search_error{false};
|
||||||
|
};
|
||||||
|
} //namespace detail
|
||||||
|
|
||||||
|
typedef detail::FindTVisitor<TorrentStringType> FindStringVisitor;
|
||||||
|
typedef detail::FindTVisitor<TorrentIntType> FindIntVisitor;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T find_item(std::string_view path, const std::vector<TorrentValue>& values) {
|
||||||
|
detail::FindTVisitor<T> 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<TorrentValue>& values) {
|
||||||
|
return find_item<TorrentIntType>(path, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TorrentStringType find_string(std::string_view path, const std::vector<TorrentValue>& values) {
|
||||||
|
return find_item<TorrentStringType>(path, values);
|
||||||
|
}
|
||||||
|
} //namespace duck
|
Loading…
Add table
Reference in a new issue