Add a read_announce_list() to TorrentRead

announce-list is very annoyingly a list of lists. I don't
have any useful primitive to manage that case in TorrentRead,
so for now I've done a not-so-elegant implementation where I
step around the visitor, use my knowledge of the variant and
access the outer list directly through boost::get(). I'm not
necessarily convinced that this is bad since on the other hand
the solution consistent with the rest of the implementation
would involve searching for "/[[x]]/[[y]]" which has its own
overhead. I mean, variant contains a vector, I can access it
normally. Visitor is a convenience thing, I don't see why
parser's client code should be obliged to pretend the variant
cannot be accessed directly.
This commit is contained in:
King_DuckZ 2025-04-07 02:39:35 +01:00
commit d04b4d8274
3 changed files with 61 additions and 20 deletions

View file

@ -49,6 +49,16 @@ int main(int argc, const char* argv[]) {
std::cout << "Creation date: " << torrent.read_creation_date() << '\n';
std::cout << "Comment: " << torrent.read_comment() << '\n';
std::cout << "Announce: " << torrent.read_announce() << '\n';
{
std::size_t z = 0;
for (const std::vector<std::string_view>& sublist : torrent.read_announce_list()) {
std::cout << '\t' << ++z << ": < ";
for (std::string_view announce : sublist) {
std::cout << '"' << announce << "\", ";
}
std::cout << " >\n";
}
}
std::cout << "Is private: " << std::boolalpha << torrent.read_is_private() << '\n';
torrent.print(std::cout);

View file

@ -87,6 +87,31 @@ std::string_view build_fast_search_path (
return retval;
}
std::vector<std::string_view> grab_all_strings_in_list (const TorrentValue& node) {
const auto path_size = find_int<std::int_fast32_t>("/[[size]]", node);
std::vector<std::string_view> retval;
retval.reserve(path_size);
{
constexpr char prefix[] = "/[[";
constexpr char postfix[] = "]]";
constexpr std::size_t prefix_size = sizeof(prefix) / sizeof(prefix[0]) - 1u;
constexpr std::size_t postfix_size = sizeof(postfix) / sizeof(postfix[0]) - 1u;
char buff[4 + prefix_size + postfix_size];
std::string buff2;
for (std::int_fast32_t z = 0; z < path_size; ++z) {
std::string_view piece = find_string(
build_fast_search_path(prefix, postfix, z, buff, buff2),
node
);
retval.push_back(piece);
}
}
return retval;
}
} //unnamed namespace
TorrentRead::TorrentRead (std::string_view torrent_path, std::filesystem::path workdir) :
@ -129,7 +154,6 @@ std::int_fast32_t TorrentRead::read_file_count() const {
std::vector<std::string_view> TorrentRead::read_file_path(std::size_t index) const {
const TorrentValue* path_variant;
std::string buff2;
{
constexpr char prefix[] = "/[[";
@ -140,6 +164,7 @@ std::vector<std::string_view> TorrentRead::read_file_path(std::size_t index) con
//see comment in read_file_size()
//in this case a search should look like "/[[1234]]/path"
char buff[4 + prefix_size + postfix_size];
std::string buff2;
auto found = find_variant(
build_fast_search_path(prefix, postfix, index, buff, buff2),
@ -151,25 +176,7 @@ std::vector<std::string_view> TorrentRead::read_file_path(std::size_t index) con
path_variant = &found->get();
}
const auto path_size = find_int<std::int_fast32_t>("/[[size]]", *path_variant);
std::vector<std::string_view> retval;
retval.reserve(path_size);
{
constexpr char prefix[] = "/[[";
constexpr char postfix[] = "]]";
constexpr std::size_t prefix_size = sizeof(prefix) / sizeof(prefix[0]) - 1u;
constexpr std::size_t postfix_size = sizeof(postfix) / sizeof(postfix[0]) - 1u;
char buff[4 + prefix_size + postfix_size];
for (std::int_fast32_t z = 0; z < path_size; ++z) {
std::string_view piece = find_string(
build_fast_search_path(prefix, postfix, z, buff, buff2),
*path_variant
);
retval.push_back(piece);
}
}
return retval;
return grab_all_strings_in_list(*path_variant);
}
std::string TorrentRead::read_joint_file_path(std::size_t index, char sep) const {
@ -224,6 +231,29 @@ std::string_view TorrentRead::read_announce() const {
return find_string("/announce", m_parsed_values);
}
std::vector<std::vector<std::string_view>> TorrentRead::read_announce_list() const {
typedef boost::spirit::x3::forward_ast<std::vector<TorrentValue>> TorrentListType;
//see https://bittorrent.org/beps/bep_0012.html it's a list of lists
auto list_variant = find_variant("/announce-list", m_parsed_values);
if (list_variant) {
auto* node_list = boost::get<TorrentListType>(&list_variant->get());
std::vector<std::vector<std::string_view>> retval;
if (node_list) {
const auto list_size = static_cast<std::int_fast32_t>(node_list->get().size());
retval.reserve(list_size);
for (std::int_fast32_t z = 0; z < list_size; ++z) {
retval.push_back(grab_all_strings_in_list(node_list->get()[z]));
}
}
return retval;
}
else {
//from my tests this node is optional
return {};
}
}
std::time_t TorrentRead::read_creation_date() const {
return find_int<std::time_t>("/creation date", m_parsed_values);
}

View file

@ -55,6 +55,7 @@ public:
std::string_view read_hashes() const;
std::string_view read_created_by() const;
std::string_view read_announce() const;
std::vector<std::vector<std::string_view>> read_announce_list() const;
std::time_t read_creation_date() const;
private: