entry dht_tracker::state() const { entry ret(entry::dictionary_t); { entry nodes(entry::list_t); for (node_impl::iterator i(m_dht.begin()) , end(m_dht.end()); i != end; ++i) { std::string node; std::back_insert_iterator<std::string> out(node); write_endpoint(i->addr, out); nodes.list().push_back(entry(node)); } bucket_t cache; m_dht.replacement_cache(cache); for (bucket_t::iterator i(cache.begin()) , end(cache.end()); i != end; ++i) { std::string node; std::back_insert_iterator<std::string> out(node); write_endpoint(i->addr, out); nodes.list().push_back(entry(node)); } if (!nodes.list().empty()) ret["nodes"] = nodes; } ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid()); return ret; }
void on_read(lt::error_code const& ec, std::size_t bytes_transferred) { if (ec) return; using libtorrent::entry; using libtorrent::bdecode; int pos; error_code err; // since the simulation is single threaded, we can get away with just // allocating a single of these static bdecode_node msg; int ret = bdecode(m_buffer, m_buffer + bytes_transferred, msg, err, &pos, 10, 500); if (ret != 0) return; if (msg.type() != bdecode_node::dict_t) return; libtorrent::dht::msg m(msg, m_ep); dht().incoming(m); sock().async_receive_from(asio::mutable_buffers_1(m_buffer, sizeof(m_buffer)) , m_ep, [&](lt::error_code const& ec, std::size_t bytes_transferred) { this->on_read(ec, bytes_transferred); }); }
entry bdecode(const path &file) { ifstream fs(file, ifstream::binary); if(fs.is_open()) { fs.unsetf(ifstream::skipws); return libtorrent::bdecode(std::istream_iterator<char>(fs), std::istream_iterator<char>()); } else return entry(); }
void test_buffer() { char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; buffer b; TEST_CHECK(b.size() == 0); TEST_CHECK(b.capacity() == 0); TEST_CHECK(b.empty()); b.resize(10); TEST_CHECK(b.size() == 10); TEST_CHECK(b.capacity() == 10); std::memcpy(b.begin(), data, 10); b.reserve(50); TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); TEST_CHECK(b.capacity() == 50); b.erase(b.begin() + 6, b.end()); TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 6); b.insert(b.begin(), data + 5, data + 10); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 11); TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0); b.clear(); TEST_CHECK(b.size() == 0); TEST_CHECK(b.capacity() == 50); b.insert(b.end(), data, data + 10); TEST_CHECK(b.size() == 10); TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); b.erase(b.begin(), b.end()); TEST_CHECK(b.capacity() == 50); TEST_CHECK(b.size() == 0); buffer().swap(b); TEST_CHECK(b.capacity() == 0); }
static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) { using libtorrent::wchar_utf8; void* storage = ((converter::rvalue_from_python_storage< std::string>*)data)->storage.bytes; if (PyUnicode_Check(x)) { std::wstring str; str.resize(PyUnicode_GetSize(x) + 1, 0); #if PY_VERSION_HEX >= 0x03020000 int len = PyUnicode_AsWideChar(x, &str[0], str.size()); #else int len = PyUnicode_AsWideChar((PyUnicodeObject*)x, &str[0], str.size()); #endif if (len > -1) { assert(len < int(str.size())); str[len] = 0; } else str[str.size()-1] = 0; std::string utf8; wchar_utf8(str, utf8); new (storage) std::string(utf8); } else { #if PY_VERSION_HEX >= 0x03000000 new (storage) std::string(PyBytes_AsString(x)); #else new (storage) std::string(PyString_AsString(x)); #endif } data->convertible = storage; }
void dht_tracker::send_packet(msg const& m) try { using libtorrent::bencode; using libtorrent::entry; entry e(entry::dictionary_t); TORRENT_ASSERT(!m.transaction_id.empty() || m.message_id == messages::error); e["t"] = m.transaction_id; static char const version_str[] = {'L', 'T' , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR}; e["v"] = std::string(version_str, version_str + 4); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << time_now_string() << " SENDING [" << m.addr << "]:"; TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id; #endif if (m.message_id == messages::error) { TORRENT_ASSERT(m.reply); e["y"] = "e"; entry error_list(entry::list_t); TORRENT_ASSERT(m.error_code > 200 && m.error_code <= 204); error_list.list().push_back(entry(m.error_code)); error_list.list().push_back(entry(m.error_msg)); e["e"] = error_list; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << time_now_string() << " outgoing error: " << m.error_code << " " << m.error_msg; #endif } else if (m.reply) { e["y"] = "r"; e["r"] = entry(entry::dictionary_t); entry& r = e["r"]; r["id"] = std::string(m.id.begin(), m.id.end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << time_now_string() << " reply: " << messages::ids[m.message_id]; #endif if (m.write_token.type() != entry::undefined_t) r["token"] = m.write_token; switch (m.message_id) { case messages::ping: break; case messages::find_node: { write_nodes_entry(r, m); break; } case messages::get_peers: { if (m.peers.empty()) { write_nodes_entry(r, m); } else { r["values"] = entry(entry::list_t); entry& p = r["values"]; std::string endpoint; for (msg::peers_t::const_iterator i = m.peers.begin() , end(m.peers.end()); i != end; ++i) { endpoint.resize(18); std::string::iterator out = endpoint.begin(); write_endpoint(*i, out); endpoint.resize(out - endpoint.begin()); p.list().push_back(entry(endpoint)); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); #endif } break; } case messages::announce_peer: break; break; } } else { e["y"] = "q"; e["a"] = entry(entry::dictionary_t); entry& a = e["a"]; a["id"] = std::string(m.id.begin(), m.id.end()); if (m.write_token.type() != entry::undefined_t) a["token"] = m.write_token; TORRENT_ASSERT(m.message_id <= messages::error); e["q"] = messages::ids[m.message_id]; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " query: " << messages::ids[m.message_id]; #endif switch (m.message_id) { case messages::find_node: { a["target"] = std::string(m.info_hash.begin(), m.info_hash.end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " target: " << boost::lexical_cast<std::string>(m.info_hash); #endif break; } case messages::get_peers: { a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); #endif break; } case messages::announce_peer: a["port"] = m.port; a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); a["token"] = m.write_token; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " port: " << m.port << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); #endif break; default: break; } } m_send_buf.clear(); bencode(std::back_inserter(m_send_buf), e); error_code ec; m_socket.send_to(asio::buffer(&m_send_buf[0] , (int)m_send_buf.size()), m.addr, 0, ec); if (ec) return; #ifdef TORRENT_DHT_VERBOSE_LOGGING m_total_out_bytes += m_send_buf.size(); if (m.reply) { ++m_replies_sent[m.message_id]; m_replies_bytes_sent[m.message_id] += int(m_send_buf.size()); } else { m_queries_out_bytes += m_send_buf.size(); } TORRENT_LOG(dht_tracker) << e; #endif if (!m.piggy_backed_ping) return; msg pm; pm.reply = false; pm.piggy_backed_ping = false; pm.message_id = messages::ping; pm.transaction_id = m.ping_transaction_id; pm.id = m.id; pm.addr = m.addr; send_packet(pm); } catch (std::exception&) { // m_send may fail with "no route to host" // but it shouldn't throw since an error code // is passed in instead TORRENT_ASSERT(false); }
// translate bittorrent kademlia message into the generice kademlia message // used by the library void dht_tracker::on_receive(error_code const& error, size_t bytes_transferred) try { if (error == asio::error::operation_aborted) return; if (!m_socket.is_open()) return; int current_buffer = m_buffer; m_buffer = (m_buffer + 1) & 1; m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] , m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2))); if (error) return; node_ban_entry* match = 0; node_ban_entry* min = m_ban_nodes; ptime now = time_now(); for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) { if (i->src == m_remote_endpoint[current_buffer]) { match = i; break; } if (i->count < min->count) min = i; } if (match) { ++match->count; if (match->count >= 20) { if (now < match->limit) { #ifdef TORRENT_DHT_VERBOSE_LOGGING if (match->count == 20) { TORRENT_LOG(dht_tracker) << time_now_string() << " BANNING PEER [ ip: " << m_remote_endpoint[current_buffer] << " | " "time: " << total_seconds((now - match->limit) + seconds(5)) << " | count: " << match->count << " ]"; } #endif // we've received 20 messages in less than 5 seconds from // this node. Ignore it until it's silent for 5 minutes match->limit = now + minutes(5); return; } // we got 50 messages from this peer, but it was in // more than 5 seconds. Reset the counter and the timer match->count = 0; match->limit = now + seconds(5); } } else { min->count = 1; min->limit = now + seconds(5); min->src = m_remote_endpoint[current_buffer]; } #ifdef TORRENT_DHT_VERBOSE_LOGGING ++m_total_message_input; m_total_in_bytes += bytes_transferred; #endif try { using libtorrent::entry; using libtorrent::bdecode; TORRENT_ASSERT(bytes_transferred > 0); entry e = bdecode(m_in_buf[current_buffer].begin() , m_in_buf[current_buffer].end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << time_now_string() << " RECEIVED [" << m_remote_endpoint[current_buffer] << "]:"; #endif libtorrent::dht::msg m; m.message_id = 0; m.addr = m_remote_endpoint[current_buffer]; m.transaction_id = e["t"].string(); #ifdef TORRENT_DHT_VERBOSE_LOGGING try { entry const* ver = e.find_key("v"); if (!ver) throw std::exception(); std::string const& client = ver->string(); if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT")) { ++m_ut_message_input; TORRENT_LOG(dht_tracker) << " client: uTorrent"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT")) { ++m_lt_message_input; TORRENT_LOG(dht_tracker) << " client: libtorrent"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP")) { ++m_mp_message_input; TORRENT_LOG(dht_tracker) << " client: MooPolice"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR")) { ++m_gr_message_input; TORRENT_LOG(dht_tracker) << " client: GetRight"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MO")) { ++m_mo_message_input; TORRENT_LOG(dht_tracker) << " client: Mono Torrent"; } else { TORRENT_LOG(dht_tracker) << " client: " << client; } } catch (std::exception&) { TORRENT_LOG(dht_tracker) << " client: generic"; }; #endif std::string const& msg_type = e["y"].string(); if (msg_type == "r") { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " reply: transaction: " << m.transaction_id; #endif m.reply = true; entry const& r = e["r"]; std::string const& id = r["id"].string(); if (id.size() != 20) throw std::runtime_error("invalid size of id"); std::copy(id.begin(), id.end(), m.id.begin()); if (entry const* n = r.find_key("values")) { m.peers.clear(); if (n->list().size() == 1) { // assume it's mainline format std::string const& peers = n->list().front().string(); std::string::const_iterator i = peers.begin(); std::string::const_iterator end = peers.end(); while (std::distance(i, end) >= 6) m.peers.push_back(read_v4_endpoint<tcp::endpoint>(i)); } else { // assume it's uTorrent/libtorrent format read_endpoint_list<tcp::endpoint>(n, m.peers); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); #endif } m.nodes.clear(); if (entry const* n = r.find_key("nodes")) { std::string const& nodes = n->string(); std::string::const_iterator i = nodes.begin(); std::string::const_iterator end = nodes.end(); while (std::distance(i, end) >= 26) { node_id id; std::copy(i, i + 20, id.begin()); i += 20; m.nodes.push_back(libtorrent::dht::node_entry( id, read_v4_endpoint<udp::endpoint>(i))); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); #endif } if (entry const* n = r.find_key("nodes2")) { entry::list_type const& contacts = n->list(); for (entry::list_type::const_iterator i = contacts.begin() , end(contacts.end()); i != end; ++i) { std::string const& p = i->string(); if (p.size() < 6 + 20) continue; std::string::const_iterator in = p.begin(); node_id id; std::copy(in, in + 20, id.begin()); in += 20; if (p.size() == 6 + 20) m.nodes.push_back(libtorrent::dht::node_entry( id, read_v4_endpoint<udp::endpoint>(in))); else if (p.size() == 18 + 20) m.nodes.push_back(libtorrent::dht::node_entry( id, read_v6_endpoint<udp::endpoint>(in))); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); #endif } entry const* token = r.find_key("token"); if (token) m.write_token = *token; } else if (msg_type == "q") { m.reply = false; entry const& a = e["a"]; std::string const& id = a["id"].string(); if (id.size() != 20) throw std::runtime_error("invalid size of id"); std::copy(id.begin(), id.end(), m.id.begin()); std::string request_kind(e["q"].string()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " query: " << request_kind; #endif if (request_kind == "ping") { m.message_id = libtorrent::dht::messages::ping; } else if (request_kind == "find_node") { std::string const& target = a["target"].string(); if (target.size() != 20) throw std::runtime_error("invalid size of target id"); std::copy(target.begin(), target.end(), m.info_hash.begin()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " target: " << boost::lexical_cast<std::string>(m.info_hash); #endif m.message_id = libtorrent::dht::messages::find_node; } else if (request_kind == "get_peers") { std::string const& info_hash = a["info_hash"].string(); if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); m.message_id = libtorrent::dht::messages::get_peers; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); #endif } else if (request_kind == "announce_peer") { #ifdef TORRENT_DHT_VERBOSE_LOGGING ++m_announces; #endif std::string const& info_hash = a["info_hash"].string(); if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); m.port = a["port"].integer(); m.write_token = a["token"]; m.message_id = libtorrent::dht::messages::announce_peer; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); TORRENT_LOG(dht_tracker) << " port: " << m.port; if (!m_dht.verify_token(m)) ++m_failed_announces; #endif } else { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : " << request_kind; #endif throw std::runtime_error("unsupported request: " + request_kind); } } else if (msg_type == "e") { entry::list_type const& list = e["e"].list(); m.message_id = messages::error; m.error_msg = list.back().string(); m.error_code = list.front().integer(); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " incoming error: " << m.error_code << " " << m.error_msg; #endif throw std::runtime_error("DHT error message"); } else { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : " << msg_type; #endif throw std::runtime_error("unsupported message type: " + msg_type); } #ifdef TORRENT_DHT_VERBOSE_LOGGING if (!m.reply) { ++m_queries_received[m.message_id]; m_queries_bytes_received[m.message_id] += int(bytes_transferred); } TORRENT_LOG(dht_tracker) << e; #endif TORRENT_ASSERT(m.message_id != messages::error); m_dht.incoming(m); } catch (std::exception& e) { #ifdef TORRENT_DHT_VERBOSE_LOGGING int current_buffer = (m_buffer + 1) & 1; std::string msg(m_in_buf[current_buffer].begin() , m_in_buf[current_buffer].begin() + bytes_transferred); TORRENT_LOG(dht_tracker) << "invalid incoming packet: " << e.what() << "\n" << msg << "\n"; #endif } } catch (std::exception& e) { TORRENT_ASSERT(false); };
session_view::session_view() : m_position(0) , m_print_utp_stats(false) { using libtorrent::find_metric_idx; m_width = 128; std::vector<lt::stats_metric> metrics = lt::session_stats_metrics(); m_cnt[0].resize(metrics.size(), 0); m_cnt[1].resize(metrics.size(), 0); m_queued_bytes_idx = find_metric_idx("disk.queued_write_bytes"); m_wasted_bytes_idx = find_metric_idx("net.recv_redundant_bytes"); m_failed_bytes_idx = find_metric_idx("net.recv_failed_bytes"); m_num_peers_idx = find_metric_idx("peer.num_peers_connected"); m_recv_payload_idx = find_metric_idx("net.recv_payload_bytes"); m_sent_payload_idx = find_metric_idx("net.sent_payload_bytes"); m_unchoked_idx = find_metric_idx("peer.num_peers_up_unchoked"); m_unchoke_slots_idx = find_metric_idx("ses.num_unchoke_slots"); m_limiter_up_queue_idx = find_metric_idx("net.limiter_up_queue"); m_limiter_down_queue_idx = find_metric_idx("net.limiter_down_queue"); m_queued_writes_idx = find_metric_idx("disk.num_write_jobs"); m_queued_reads_idx = find_metric_idx("disk.num_read_jobs"); m_writes_cache_idx = find_metric_idx("disk.write_cache_blocks"); m_reads_cache_idx = find_metric_idx("disk.read_cache_blocks"); m_pinned_idx = find_metric_idx("disk.pinned_blocks"); m_num_blocks_read_idx = find_metric_idx("disk.num_blocks_read"); m_cache_hit_idx = find_metric_idx("disk.num_blocks_cache_hits"); m_blocks_in_use_idx = find_metric_idx("disk.disk_blocks_in_use"); m_blocks_written_idx = find_metric_idx("disk.num_blocks_written"); m_write_ops_idx = find_metric_idx("disk.num_write_ops"); m_mfu_size_idx = find_metric_idx("disk.arc_mfu_size"); m_mfu_ghost_idx = find_metric_idx("disk.arc_mfu_ghost_size"); m_mru_size_idx = find_metric_idx("disk.arc_mru_size"); m_mru_ghost_idx = find_metric_idx("disk.arc_mru_ghost_size"); m_utp_idle = find_metric_idx("utp.num_utp_idle"); m_utp_syn_sent = find_metric_idx("utp.num_utp_syn_sent"); m_utp_connected = find_metric_idx("utp.num_utp_connected"); m_utp_fin_sent = find_metric_idx("utp.num_utp_fin_sent"); m_utp_close_wait = find_metric_idx("utp.num_utp_close_wait"); }
int main(int argc, char const* argv[]) { using libtorrent::directory; using libtorrent::torrent_info; using libtorrent::error_code; using libtorrent::combine_path; using libtorrent::announce_entry; std::map<int, int> piece_sizes; std::map<boost::uint64_t, int> torrent_sizes; std::map<std::string, int> creators; std::map<std::string, int> trackers; if (argc < 2) { fprintf(stderr, "usage: torrent_analyzer <directory-with-torrent-files> ...\n"); return 1; } int num_torrents = 0; int spin_cnt = 0; char const* spinner = "/-\\|"; for (int a = 1; a < argc; ++a) { error_code ec; directory dir(argv[a], ec); for (; !ec && !dir.done(); dir.next(ec)) { fprintf(stderr, "\r%c", spinner[spin_cnt++]); spin_cnt %= 4; std::string filename = dir.file(); if (filename.size() < 9) continue; if (filename.substr(filename.size() - 8) != ".torrent") continue; filename = combine_path(argv[a], filename); torrent_info ti(filename, ec); if (ec) { fprintf(stderr, "failed %s: %s\n", filename.c_str(), ec.message().c_str()); continue; } // piece size int piece_size = ti.piece_length(); piece_sizes[piece_size] += 1; // creator std::string creator = ti.creator(); if (creator.substr(0, 4) != "http") creator = creator.substr(0, creator.find_first_of('/')); creators[creator] += 1; // torrent size boost::uint64_t torrent_size = ti.total_size(); torrent_sizes[torrent_size / (torrent_size_quantization*1024*1024)] += 1; // tracker std::vector<announce_entry> const& tr = ti.trackers(); for (std::vector<announce_entry>::const_iterator i = tr.begin() , end(tr.end()); i != end; ++i) { std::string t = i->url; if (t.size() > 5 && t.substr(0, 6) == "dht://") t = "dht://xxxxx"; trackers[t] += 1; } ++num_torrents; } } printf("writing piece_size.dat\n"); FILE* f = fopen("piece_size.dat", "w+"); double sum = 0.0; for (std::map<int, int>::iterator i = piece_sizes.begin(); i != piece_sizes.end(); ++i) { sum += i->second; fprintf(f, "%5d\t%-2.1f\n", i->first / 1024, sum * 100. / num_torrents); } fclose(f); printf("writing size.dat\n"); f = fopen("size.dat", "w+"); sum = 0.0; for (std::map<boost::uint64_t, int>::iterator i = torrent_sizes.begin(); i != torrent_sizes.end(); ++i) { sum += i->second; fprintf(f, "%" PRId64 "\t%-4.4f\n" , i->first * torrent_size_quantization + (torrent_size_quantization / 2) , sum * 100. / num_torrents); } fclose(f); printf("writing creator.txt\n"); f = fopen("creator.txt", "w+"); std::vector<std::pair<int, std::string> > sorted_list; for (std::map<std::string, int>::iterator i = creators.begin(); i != creators.end(); ++i) { sorted_list.push_back(std::make_pair(i->second, i->first)); } std::sort(sorted_list.rbegin(), sorted_list.rend()); for (std::vector<std::pair<int, std::string> >::iterator i = sorted_list.begin(); i != sorted_list.end(); ++i) { fprintf(f, "%2.1f %%: %s\n", float(i->first) * 100.f / num_torrents, i->second.c_str()); } fclose(f); printf("writing tracker.txt\n"); f = fopen("tracker.txt", "w+"); sorted_list.clear(); for (std::map<std::string, int>::iterator i = trackers.begin(); i != trackers.end(); ++i) { sorted_list.push_back(std::make_pair(i->second, i->first)); } std::sort(sorted_list.rbegin(), sorted_list.rend()); for (std::vector<std::pair<int, std::string> >::iterator i = sorted_list.begin(); i != sorted_list.end(); ++i) { fprintf(f, "%-4.4f %%: %s\n", float(i->first) * 100.f / num_torrents, i->second.c_str()); } fclose(f); }