From ea9dc35bbe3b0da45ee34e2477cceeebe8206b3a Mon Sep 17 00:00:00 2001 From: rakshasa Date: Wed, 20 Apr 2011 15:14:02 +0000 Subject: [PATCH] * Optimized view filtering and sorting. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1213 e378c898-3ddf-0310-93e7-cc216c733640 --- doc/log_stats.plot | 61 +++++++++++++++++-------------- src/command_ui.cc | 70 +++++++++++++++++++++++++++--------- src/core/view.cc | 47 +++++++++++++++++++----- src/core/view.h | 15 ++++---- src/core/view_manager.cc | 2 +- src/core/view_manager.h | 6 ++-- src/main.cc | 40 ++++++++++----------- src/rpc/command_scheduler.cc | 2 ++ src/rpc/parse.cc | 38 ++++++++++---------- src/rpc/parse_commands.cc | 34 ++++++++++-------- 10 files changed, 200 insertions(+), 115 deletions(-) diff --git a/doc/log_stats.plot b/doc/log_stats.plot index 9245c70f..acd7a4a3 100644 --- a/doc/log_stats.plot +++ b/doc/log_stats.plot @@ -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)=(xmax)?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',\ + "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. diff --git a/src/core/view.cc b/src/core/view.cc index 5f2454d5..441b84bd 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -55,14 +55,28 @@ namespace core { // Also add focus thingie here? struct view_downloads_compare : std::binary_function { - 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 } } - const std::string& m_command; + const torrent::Object& m_command; }; struct view_downloads_filter : std::unary_function { - 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 { } } - const std::string& m_command; + const torrent::Object& m_command; }; inline void diff --git a/src/core/view.h b/src/core/view.h index f0bbde35..032767e0 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -54,6 +54,7 @@ #include #include #include +#include #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; diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 0052cf53..91298e68 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -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); diff --git a/src/core/view_manager.h b/src/core/view_manager.h index 60d0db4d..f3ba0ca7 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -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); } diff --git a/src/main.cc b/src/main.cc index 19bff2e0..545ffac1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -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" diff --git a/src/rpc/command_scheduler.cc b/src/rpc/command_scheduler.cc index 9010ffce..a3020a73 100644 --- a/src/rpc/command_scheduler.cc +++ b/src/rpc/command_scheduler.cc @@ -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); diff --git a/src/rpc/parse.cc b/src/rpc/parse.cc index 5453f043..be57d86e 100644 --- a/src/rpc/parse.cc +++ b/src/rpc/parse.cc @@ -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 diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index f957edec..60765733 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -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; -} - // // //