* Optimized view filtering and sorting.

git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1213 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
rakshasa
2011-04-20 15:14:02 +00:00
parent 8e15749d0c
commit ea9dc35bbe
10 changed files with 200 additions and 115 deletions
+34 -27
View File
@@ -27,11 +27,11 @@ gnuplot << EOF
# 4) throttle.global_down.rate
# 5) throttle.global_down.total
#
# 9) pieces.memory.current
# 10) pieces.memory.sync_queue
# 11) pieces.memory.block_count
# 12) pieces.stats.total_size
# 13) pieces.hash.queue_size
# 6) pieces.memory.current
# 7) pieces.memory.sync_queue
# 8) pieces.memory.block_count
# 9) pieces.stats.total_size
# 10) pieces.hash.queue_size
#
# schedule = log_bandwidth_stats,5,10,((execute,log_rtorrent.sh,((cat,/foo/bandwidth_stats.,((system.pid)))),((system.time_seconds)),((throttle.global_up.rate)),((throttle.global_up.total)),((throttle.global_down.rate)),((throttle.global_down.total)),((pieces.memory.current)),((pieces.memory.sync_queue)),((pieces.memory.block_count)),((pieces.stats.total_size)),((pieces.hash.queue_size))))
#
@@ -52,57 +52,64 @@ set terminal png size 1024,600
set xdata time
set timefmt "%s"
set format x "%H:%M"
set format y "%.0s %cb"
set format y2 "%.0f"
set y2tics
set autoscale xfix
grab(x)=(x<min)?min=x:(x>max)?max=x:0;
#div100(x)=(x/100)
divrak(v)=(v>0)?-v:0;
set output "output_$1_sockets.png"
plot "peer_stats.$1" using 1:7 smooth bezier with lines lw 4 title 'active',\
"peer_stats.$1" using 1:5 smooth bezier with lines lw 4 title 'leeching',\
"peer_stats.$1" using 1:6 smooth bezier with lines lw 4 title 'seeding',\
"<awk '{x=1000; if (\$4 < 1000) x=\$4; print \$1,x}' peer_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'open sockets' axis x1y2,\
"peer_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'upload unchoked' axis x1y2,\
"peer_stats.$1" using 1:3 smooth bezier with lines lw 2 title 'download unchoked' axis x1y2
# "peer_stats.$1" using 1:4 smooth bezier with lines lw 2 title 'open sockets' axis x1y2,\
set format y "%.0s %cb"
set output "output_$1_bandwidth.png"
plot "bandwidth_stats.$1" using 1:2 smooth bezier with lines lw 4 title 'Upload rate',\
"bandwidth_stats.$1" using 1:4 smooth bezier with lines lw 4 title 'Download rate',\
"bandwidth_stats.$1" using 1:6 smooth bezier with lines lw 2 title 'Upload unchoked' axis x1y2,\
"bandwidth_stats.$1" using 1:7 smooth bezier with lines lw 2 title 'Download unchoked' axis x1y2
"peer_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Upload unchoked' axis x1y2,\
"peer_stats.$1" using 1:3 smooth bezier with lines lw 2 title 'Download unchoked' axis x1y2
set output "output_$1_memory.png"
plot "bandwidth_stats.$1" using 1:9 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"bandwidth_stats.$1" using 1:6 smooth bezier with lines lw 2 title 'Upload unchoked' axis x1y2,\
"bandwidth_stats.$1" using 1:7 smooth bezier with lines lw 2 title 'Download unchoked' axis x1y2
plot "bandwidth_stats.$1" using 1:6 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"peer_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Upload unchoked' axis x1y2,\
"peer_stats.$1" using 1:3 smooth bezier with lines lw 2 title 'Download unchoked' axis x1y2
#set yrange [0:3900000000.0]
#set y2range [0:2000]
set output "output_$1_pieces.png"
plot "bandwidth_stats.$1" using 1:9 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"bandwidth_stats.$1" using 1:10 smooth bezier with lines lw 4 title 'Sync queue Memory' axis x1y1,\
plot "bandwidth_stats.$1" using 1:6 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"bandwidth_stats.$1" using 1:7 smooth bezier with lines lw 4 title 'Sync queue Memory' axis x1y1,\
"<awk '{x=\$12*10; print \$1,x}' mincore_stats.$1" using 1:2 smooth bezier with lines lw 4 title 'Alloc /100s' axis x1y1,\
"<awk '{x=\$13*10; print \$1,x}' mincore_stats.$1" using 1:2 smooth bezier with lines lw 4 title 'Dealloc /100s' axis x1y1,\
"mincore_stats.$1" using 1:7 smooth bezier with lines lw 2 title 'Sync success /10s' axis x1y2,\
"mincore_stats.$1" using 1:8 smooth bezier with lines lw 2 title 'Sync failed /10s' axis x1y2,\
"mincore_stats.$1" using 1:7 smooth bezier with lines lw 2 title 'Sync success /10s' axis x1y2,\
"mincore_stats.$1" using 1:8 smooth bezier with lines lw 2 title 'Sync failed /10s' axis x1y2,\
"mincore_stats.$1" using 1:11 smooth bezier with lines lw 2 title 'Alloc failed /10s' axis x1y2,\
"<awk '{x=\$11/10; print \$1,x}' bandwidth_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Block count /10' axis x1y2,\
"bandwidth_stats.$1" using 1:13 smooth bezier with lines lw 2 title 'Hash queue count' axis x1y2
# "<awk '{x=\$13/10; print \$1,x}' bandwidth_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Hash queue count /10' axis x1y2
"<awk '{x=\$8/10; print \$1,x}' bandwidth_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Block count /10' axis x1y2,\
"bandwidth_stats.$1" using 1:10 smooth bezier with lines lw 2 title 'Hash queue count' axis x1y2
#set yrange [*:*]
#set y2range [*:*]
set format y2 "%.0s %cb"
set output "output_$1_torrents.png"
plot "bandwidth_stats.$1" using 1:9 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"bandwidth_stats.$1" using 1:12 smooth bezier with lines lw 2 title 'Pieces total size' axis x1y2
plot "bandwidth_stats.$1" using 1:6 smooth bezier with lines lw 4 title 'Memory usage' axis x1y1,\
"bandwidth_stats.$1" using 1:9 smooth bezier with lines lw 2 title 'Pieces total size' axis x1y2
set format y "%.0f"
set output "output_$1_incore.png"
plot "bandwidth_stats.$1" using 1:(0) smooth bezier with lines title '',\
"mincore_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Incore cont.' axis x1y1,\
"mincore_stats.$1" using 1:3 smooth bezier with lines lw 4 title 'Incore new' axis x1y2,\
"mincore_stats.$1" using 1:4 smooth bezier with lines lw 2 title 'Not incore cont.' axis x1y1,\
"mincore_stats.$1" using 1:5 smooth bezier with lines lw 4 title 'Not incore new' axis x1y2
"mincore_stats.$1" using 1:2 smooth bezier with lines lw 2 title 'Incore cont. /10s' axis x1y1,\
"mincore_stats.$1" using 1:3 smooth bezier with lines lw 4 title 'Incore new /10s' axis x1y2,\
"mincore_stats.$1" using 1:4 smooth bezier with lines lw 2 title 'Not incore cont. /10s' axis x1y1,\
"mincore_stats.$1" using 1:5 smooth bezier with lines lw 4 title 'Not incore new /10s' axis x1y2
EOF
+54 -16
View File
@@ -53,7 +53,8 @@
#include "control.h"
#include "command_helpers.h"
typedef void (core::ViewManager::*view_cfilter_slot)(const std::string&, const std::string&);
typedef void (core::ViewManager::*view_cfilter_slot)(const std::string&, const torrent::Object&);
typedef void (core::ViewManager::*view_event_slot)(const std::string&, const std::string&);
torrent::Object
apply_view_filter_on(const torrent::Object::list_type& args) {
@@ -82,6 +83,21 @@ apply_view_cfilter(view_cfilter_slot viewFilterSlot, const torrent::Object::list
const std::string& name = args.front().as_string();
if (name.empty())
throw torrent::input_error("First argument must be a string.");
(control->view_manager()->*viewFilterSlot)(name, args.back());
return torrent::Object();
}
torrent::Object
apply_view_event(view_event_slot viewFilterSlot, const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Too few arguments.");
const std::string& name = args.front().as_string();
if (name.empty())
throw torrent::input_error("First argument must be a string.");
@@ -156,7 +172,7 @@ apply_cat(rpc::target_type target, const torrent::Object& rawArgs) {
// Move these boolean operators to a new file.
bool
inline bool
as_boolean(const torrent::Object& rawArgs) {
switch (rawArgs.type()) {
case torrent::Object::TYPE_VALUE: return rawArgs.as_value();
@@ -169,6 +185,11 @@ as_boolean(const torrent::Object& rawArgs) {
torrent::Object
apply_not(rpc::target_type target, const torrent::Object& rawArgs) {
if (rawArgs.is_dict_key())
return (int64_t)!as_boolean(rpc::commands.call_command(rawArgs.as_dict_key().c_str(),
rawArgs.as_dict_obj(),
target));
return (int64_t)!as_boolean(rawArgs);
}
@@ -183,8 +204,14 @@ apply_and(rpc::target_type target, const torrent::Object& rawArgs) {
return as_boolean(rawArgs);
for (torrent::Object::list_const_iterator itr = rawArgs.as_list().begin(), last = rawArgs.as_list().end(); itr != last; itr++)
if (!as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)false;
if (itr->is_dict_key()) {
if (!as_boolean(rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target)))
return (int64_t)false;
} else {
if (!as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)false;
}
return (int64_t)true;
}
@@ -195,8 +222,14 @@ apply_or(rpc::target_type target, const torrent::Object& rawArgs) {
return as_boolean(rawArgs);
for (torrent::Object::list_const_iterator itr = rawArgs.as_list().begin(), last = rawArgs.as_list().end(); itr != last; itr++)
if (as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)true;
if (itr->is_dict_key()) {
if (as_boolean(rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target)))
return (int64_t)true;
} else {
if (as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)true;
}
return (int64_t)false;
}
@@ -215,13 +248,18 @@ apply_cmp(rpc::target_type target, const torrent::Object::list_type& args) {
torrent::Object result1;
torrent::Object result2;
if (rpc::is_target_pair(target)) {
result1 = rpc::parse_command_single(rpc::get_target_left(target), args.front().as_string());
result2 = rpc::parse_command_single(rpc::get_target_right(target), args.back().as_string());
} else {
result1 = rpc::parse_command_single(target, args.front().as_string());
result2 = rpc::parse_command_single(target, args.back().as_string());
}
rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target;
rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target;
if (args.front().is_dict_key())
result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1);
else
result1 = rpc::parse_command_single(target1, args.front().as_string());
if (args.back().is_dict_key())
result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2);
else
result2 = rpc::parse_command_single(target2, args.back().as_string());
if (result1.type() != result2.type())
throw torrent::input_error("Type mismatch.");
@@ -410,7 +448,7 @@ torrent::Object
cmd_view_persistent(const torrent::Object::string_type& args) {
core::View* view = *control->view_manager()->find_throw(args);
if (!view->get_filter().empty() || !view->get_event_added().empty() || !view->get_event_removed().empty())
if (!view->get_filter().is_empty() || !view->get_event_added().empty() || !view->get_event_removed().empty())
throw torrent::input_error("Cannot set modified views as persitent.");
view->set_filter("d.views.has=" + args);
@@ -471,8 +509,8 @@ initialize_command_ui() {
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_cfilter, &core::ViewManager::set_sort_new, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_current", std::bind(&apply_view_cfilter, &core::ViewManager::set_sort_current, std::placeholders::_2));
CMD2_ANY_LIST("view.event_added", std::bind(&apply_view_cfilter, &core::ViewManager::set_event_added, std::placeholders::_2));
CMD2_ANY_LIST("view.event_removed", std::bind(&apply_view_cfilter, &core::ViewManager::set_event_removed, std::placeholders::_2));
CMD2_ANY_LIST("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2));
CMD2_ANY_LIST("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2));
// Cleanup and add . to view.
+39 -8
View File
@@ -55,14 +55,28 @@ namespace core {
// Also add focus thingie here?
struct view_downloads_compare : std::binary_function<Download*, Download*, bool> {
view_downloads_compare(const std::string& cmd) : m_command(cmd) {}
view_downloads_compare(const torrent::Object& cmd) : m_command(cmd) {}
bool operator () (Download* d1, Download* d2) const {
try {
if (m_command.empty())
if (m_command.is_empty())
return false;
return rpc::parse_command_single(rpc::make_target_pair(d1, d2), m_command).as_value();
if (!m_command.is_dict_key())
return rpc::parse_command_single(rpc::make_target_pair(d1, d2), m_command.as_string()).as_value();
// torrent::Object tmp_command = m_command;
// uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
// tmp_command.unset_flags(torrent::Object::mask_function);
// tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function);
// rpc::parse_command_execute(rpc::make_target_pair(d1, d2), &tmp_command);
// return rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
// rpc::make_target_pair(d1, d2)).as_value();
return rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(),
rpc::make_target_pair(d1, d2)).as_value();
} catch (torrent::input_error& e) {
control->core()->push_log(e.what());
@@ -71,18 +85,35 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
}
}
const std::string& m_command;
const torrent::Object& m_command;
};
struct view_downloads_filter : std::unary_function<Download*, bool> {
view_downloads_filter(const std::string& cmd) : m_command(cmd) {}
view_downloads_filter(const torrent::Object& cmd) : m_command(cmd) {}
bool operator () (Download* d1) const {
if (m_command.empty())
if (m_command.is_empty())
return true;
try {
torrent::Object result = rpc::parse_command_single(rpc::make_target(d1), m_command);
torrent::Object result;
if (m_command.is_dict_key()) {
// torrent::Object tmp_command = m_command;
// uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
// tmp_command.unset_flags(torrent::Object::mask_function);
// tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function);
// rpc::parse_command_execute(rpc::make_target(d1), &tmp_command);
// result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
// rpc::make_target(d1));
result = rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(), rpc::make_target(d1));
} else {
result = rpc::parse_command_single(rpc::make_target(d1), m_command.as_string());
}
switch (result.type()) {
// case torrent::Object::TYPE_RAW_BENCODE: return !result.as_raw_bencode().empty();
@@ -104,7 +135,7 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
}
}
const std::string& m_command;
const torrent::Object& m_command;
};
inline void
+8 -7
View File
@@ -54,6 +54,7 @@
#include <vector>
#include <rak/timer.h>
#include <sigc++/signal.h>
#include <torrent/object.h>
#include "globals.h"
@@ -115,15 +116,15 @@ public:
void sort();
void set_sort_new(const std::string& s) { m_sortNew = s; }
void set_sort_current(const std::string& s) { m_sortCurrent = s; }
void set_sort_new(const torrent::Object& s) { m_sortNew = s; }
void set_sort_current(const torrent::Object& s) { m_sortCurrent = s; }
// Need to explicity trigger filtering.
void filter();
void filter_download(core::Download* download);
const std::string& get_filter() const { return m_filter; }
void set_filter(const std::string& s) { m_filter = s; }
const torrent::Object& get_filter() const { return m_filter; }
void set_filter(const torrent::Object& s) { m_filter = s; }
void set_filter_on_event(const std::string& event);
void clear_filter_on();
@@ -167,10 +168,10 @@ private:
size_type m_focus;
// These should be replaced by a faster non-string command type.
std::string m_sortNew;
std::string m_sortCurrent;
torrent::Object m_sortNew;
torrent::Object m_sortCurrent;
std::string m_filter;
torrent::Object m_filter;
event_list_type m_events;
std::string m_eventAdded;
+1 -1
View File
@@ -102,7 +102,7 @@ ViewManager::sort(const std::string& name, uint32_t timeout) {
}
void
ViewManager::set_filter(const std::string& name, const std::string& cmd) {
ViewManager::set_filter(const std::string& name, const torrent::Object& cmd) {
iterator viewItr = find_throw(name);
(*viewItr)->set_filter(cmd);
+3 -3
View File
@@ -88,10 +88,10 @@ public:
void sort(const std::string& name, uint32_t timeout = 0);
// These could be moved to where the command is implemented.
void set_sort_new(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_sort_new(cmd); }
void set_sort_current(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_sort_current(cmd); }
void set_sort_new(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_sort_new(cmd); }
void set_sort_current(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_sort_current(cmd); }
void set_filter(const std::string& name, const std::string& cmd);
void set_filter(const std::string& name, const torrent::Object& cmd);
void set_filter_on(const std::string& name, const filter_args& args);
void set_event_added(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_event_added(cmd); }
+20 -20
View File
@@ -260,54 +260,54 @@ main(int argc, char** argv) {
"view.add = default\n"
"view.add = name\n"
"view.sort_new = name,less=d.name=\n"
"view.sort_current = name,less=d.name=\n"
"view.sort_new = name,((less,((d.name))))\n"
"view.sort_current = name,((less,((d.name))))\n"
"view.add = active\n"
"view.filter = active,false=\n"
"view.filter = active,((false))\n"
"view.add = started\n"
"view.filter = started,false=\n"
"view.filter = started,((false))\n"
"view.event_added = started,\"view.set_not_visible=stopped ;d.state.set=1 ;scheduler.simple.added=\"\n"
"view.event_removed = started,\"view.set_visible=stopped ;scheduler.simple.removed=\"\n"
"view.add = stopped\n"
"view.filter = stopped,false=\n"
"view.filter = stopped,((false))\n"
"view.event_added = stopped,\"view.set_not_visible=started ;d.state.set=0\"\n"
"view.event_removed = stopped,view.set_visible=started\n"
"view.add = complete\n"
"view.filter = complete,d.complete=\n"
"view.filter = complete,((d.complete))\n"
"view.filter_on = complete,event.download.hash_done,event.download.hash_failed,event.download.hash_final_failed,event.download.finished\n"
"view.sort_new = complete,less=d.state_changed=\n"
"view.sort_current = complete,less=d.state_changed=\n"
// "view.sort_new = complete,((less,((d.state_changed))))\n"
// "view.sort_current = complete,((less,((d.state_changed))))\n"
"view.add = incomplete\n"
"view.filter = incomplete,not=$d.complete=\n"
"view.filter = incomplete,((not,((d.complete))))\n"
"view.filter_on = incomplete,event.download.hash_done,event.download.hash_failed,"
"event.download.hash_final_failed,event.download.finished\n"
"view.sort_new = incomplete,less=d.state_changed=\n"
"view.sort_current = incomplete,less=d.state_changed=\n"
"event.download.hash_final_failed,event.download.finished\n"
// "view.sort_new = incomplete,((less,((d.state_changed))))\n"
// "view.sort_current = incomplete,((less,((d.state_changed))))\n"
// The hashing view does not include stopped torrents.
"view.add = hashing\n"
"view.filter = hashing,d.hashing=\n"
"view.filter = hashing,((d.hashing))\n"
"view.filter_on = hashing,event.download.hash_queued,event.download.hash_removed,"
"event.download.hash_done,event.download.hash_failed,event.download.hash_final_failed\n"
"event.download.hash_done,event.download.hash_failed,event.download.hash_final_failed\n"
// "view.sort_new = hashing,less=d.state_changed=\n"
// "view.sort_current = hashing,less=d.state_changed=\n"
"view.add = seeding\n"
"view.filter = seeding,\"and=d.state=,d.complete=\"\n"
"view.filter = seeding,((and,((d.state)),((d.complete))))\n"
"view.filter_on = seeding,event.download.resumed,event.download.paused,event.download.finished\n"
"view.sort_new = seeding,less=d.state_changed=\n"
"view.sort_current = seeding,less=d.state_changed=\n"
// "view.sort_new = seeding,((less,((d.state_changed))))\n"
// "view.sort_current = seeding,((less,((d.state_changed))))\n"
"view.add = leeching\n"
"view.filter = leeching,\"and=d.state=,not=$d.complete=\"\n"
"view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n"
"view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n"
"view.sort_new = leeching,less=d.state_changed=\n"
"view.sort_current = leeching,less=d.state_changed=\n"
// "view.sort_new = leeching,((less,((d.state_changed))))\n"
// "view.sort_current = leeching,((less,((d.state_changed))))\n"
"schedule2 = view.main,10,10,((view.sort,main,20))\n"
"schedule2 = view.name,10,10,((view.sort,name,20))\n"
+2
View File
@@ -106,6 +106,8 @@ CommandScheduler::call_item(value_type item) {
// Unquote the root function object so 'parse_command_execute'
// doesn't end up calling it.
//
// TODO: Only call this if mask_function is set?
uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
tmp_command.unset_flags(torrent::Object::mask_function);
tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function);
+20 -18
View File
@@ -67,32 +67,34 @@ parse_string(const char* first, const char* last, std::string* dest, bool (*deli
if (first == last)
return first;
bool quoted = parse_is_quote(*first);
if (quoted)
if (parse_is_quote(*first)) {
first++;
while (first != last) {
if (quoted) {
while (first != last) {
if (parse_is_quote(*first))
return ++first;
} else {
if (delim(*first))
return first;
}
if (parse_is_escape(*first) && ++first == last)
throw torrent::input_error("Escape character at end of input.");
if (parse_is_escape(*first) && ++first == last)
throw torrent::input_error("Escape character at end of input.");
dest->push_back(*first++);
}
dest->push_back(*first);
first++;
}
if (quoted)
throw torrent::input_error("Missing closing quote.");
return first;
} else {
while (first != last) {
if (delim(*first))
return first;
if (parse_is_escape(*first) && ++first == last)
throw torrent::input_error("Escape character at end of input.");
dest->push_back(*first++);
}
return first;
}
}
void
+19 -15
View File
@@ -112,6 +112,21 @@ parse_command_execute(target_type target, torrent::Object* object) {
}
}
// Use a static length buffer for dest.
inline const char*
parse_command_name(const char* first, const char* last, char* dest_first, char* dest_last) {
if (first == last || !std::isalpha(*first))
throw torrent::input_error("Invalid start of command name.");
last = first + std::min(std::distance(first, last), std::distance(dest_first, dest_last) - 1);
while (first != last && (std::isalnum(*first) || *first == '_' || *first == '.'))
*dest_first++ = *first++;
*dest_first = '\0';
return first;
}
// Set 'download' to NULL to call the generic functions, thus reusing
// the code below for both cases.
parse_command_type
@@ -121,8 +136,9 @@ parse_command(target_type target, const char* first, const char* last) {
if (first == last || *first == '#')
return std::make_pair(torrent::Object(), first);
std::string key;
first = parse_command_name(first, last, &key);
char key[128];
first = parse_command_name(first, last, key, key + 128);
first = std::find_if(first, last, std::not1(command_map_is_space()));
if (first == last || *first != '=')
@@ -147,7 +163,7 @@ parse_command(target_type target, const char* first, const char* last) {
// following command.
parse_command_execute(target, &args);
return std::make_pair(commands.call_command(key.c_str(), args, target), first);
return std::make_pair(commands.call_command(key, args, target), first);
}
torrent::Object
@@ -223,18 +239,6 @@ parse_command_file(const std::string& path) {
return true;
}
// Use a static length buffer for dest.
const char*
parse_command_name(const char* first, const char* last, std::string* dest) {
if (first == last || !std::isalpha(*first))
throw torrent::input_error("Invalid start of name.");
for ( ; first != last && (std::isalnum(*first) || *first == '_' || *first == '.'); ++first)
dest->push_back(*first);
return first;
}
//
//
//