mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-21 16:46:18 +00:00
Parse multicall commands once per call
Keep the change to d, f, p and t multicalls. Parse each column lazily on first use and evaluate it through rpc::call_object, preserving per-target argument expansion. Compared unpatched and patched builds on macOS: output was byte-identical across the test harness, including an erase-mid-call check.
This commit is contained in:
@@ -123,6 +123,39 @@ parse_command(target_type target, const char* first, const char* last) {
|
||||
return std::make_pair(commands.call_command(key, args, target), first);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
parse_command_object(const char* first, const char* last) {
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
if (first == last || *first == '#')
|
||||
return torrent::Object();
|
||||
|
||||
char key[128];
|
||||
|
||||
first = parse_command_name(first, last, key, key + 128);
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
if (first == last || *first != '=')
|
||||
throw torrent::input_error("Could not find '=' in command '" + std::string(key) + "'.");
|
||||
|
||||
torrent::Object result = torrent::Object::create_dict_key();
|
||||
|
||||
result.as_dict_key() = key;
|
||||
|
||||
first = parse_whole_list(first + 1, last, &result.as_dict_obj(), &parse_is_delim_command);
|
||||
|
||||
// Find the last character that is part of this command, skipping
|
||||
// the whitespace at the end.
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
// This helper accepts exactly one command and cannot return where a next
|
||||
// command begins, so reject every non-whitespace suffix, including ';'.
|
||||
if (first != last && *first != '\0')
|
||||
throw torrent::input_error("Junk at end of input.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
parse_command_multiple(target_type target, const char* first, const char* last) {
|
||||
parse_command_type result;
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xmlrpc.h"
|
||||
#include "rpc_manager.h"
|
||||
@@ -67,6 +70,34 @@ parse_command_single(target_type target, const std::string& cmd) {
|
||||
return parse_command(target, cmd.c_str(), cmd.c_str() + cmd.size()).first;
|
||||
}
|
||||
|
||||
// Parse one RPC command without executing it. Repeated evaluations can use
|
||||
// call_object on the result, which handles per-target argument expansion.
|
||||
torrent::Object parse_command_object(const char* first, const char* last);
|
||||
|
||||
inline torrent::Object parse_command_object(const std::string& cmd) {
|
||||
return parse_command_object(cmd.c_str(), cmd.c_str() + cmd.size());
|
||||
}
|
||||
|
||||
// Prepare a multicall's commands once, on the first target that uses them. This
|
||||
// keeps empty target lists from parsing commands that would never be evaluated.
|
||||
struct preparsed_commands : public std::vector<torrent::Object> {
|
||||
explicit preparsed_commands(std::function<void(preparsed_commands&)> prepare)
|
||||
: m_prepare(std::move(prepare)) {}
|
||||
|
||||
void prepare_if_needed() {
|
||||
if (m_prepare) {
|
||||
// Clear before invoking: the callback may inspect this vector, and a
|
||||
// throwing callback must not be run again against partially added items.
|
||||
auto prepare = std::move(m_prepare);
|
||||
m_prepare = {};
|
||||
prepare(*this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(preparsed_commands&)> m_prepare;
|
||||
};
|
||||
|
||||
inline torrent::Object
|
||||
parse_command_multiple_std(const std::string& cmd, target_type target = rpc::make_target()) {
|
||||
return parse_command_multiple(target, cmd.c_str(), cmd.c_str() + cmd.size());
|
||||
|
||||
Reference in New Issue
Block a user