diff --git a/src/command_events.cc b/src/command_events.cc index 9cb2a560..38f420ff 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -313,8 +313,6 @@ void initialize_command_events() { CMD2_ANY("test.thread_locking", std::tr1::bind(&test_thread_locking)); - CMD2_VAR_BOOL ("pieces.hash.on_completion", true); - CMD2_ANY_STRING ("on_ratio", std::tr1::bind(&apply_on_ratio, std::tr1::placeholders::_2)); CMD2_ANY ("start_tied", std::tr1::bind(&apply_start_tied)); @@ -322,8 +320,11 @@ initialize_command_events() { CMD2_ANY ("close_untied", std::tr1::bind(&apply_close_untied)); CMD2_ANY ("remove_untied", std::tr1::bind(&apply_remove_untied)); - CMD2_ANY_LIST ("schedule", std::tr1::bind(&apply_schedule, std::tr1::placeholders::_2)); - CMD2_ANY_STRING_V("schedule_remove", std::tr1::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::tr1::placeholders::_2)); + CMD2_ANY_LIST ("schedule2", std::tr1::bind(&apply_schedule, std::tr1::placeholders::_2)); + CMD2_ANY_STRING_V("schedule_remove2", std::tr1::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::tr1::placeholders::_2)); + + CMD2_REDIRECT_GENERIC("schedule", "schedule2"); + CMD2_REDIRECT_GENERIC("schedule_remove", "schedule_remove2"); CMD2_ANY_STRING_V("import", std::tr1::bind(&apply_import, std::tr1::placeholders::_2)); CMD2_ANY_STRING_V("try_import", std::tr1::bind(&apply_try_import, std::tr1::placeholders::_2)); diff --git a/src/command_helpers.h b/src/command_helpers.h index e4fa731c..84a79a15 100644 --- a/src/command_helpers.h +++ b/src/command_helpers.h @@ -79,6 +79,7 @@ void initialize_commands(); #define CMD2_ANY_VALUE(key, slot) CMD2_A_FUNCTION(key, command_base_call_value, slot, "i:i", "") #define CMD2_ANY_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value, object_convert_void(slot), "i:i", "") +#define CMD2_ANY_VALUE_KB(key, slot) CMD2_A_FUNCTION(key, command_base_call_value_kb, object_convert_void(slot), "i:i", "") #define CMD2_ANY_STRING(key, slot) CMD2_A_FUNCTION(key, command_base_call_string, slot, "i:s", "") #define CMD2_ANY_STRING_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_string, object_convert_void(slot), "i:s", "") @@ -125,9 +126,13 @@ void initialize_commands(); #define CMD2_REDIRECT(from_key, to_key) \ rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc); - #define CMD2_REDIRECT_GENERIC(from_key, to_key) \ rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target); +#define CMD2_REDIRECT_FILE(from_key, to_key) \ + rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_file_target); +#define CMD2_REDIRECT_TRACKER(from_key, to_key) \ + rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_tracker_target); + // // Conversion of return types: diff --git a/src/command_local.cc b/src/command_local.cc index 4969cfdf..c073095e 100644 --- a/src/command_local.cc +++ b/src/command_local.cc @@ -224,6 +224,8 @@ initialize_command_local() { CMD2_ANY ("pieces.stats_preloaded", std::tr1::bind(&CM_t::stats_preloaded, chunkManager)); CMD2_ANY ("pieces.stats_not_preloaded", std::tr1::bind(&CM_t::stats_not_preloaded, chunkManager)); + CMD2_VAR_BOOL ("pieces.hash.on_completion", true); + CMD2_VAR_STRING ("directory.default", "./"); // TODO: Clean up. diff --git a/src/command_network.cc b/src/command_network.cc index c9528249..69d7bc68 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -434,14 +434,16 @@ initialize_command_network() { CMD2_VAR_VALUE("max_downloads_global", 0); // TODO: Move the logic into some libtorrent function. - CMD2_ANY ("throttle.global_up.rate", std::tr1::bind(&torrent::Rate::rate, torrent::up_rate())); - CMD2_ANY ("throttle.global_up.total", std::tr1::bind(&torrent::Rate::total, torrent::up_rate())); - CMD2_ANY ("throttle.global_up.max_rate", std::tr1::bind(&torrent::Throttle::max_rate, torrent::up_throttle_global())); - CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::tr1::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::tr1::placeholders::_2)); - CMD2_ANY ("throttle.global_down.rate", std::tr1::bind(&torrent::Rate::rate, torrent::down_rate())); - CMD2_ANY ("throttle.global_down.total", std::tr1::bind(&torrent::Rate::total, torrent::down_rate())); - CMD2_ANY ("throttle.global_down.max_rate", std::tr1::bind(&torrent::Throttle::max_rate, torrent::down_throttle_global())); - CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::tr1::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::tr1::placeholders::_2)); + CMD2_ANY ("throttle.global_up.rate", std::tr1::bind(&torrent::Rate::rate, torrent::up_rate())); + CMD2_ANY ("throttle.global_up.total", std::tr1::bind(&torrent::Rate::total, torrent::up_rate())); + CMD2_ANY ("throttle.global_up.max_rate", std::tr1::bind(&torrent::Throttle::max_rate, torrent::up_throttle_global())); + CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::tr1::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::tr1::placeholders::_2)); + CMD2_ANY_VALUE_KB("throttle.global_up.max_rate.set_kb", std::tr1::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::tr1::placeholders::_2)); + CMD2_ANY ("throttle.global_down.rate", std::tr1::bind(&torrent::Rate::rate, torrent::down_rate())); + CMD2_ANY ("throttle.global_down.total", std::tr1::bind(&torrent::Rate::total, torrent::down_rate())); + CMD2_ANY ("throttle.global_down.max_rate", std::tr1::bind(&torrent::Throttle::max_rate, torrent::down_throttle_global())); + CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::tr1::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::tr1::placeholders::_2)); + CMD2_ANY_VALUE_KB("throttle.global_down.max_rate.set_kb", std::tr1::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::tr1::placeholders::_2)); CMD2_ANY_LIST("throttle_up", std::tr1::bind(&apply_throttle, std::tr1::placeholders::_2, true)); CMD2_ANY_LIST("throttle_down", std::tr1::bind(&apply_throttle, std::tr1::placeholders::_2, false)); diff --git a/src/main.cc b/src/main.cc index a0a191d5..1a93b212 100644 --- a/src/main.cc +++ b/src/main.cc @@ -65,6 +65,7 @@ #include "utils/directory.h" #include "control.h" +#include "command_helpers.h" #include "globals.h" #include "signal_handler.h" #include "option_parser.h" @@ -321,16 +322,6 @@ main(int argc, char** argv) { // * command_tracker.cc // * command_ui.cc -#define CMD2_REDIRECT(from_key, to_key) \ - rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc); - -#define CMD2_REDIRECT_GENERIC(from_key, to_key) \ - rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target); -#define CMD2_REDIRECT_FILE(from_key, to_key) \ - rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_file_target); -#define CMD2_REDIRECT_TRACKER(from_key, to_key) \ - rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_tracker_target); - CMD2_REDIRECT_GENERIC("system.method.insert", "method.insert"); CMD2_REDIRECT_GENERIC("system.method.erase", "method.erase"); CMD2_REDIRECT_GENERIC("system.method.get", "method.get"); @@ -340,26 +331,26 @@ main(int argc, char** argv) { CMD2_REDIRECT_GENERIC("system.method.set_key", "method.set_key"); CMD2_REDIRECT ("get_handshake_log", "log.handshake"); - CMD2_REDIRECT ("set_handshake_log", "log.handshake.set"); + CMD2_REDIRECT_GENERIC("set_handshake_log", "log.handshake.set"); CMD2_REDIRECT ("get_log.tracker", "log.tracker"); - CMD2_REDIRECT ("set_log.tracker", "log.tracker.set"); + CMD2_REDIRECT_GENERIC("set_log.tracker", "log.tracker.set"); CMD2_REDIRECT ("get_name", "system.session_name"); - CMD2_REDIRECT ("set_name", "system.session_name.set"); + CMD2_REDIRECT_GENERIC("set_name", "system.session_name.set"); CMD2_REDIRECT ("system.file_allocate", "system.file.allocate"); CMD2_REDIRECT ("system.file_allocate.set", "system.file.allocate.set"); CMD2_REDIRECT ("get_max_file_size", "system.file.max_size"); - CMD2_REDIRECT ("set_max_file_size", "system.file.max_size.set"); + CMD2_REDIRECT_GENERIC("set_max_file_size", "system.file.max_size.set"); CMD2_REDIRECT ("get_split_file_size", "system.file.split_size"); - CMD2_REDIRECT ("set_split_file_size", "system.file.split_size.set"); + CMD2_REDIRECT_GENERIC("set_split_file_size", "system.file.split_size.set"); CMD2_REDIRECT ("get_split_suffix", "system.file.split_suffix"); - CMD2_REDIRECT ("set_split_suffix", "system.file.split_suffix.set"); + CMD2_REDIRECT_GENERIC("set_split_suffix", "system.file.split_suffix.set"); CMD2_REDIRECT ("get_timeout_sync", "pieces.sync.timeout"); - CMD2_REDIRECT ("set_timeout_sync", "pieces.sync.timeout.set"); + CMD2_REDIRECT_GENERIC("set_timeout_sync", "pieces.sync.timeout.set"); CMD2_REDIRECT ("get_timeout_safe_sync", "pieces.sync.timeout_safe"); - CMD2_REDIRECT ("set_timeout_safe_sync", "pieces.sync.timeout_safe.set"); + CMD2_REDIRECT_GENERIC("set_timeout_safe_sync", "pieces.sync.timeout_safe.set"); CMD2_REDIRECT_GENERIC("get_preload_type", "pieces.preload.type"); CMD2_REDIRECT_GENERIC("get_preload_min_size", "pieces.preload.min_size"); @@ -378,77 +369,77 @@ main(int argc, char** argv) { CMD2_REDIRECT_GENERIC("set_max_memory_usage", "pieces.memory.max.set"); CMD2_REDIRECT ("get_send_buffer_size", "network.send_buffer.size"); - CMD2_REDIRECT ("set_send_buffer_size", "network.send_buffer.size.set"); + CMD2_REDIRECT_GENERIC("set_send_buffer_size", "network.send_buffer.size.set"); CMD2_REDIRECT ("get_receive_buffer_size", "network.receive_buffer.size"); - CMD2_REDIRECT ("set_receive_buffer_size", "network.receive_buffer.size.set"); + CMD2_REDIRECT_GENERIC("set_receive_buffer_size", "network.receive_buffer.size.set"); CMD2_REDIRECT ("get_up_rate", "throttle.global_up.rate"); CMD2_REDIRECT ("get_up_total", "throttle.global_up.total"); CMD2_REDIRECT ("get_upload_rate", "throttle.global_up.max_rate"); - CMD2_REDIRECT ("set_upload_rate", "throttle.global_up.max_rate.set"); + CMD2_REDIRECT_GENERIC("set_upload_rate", "throttle.global_up.max_rate.set"); CMD2_REDIRECT ("get_down_rate", "throttle.global_down.rate"); CMD2_REDIRECT ("get_down_total", "throttle.global_down.total"); CMD2_REDIRECT ("get_download_rate", "throttle.global_down.max_rate"); - CMD2_REDIRECT ("set_download_rate", "throttle.global_down.max_rate.set"); + CMD2_REDIRECT_GENERIC("set_download_rate", "throttle.global_down.max_rate.set"); CMD2_REDIRECT ("bind", "network.bind_address.set"); - CMD2_REDIRECT ("set_bind", "network.bind_address.set"); + CMD2_REDIRECT_GENERIC("set_bind", "network.bind_address.set"); CMD2_REDIRECT ("get_bind", "network.bind_address"); CMD2_REDIRECT ("ip", "network.local_address.set"); - CMD2_REDIRECT ("set_ip", "network.local_address.set"); + CMD2_REDIRECT_GENERIC("set_ip", "network.local_address.set"); CMD2_REDIRECT ("get_ip", "network.local_address"); CMD2_REDIRECT ("port_range", "network.port_range.set"); CMD2_REDIRECT ("get_port_range", "network.port_range"); - CMD2_REDIRECT ("set_port_range", "network.port_range.set"); + CMD2_REDIRECT_GENERIC("set_port_range", "network.port_range.set"); CMD2_REDIRECT ("port_random", "network.port_random.set"); CMD2_REDIRECT ("get_port_random", "network.port_random"); - CMD2_REDIRECT ("set_port_random", "network.port_random.set"); + CMD2_REDIRECT_GENERIC("set_port_random", "network.port_random.set"); CMD2_REDIRECT ("port_open", "network.port_open.set"); CMD2_REDIRECT ("get_port_open", "network.port_open"); - CMD2_REDIRECT ("set_port_open", "network.port_open.set"); + CMD2_REDIRECT_GENERIC("set_port_open", "network.port_open.set"); CMD2_REDIRECT ("proxy_address", "network.proxy_address.set"); - CMD2_REDIRECT ("set_proxy_address", "network.proxy_address.set"); + CMD2_REDIRECT_GENERIC("set_proxy_address", "network.proxy_address.set"); CMD2_REDIRECT ("get_proxy_address", "network.proxy_address"); CMD2_REDIRECT ("scgi_port", "network.scgi.open_port"); CMD2_REDIRECT ("scgi_local", "network.scgi.open_local"); CMD2_REDIRECT ("scgi_dont_route", "network.scgi.dont_route.set"); - CMD2_REDIRECT ("set_scgi_dont_route", "network.scgi.dont_route.set"); + CMD2_REDIRECT_GENERIC("set_scgi_dont_route", "network.scgi.dont_route.set"); CMD2_REDIRECT ("get_scgi_dont_route", "network.scgi.dont_route"); CMD2_REDIRECT ("xmlrpc_dialect", "network.xmlrpc.size_limit.set"); CMD2_REDIRECT ("get_connection_leech", "connection_leech"); - CMD2_REDIRECT ("set_connection_leech", "connection_leech.set"); + CMD2_REDIRECT_GENERIC("set_connection_leech", "connection_leech.set"); CMD2_REDIRECT ("get_connection_seed", "connection_seed"); - CMD2_REDIRECT ("set_connection_seed", "connection_seed.set"); + CMD2_REDIRECT_GENERIC("set_connection_seed", "connection_seed.set"); CMD2_REDIRECT ("peer_exchange", "protocol.pex.set"); CMD2_REDIRECT ("get_peer_exchange", "protocol.pex"); - CMD2_REDIRECT ("set_peer_exchange", "protocol.pex.set"); + CMD2_REDIRECT_GENERIC("set_peer_exchange", "protocol.pex.set"); CMD2_REDIRECT ("dht", "dht.mode.set"); CMD2_REDIRECT ("dht_add_node", "dht.add_node"); CMD2_REDIRECT ("dht_statistics", "dht.statistics"); CMD2_REDIRECT ("get_dht_port", "dht.port"); - CMD2_REDIRECT ("set_dht_port", "dht.port"); + CMD2_REDIRECT_GENERIC("set_dht_port", "dht.port"); CMD2_REDIRECT ("get_dht_throttle", "dht.throttle.name"); - CMD2_REDIRECT ("set_dht_throttle", "dht.throttle.name.set"); + CMD2_REDIRECT_GENERIC("set_dht_throttle", "dht.throttle.name.set"); CMD2_REDIRECT ("directory", "directory.default.set"); CMD2_REDIRECT ("get_directory", "directory.default"); - CMD2_REDIRECT ("set_directory", "directory.default.set"); + CMD2_REDIRECT_GENERIC("set_directory", "directory.default.set"); CMD2_REDIRECT ("get_session_lock", "system.session.use_lock"); - CMD2_REDIRECT ("set_session_lock", "system.session.use_lock.set"); + CMD2_REDIRECT_GENERIC("set_session_lock", "system.session.use_lock.set"); CMD2_REDIRECT ("get_session_on_completion", "system.session.on_completion"); - CMD2_REDIRECT ("set_session_on_completion", "system.session.on_completion.set"); + CMD2_REDIRECT_GENERIC("set_session_on_completion", "system.session.on_completion.set"); CMD2_REDIRECT ("check_hash", "pieces.hash.on_completion.set"); @@ -640,8 +631,8 @@ main(int argc, char** argv) { // Functions that might not get depracted as they are nice for // configuration files, and thus might do with just some // cleanup. - CMD2_REDIRECT ("upload_rate", "throttle.global_up.max_rate.set"); - CMD2_REDIRECT ("download_rate", "throttle.global_down.max_rate.set"); + CMD2_REDIRECT_GENERIC("upload_rate", "throttle.global_up.max_rate.set_kb"); + CMD2_REDIRECT_GENERIC("download_rate", "throttle.global_down.max_rate.set_kb"); } if (OptionParser::has_flag('n', argc, argv)) diff --git a/src/rpc/command_new_slot.cc b/src/rpc/command_new_slot.cc index c43b78bb..19d752b0 100644 --- a/src/rpc/command_new_slot.cc +++ b/src/rpc/command_new_slot.cc @@ -85,7 +85,13 @@ command_base_call_value(Command* rawCommand, target_type target, const torrent:: return command_base_call_value_base(rawCommand, target, rawArgs, 0, 1); } +template const torrent::Object +command_base_call_value_kb(Command* rawCommand, target_type target, const torrent::Object& rawArgs) { + return command_base_call_value_base(rawCommand, target, rawArgs, 0, 1024); +} + COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value); +COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb); template const torrent::Object command_base_call_string(Command* rawCommand, target_type target, const torrent::Object& rawArgs) { diff --git a/src/rpc/command_new_slot.h b/src/rpc/command_new_slot.h index 77a875f0..dfabc99f 100644 --- a/src/rpc/command_new_slot.h +++ b/src/rpc/command_new_slot.h @@ -117,6 +117,7 @@ COMMAND_BASE_TEMPLATE_TYPE(command_list_function, torrent::Object (T, const to COMMAND_BASE_TEMPLATE_CALL(command_base_call, command_function); COMMAND_BASE_TEMPLATE_CALL(command_base_call_value, command_value_function); +COMMAND_BASE_TEMPLATE_CALL(command_base_call_value_kb, command_value_function); COMMAND_BASE_TEMPLATE_CALL(command_base_call_string, command_string_function); COMMAND_BASE_TEMPLATE_CALL(command_base_call_list, command_list_function); diff --git a/src/rpc/object_storage.cc b/src/rpc/object_storage.cc index 911b641b..c888bec3 100644 --- a/src/rpc/object_storage.cc +++ b/src/rpc/object_storage.cc @@ -38,6 +38,72 @@ #include "object_storage.h" +#include "parse.h" + namespace rpc { +object_storage::local_iterator +object_storage::find_local(const torrent::raw_string& key) { + std::size_t n = hash_fixed_key_type::hash(key.data(), key.size()) % bucket_count(); + + for (local_iterator itr = begin(n), last = end(n); itr != last; itr++) + if (itr->first.size() == key.size() && std::memcmp(itr->first.data(), key.data(), key.size()) == 0) + return itr; + + return end(bucket_count()); +} + +object_storage::iterator +object_storage::insert(const char* key_data, uint32_t key_size, const torrent::Object& object, unsigned int flags) { + if (std::find(key_data, key_data + key_size, '\0') != key_data + key_size) + throw torrent::input_error("Found nul-char in string."); + + // Check for size > key_size. + // Check for empty string. + + // Ensure the object type is correct. + + if (!(flags & mask_type)) + throw torrent::input_error("No type flags set when calling object_storage::insert."); + + std::pair result = base_type::insert(std::make_pair(key_type(key_data, key_size), object_storage_node())); + + if (!result.second) + throw torrent::input_error("Key already exists in object_storage."); + + result.first->second.object = object; + result.first->second.flags = flags; + + return result.first; +} + +const torrent::Object& +object_storage::get(const torrent::raw_string& key) { + local_iterator itr = find_local(key); + + if (itr == end(bucket_count())) + throw torrent::input_error("Key not found."); + + return itr->second.object; +} + +const torrent::Object& +object_storage::set(const torrent::raw_string& key, const torrent::Object& object) { + local_iterator itr = find_local(key); + + if (itr == end(bucket_count())) + throw torrent::input_error("Key not found."); + + // Redo this... + + switch (itr->second.flags & mask_type) { + case flag_generic_type: itr->second.object = object; break; + case flag_value_type: itr->second.object = convert_to_value(object); break; + case flag_string_type: itr->second.object = convert_to_string(object); break; + default: throw torrent::internal_error("object_storage::set: Type not set."); + } + + return itr->second.object; +} + } diff --git a/src/rpc/object_storage.h b/src/rpc/object_storage.h index d4b7025b..8e7b3c3d 100644 --- a/src/rpc/object_storage.h +++ b/src/rpc/object_storage.h @@ -42,7 +42,7 @@ #define RTORRENT_RPC_OBJECT_STORAGE_H #include -#include +#include #include namespace rpc { @@ -50,42 +50,185 @@ namespace rpc { // The key size should be such that the value type size which includes // the next-pointer. -struct object_storage_node_internal { +template +class fixed_key_type { +public: + typedef char value_type; + typedef const char* iterator; + typedef const char* const_iterator; + typedef uint32_t size_type; + + static const size_type max_size = MaxSize - 1; + + fixed_key_type() : m_size(0) { m_data[0] = '\0'; } + fixed_key_type(const fixed_key_type& k) : m_size(k.m_size) { std::memcpy(m_data, k.m_data, k.m_size + 1); } + + fixed_key_type(const value_type* src_data, size_type src_size) { set_data(src_data, src_size); } + + static fixed_key_type from_c_str(const char* str) { fixed_key_type k; k.set_c_str(str); return k; } + static fixed_key_type from_string(const std::string& str) { fixed_key_type k; k.set_c_str(str.c_str(), str.size()); return k; } + static fixed_key_type from_raw_string(const torrent::raw_string& str) { fixed_key_type k; k.set_data(str.data(), str.size()); return k; } + + bool empty() const { return m_size == 0;; } + size_type size() const { return m_size; } + + iterator begin() const { return m_data; } + iterator end() const { return m_data + m_size; } + + value_type* data() { return m_data; } + const value_type* data() const { return m_data; } + const char* c_str() const { return m_data; } + + void set_data(const value_type* src_data, size_type src_size); + void set_c_str(const value_type* src_data); + void set_c_str(const value_type* src_data, size_type src_size); + + bool operator == (const fixed_key_type& rhs) const { return m_size == rhs.m_size && std::memcmp(m_data, rhs.m_data, m_size) == 0; } + bool operator != (const fixed_key_type& rhs) const { return m_size != rhs.m_size || std::memcmp(m_data, rhs.m_data, m_size) != 0; } + + bool operator == (const std::string& rhs) const { return m_size == rhs.size() && std::memcmp(m_data, rhs.data(), m_size) == 0; } + +private: + size_type m_size; + char m_data[max_size]; +}; + +struct hash_fixed_key_type { + template + inline std::size_t operator () (const fixed_key_type& p) const { return hash(p.data()); } + + static inline std::size_t hash(const char* data) { + std::size_t result = 0; + + while (*data != '\0') + result = (result * 131) + *data++; + + return result; + } + + static inline std::size_t hash(const char* data, uint32_t size) { + std::size_t result = 0; + + while (size--) + result = (result * 131) + *data++; + + return result; + } +}; + +// +// +// + +struct object_storage_node { torrent::Object object; - char key[0]; + char flags; }; -struct object_storage_node : public object_storage_node_internal { - // TODO: Use the size of the unordered_map::value_type with a struct {}. - static const size_t key_size = 128 - sizeof(object_storage_node_internal) - sizeof(void*); -}; - -struct hash_object_storage_node { - inline std::size_t operator () (const object_storage_node& p); -}; - -bool operator == (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) == 0; } -bool operator != (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) != 0; } -bool operator < (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) < 0; } -bool operator <= (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) <= 0; } -bool operator > (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) > 0; } -bool operator >= (const object_storage_node& left, const object_storage_node& right) { return std::strcmp(left.key, right.key) >= 0; } - -class object_storage : private std::tr1::unordered_set { - typedef std::tr1::unordered_set base_type; +class object_storage : private std::tr1::unordered_map, object_storage_node, hash_fixed_key_type> { +public: + typedef std::tr1::unordered_map, object_storage_node, hash_fixed_key_type> base_type; + using base_type::key_type; + using base_type::value_type; using base_type::iterator; + using base_type::const_iterator; + using base_type::local_iterator; + using base_type::const_local_iterator; + + using base_type::begin; + using base_type::end; + using base_type::size; + using base_type::empty; + using base_type::key_eq; + using base_type::bucket; + using base_type::bucket_count; + using base_type::max_bucket_count; + using base_type::load_factor; + + using base_type::clear; + using base_type::find; + using base_type::erase; + + static const unsigned int flag_generic_type = 0x1; + static const unsigned int flag_value_type = 0x2; + static const unsigned int flag_string_type = 0x4; + static const unsigned int flag_last_type = 0x8; + + static const unsigned int mask_type = flag_last_type - 1; + + static const size_t key_size = key_type::max_size; + + local_iterator find_local(const torrent::raw_string& key); + + iterator insert(const char* key_data, uint32_t key_size, const torrent::Object& object, unsigned int flags); + + iterator insert(const char* key, const torrent::Object& object, unsigned int flags); + iterator insert(const torrent::raw_string& key, const torrent::Object& object, unsigned int flags); + iterator insert_string(const std::string& key, const torrent::Object& object, unsigned int flags); + + // Access functions that throw on error. + + const torrent::Object& get(const torrent::raw_string& key); + const torrent::Object& get_c_str(const char* str) { return get(torrent::raw_string(str, std::strlen(str))); } + + const torrent::Object& set(const torrent::raw_string& key, const torrent::Object& object); + + const torrent::Object& set_c_str(const char* str, const torrent::Object& object) { return set(torrent::raw_string(str, std::strlen(str)), object); } }; -inline std::size_t -hash_object_storage_node::operator () (const object_storage_node& p) { - std::size_t result = 0; - const char* first = p.key; +// +// Implementation: +// - while (*first != '\0') - result = (result * 131) + *first++; +template inline void +fixed_key_type::set_data(const value_type* src_data, size_type src_size) { + if (src_size >= max_size) { + new (this) fixed_key_type(); + return; + } - return result; + m_size = src_size; + std::memcpy(m_data, src_data, m_size); + m_data[m_size] = '\0'; +} + +template inline void +fixed_key_type::set_c_str(const value_type* src_data) { + value_type* itr = m_data; + const value_type* last = m_data + max_size; + + while (itr != last && *src_data != '\0') + *itr++ = *src_data++; + + *itr = '\0'; + m_size = std::distance(m_data, itr); +} + +template inline void +fixed_key_type::set_c_str(const value_type* src_data, size_type src_size) { + if (src_size >= max_size) { + new (this) fixed_key_type(); + return; + } + + m_size = src_size; + std::memcpy(m_data, src_data, m_size + 1); +} + +inline object_storage::iterator +object_storage::insert(const char* key, const torrent::Object& object, unsigned int flags) { + return insert(key, std::strlen(key), object, flags); +} + +inline object_storage::iterator +object_storage::insert(const torrent::raw_string& key, const torrent::Object& object, unsigned int flags) { + return insert(key.data(), key.size(), object, flags); +} + +inline object_storage::iterator +object_storage::insert_string(const std::string& key, const torrent::Object& object, unsigned int flags) { + return insert(key.data(), key.size(), object, flags); } } diff --git a/src/rpc/parse.cc b/src/rpc/parse.cc index 55c67a4f..0ae2b25e 100644 --- a/src/rpc/parse.cc +++ b/src/rpc/parse.cc @@ -228,6 +228,33 @@ parse_whole_list(const char* first, const char* last, torrent::Object* dest, boo return first; } +std::string +convert_to_string(const torrent::Object& rawSrc) { + const torrent::Object& src = convert_to_single_argument(rawSrc); + + switch (src.type()) { + case torrent::Object::TYPE_VALUE: { + char buffer[64]; + snprintf(buffer, 64, "%lli", src.as_value()); + return std::string(buffer); + } + case torrent::Object::TYPE_STRING: return src.as_string(); + case torrent::Object::TYPE_NONE: return std::string(); + + case torrent::Object::TYPE_RAW_BENCODE: + if (src.as_raw_bencode().is_empty()) + return std::string(); + + if (src.as_raw_bencode().is_raw_string()) + return src.as_raw_bencode().as_raw_string().as_string(); + + if (src.as_raw_bencode().is_value()) + return src.as_raw_bencode().as_value_string(); + + default: throw torrent::input_error("Not a string."); + } +} + std::string convert_list_to_string(const torrent::Object& src) { if (!src.is_list()) @@ -334,12 +361,20 @@ convert_to_value_nothrow(const torrent::Object& src, int64_t* value, int base, i break; case torrent::Object::TYPE_STRING: - if (parse_skip_wspace(parse_value(unpacked.as_string().c_str(), value, base, unit), - unpacked.as_string().c_str() + unpacked.as_string().size()) != unpacked.as_string().c_str() + unpacked.as_string().size()) - return false; + return parse_skip_wspace(parse_value(unpacked.as_string().c_str(), value, base, unit), + unpacked.as_string().c_str() + unpacked.as_string().size()) + == unpacked.as_string().c_str() + unpacked.as_string().size(); - break; + case torrent::Object::TYPE_RAW_STRING: { + const torrent::raw_string& str = src.as_raw_string(); + char buffer[str.size() + 1]; + std::memcpy(buffer, str.data(), str.size()); + buffer[str.size()] = '\0'; + + return parse_skip_wspace(parse_value(buffer, value, base, unit), buffer + str.size()) + == buffer + str.size(); + } case torrent::Object::TYPE_NONE: *value = 0; break; diff --git a/src/rpc/parse.h b/src/rpc/parse.h index bde586f2..ed1077ad 100644 --- a/src/rpc/parse.h +++ b/src/rpc/parse.h @@ -73,6 +73,7 @@ void parse_whole_string(const char* first, const char* last, std::string* const char* parse_value(const char* src, int64_t* value, int base = 0, int unit = 1); const char* parse_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1); +const char* parse_value_nothrow(const char* first, const char* last, int64_t* value, int base = 0, int unit = 0); void parse_whole_value(const char* src, int64_t* value, int base = 0, int unit = 1); bool parse_whole_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1); @@ -81,6 +82,8 @@ const char* parse_object (const char* first, const char* last, torrent::Objec const char* parse_list (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default); const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default); +std::string convert_to_string(const torrent::Object& src); + std::string convert_list_to_string(const torrent::Object& src); std::string convert_list_to_string(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last); std::string convert_list_to_command(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last); diff --git a/test/Makefile.am b/test/Makefile.am index 90da7231..8e46aa31 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -16,6 +16,8 @@ rtorrentTest_SOURCES = \ rpc/command_map_test.h \ rpc/command_slot_test.cc \ rpc/command_slot_test.h \ + rpc/object_storage_test.cc \ + rpc/object_storage_test.h \ main.cc rtorrentTest_CXXFLAGS = $(CPPUNIT_CFLAGS) diff --git a/test/rpc/object_storage_test.cc b/test/rpc/object_storage_test.cc new file mode 100644 index 00000000..bf4bc179 --- /dev/null +++ b/test/rpc/object_storage_test.cc @@ -0,0 +1,77 @@ +#include "config.h" + +#import "object_storage_test.h" + +CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStorageTest); + +#define ASSERT_CATCH_INPUT_ERROR(some_code) \ + try { some_code; CPPUNIT_ASSERT("torrent::input_error not caught" && false); } catch (torrent::input_error& e) { } + +void +ObjectStorageTest::test_basics() { + rpc::object_storage::iterator itr; + + CPPUNIT_ASSERT(m_storage.empty()); + + itr = m_storage.insert("test_1", torrent::Object("a"), rpc::object_storage::flag_generic_type); + + CPPUNIT_ASSERT(itr != m_storage.end()); + CPPUNIT_ASSERT(&*itr != NULL); + CPPUNIT_ASSERT(itr->first.size() == 6 && std::strcmp(itr->first.data(), "test_1") == 0); + CPPUNIT_ASSERT(itr->second.object.is_string() && itr->second.object.as_string() == "a"); + + ASSERT_CATCH_INPUT_ERROR( { m_storage.insert("test_1", torrent::Object("a"), rpc::object_storage::flag_generic_type); } ); + + // Test erase. + m_storage.erase(rpc::object_storage::key_type::from_c_str("test_1")); + CPPUNIT_ASSERT(m_storage.empty()); + + // Test with no type flag. + ASSERT_CATCH_INPUT_ERROR( { m_storage.insert("test_2", torrent::Object("b"), 0); } ); + ASSERT_CATCH_INPUT_ERROR( { m_storage.insert("test_3", torrent::Object("c"), ~rpc::object_storage::mask_type); } ); + + m_storage.clear(); +} + +void +ObjectStorageTest::test_conversions() { + CPPUNIT_ASSERT(m_storage.insert("test_1", torrent::Object("a"), rpc::object_storage::flag_generic_type) != m_storage.end()); + CPPUNIT_ASSERT(m_storage.insert_string(std::string("test_2"), torrent::Object("a"), rpc::object_storage::flag_generic_type) != m_storage.end()); + + char raw_3[8] = "test_3\x1"; + torrent::raw_string raw_string_3(raw_3, 6); + + CPPUNIT_ASSERT(m_storage.insert(raw_string_3, torrent::Object("a"), rpc::object_storage::flag_generic_type)->first == std::string("test_3")); + m_storage.clear(); +} + +void +ObjectStorageTest::test_validate_keys() { + torrent::raw_string raw_string_4("test_4\0foo", 10); + + ASSERT_CATCH_INPUT_ERROR( { m_storage.insert(raw_string_4, torrent::Object("a"), rpc::object_storage::flag_generic_type); } ); +} + +// And test many other bad/good string combos. + +// Test for various conversions of fixed_key_type. + +void +ObjectStorageTest::test_access() { + m_storage.insert("generic_1", torrent::Object("gen_a"), rpc::object_storage::flag_generic_type); + m_storage.insert("value_1", int64_t(1), rpc::object_storage::flag_value_type); + + CPPUNIT_ASSERT(m_storage.get_c_str("generic_1").as_string() == "gen_a"); + CPPUNIT_ASSERT(m_storage.set_c_str("generic_1", int64_t(1)).as_value() == 1); + + // Test value from raw and normal, list, etc. + CPPUNIT_ASSERT(m_storage.get_c_str("value_1").as_value() == 1); + CPPUNIT_ASSERT(m_storage.set_c_str("value_1", int64_t(2)).as_value() == 2); + CPPUNIT_ASSERT(m_storage.set_c_str("value_1", "123").as_value() == 123); + CPPUNIT_ASSERT(m_storage.set_c_str("value_1", torrent::raw_string::from_c_str("321")).as_value() == 321); + // CPPUNIT_ASSERT(m_storage.set_c_str("value_1", torrent::raw_bencode::from_c_str("i567e")).as_value() == 567); + + ASSERT_CATCH_INPUT_ERROR( { m_storage.set_c_str("value_1", "e123"); } ); + + // Test string from raw and normal, list, etc. +} diff --git a/test/rpc/object_storage_test.h b/test/rpc/object_storage_test.h new file mode 100644 index 00000000..629f95c3 --- /dev/null +++ b/test/rpc/object_storage_test.h @@ -0,0 +1,26 @@ +#include + +#include "rpc/object_storage.h" + +class ObjectStorageTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ObjectStorageTest); + CPPUNIT_TEST(test_basics); + CPPUNIT_TEST(test_conversions); + CPPUNIT_TEST(test_validate_keys); + CPPUNIT_TEST(test_access); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() { } + void tearDown() {} + + void test_basics(); + + void test_conversions(); + void test_validate_keys(); + + void test_access(); + +private: + rpc::object_storage m_storage; +};