diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index afb358afe..9d3b8a1e3 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -168,6 +168,10 @@ namespace libtorrent // thread started to run the main downloader loop struct session_impl: boost::noncopyable { + + // the size of each allocation that is chained in the send buffer + enum { send_buffer_size = 200 }; + #ifndef NDEBUG friend class ::libtorrent::peer_connection; #endif @@ -329,6 +333,24 @@ namespace libtorrent { return m_dht_proxy; } #endif +#ifdef TORRENT_STATS + void log_buffer_usage() + { + int send_buffer_capacity = 0; + int used_send_buffer = 0; + for (connection_map::const_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + send_buffer_capacity += i->second->send_buffer_capacity(); + used_send_buffer += i->second->send_buffer_size(); + } + assert(send_buffer_capacity >= used_send_buffer); + m_buffer_usage_logger << log_time() << " send_buffer_size: " << send_buffer_capacity << std::endl; + m_buffer_usage_logger << log_time() << " used_send_buffer: " << used_send_buffer << std::endl; + m_buffer_usage_logger << log_time() << " send_buffer_utilization: " + << (used_send_buffer * 100.f / send_buffer_capacity) << std::endl; + } +#endif void start_lsd(); void start_natpmp(); void start_upnp(); @@ -339,11 +361,25 @@ namespace libtorrent // handles delayed alerts alert_manager m_alerts; + + std::pair allocate_buffer(int size); + void free_buffer(char* buf, int size); + void free_disk_buffer(char* buf); // private: void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); + // handles disk io requests asynchronously + // peers have pointers into the disk buffer + // pool, and must be destructed before this + // object. + disk_io_thread m_disk_thread; + + // this pool is used to allocate and recycle send + // buffers from. + boost::pool<> m_send_buffers; + // this is where all active sockets are stored. // the selector can sleep while there's no activity on // them @@ -358,9 +394,6 @@ namespace libtorrent // when they are destructed. file_pool m_files; - // handles disk io requests asynchronously - disk_io_thread m_disk_thread; - // this is a list of half-open tcp connections // (only outgoing connections) // this has to be one of the last @@ -526,6 +559,10 @@ namespace libtorrent // logger used to write bandwidth usage statistics std::ofstream m_stats_logger; int m_second_counter; + // used to log send buffer usage statistics + std::ofstream m_buffer_usage_logger; + // the number of send buffers that are allocated + int m_buffer_allocations; #endif #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) boost::shared_ptr create_log(std::string const& name diff --git a/include/libtorrent/bt_peer_connection.hpp b/include/libtorrent/bt_peer_connection.hpp index 0fcba89a8..53e9667fc 100755 --- a/include/libtorrent/bt_peer_connection.hpp +++ b/include/libtorrent/bt_peer_connection.hpp @@ -208,7 +208,7 @@ namespace libtorrent void write_cancel(peer_request const& r); void write_bitfield(std::vector const& bitfield); void write_have(int index); - void write_piece(peer_request const& r, char const* buffer); + void write_piece(peer_request const& r, char* buffer); void write_handshake(); #ifndef TORRENT_DISABLE_EXTENSIONS void write_extensions(); @@ -270,8 +270,17 @@ namespace libtorrent // these functions encrypt the send buffer if m_rc4_encrypted // is true, otherwise it passes the call to the // peer_connection functions of the same names - void send_buffer(char* begin, char* end); + void send_buffer(char* buf, int size); buffer::interval allocate_send_buffer(int size); + template + void append_send_buffer(char* buffer, int size, Destructor const& destructor) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_rc4_encrypted) + m_RC4_handler->encrypt(buffer, size); +#endif + peer_connection::append_send_buffer(buffer, size, destructor); + } void setup_send(); // Returns offset at which bytestream (src, src + src_size) diff --git a/include/libtorrent/chained_buffer.hpp b/include/libtorrent/chained_buffer.hpp new file mode 100644 index 000000000..dd98fd05f --- /dev/null +++ b/include/libtorrent/chained_buffer.hpp @@ -0,0 +1,192 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CHAINED_BUFFER_HPP_INCLUDED +#define TORRENT_CHAINED_BUFFER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace libtorrent +{ + struct chained_buffer + { + chained_buffer(): m_bytes(0), m_capacity(0) {} + + struct buffer_t + { + boost::function free; // destructs the buffer + char* buf; // the first byte of the buffer + int size; // the total size of the buffer + + char* start; // the first byte to send/receive in the buffer + int used_size; // this is the number of bytes to send/receive + }; + + bool empty() const { return m_bytes == 0; } + int size() const { return m_bytes; } + int capacity() const { return m_capacity; } + + void pop_front(int bytes_to_pop) + { + assert(bytes_to_pop <= m_bytes); + while (bytes_to_pop > 0 && !m_vec.empty()) + { + buffer_t& b = m_vec.front(); + if (b.used_size > bytes_to_pop) + { + b.start += bytes_to_pop; + b.used_size -= bytes_to_pop; + m_bytes -= bytes_to_pop; + assert(m_bytes <= m_capacity); + assert(m_bytes >= 0); + assert(m_capacity >= 0); + break; + } + + b.free(b.buf); + m_bytes -= b.used_size; + m_capacity -= b.size; + bytes_to_pop -= b.used_size; + assert(m_bytes >= 0); + assert(m_capacity >= 0); + assert(m_bytes <= m_capacity); + m_vec.pop_front(); + } + } + + template + void append_buffer(char* buffer, int size, int used_size, D const& destructor) + { + assert(size >= used_size); + buffer_t b; + b.buf = buffer; + b.size = size; + b.start = buffer; + b.used_size = used_size; + b.free = destructor; + m_vec.push_back(b); + + m_bytes += used_size; + m_capacity += size; + assert(m_bytes <= m_capacity); + } + + // returns the number of bytes available at the + // end of the last chained buffer. + int space_in_last_buffer() + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + return b.size - b.used_size - (b.start - b.buf); + } + + // tries to copy the given buffer to the end of the + // last chained buffer. If there's not enough room + // it returns false + bool append(char const* buf, int size) + { + char* insert = allocate_appendix(size); + if (insert == 0) return false; + std::memcpy(insert, buf, size); + return true; + } + + // tries to allocate memory from the end + // of the last buffer. If there isn't + // enough room, returns 0 + char* allocate_appendix(int size) + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + char* insert = b.start + b.used_size; + if (insert + size > b.buf + b.size) return 0; + b.used_size += size; + m_bytes += size; + assert(m_bytes <= m_capacity); + return insert; + } + + std::list const& build_iovec(int to_send) + { + m_tmp_vec.clear(); + + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); to_send > 0 && i != end; ++i) + { + if (i->used_size > to_send) + { + assert(to_send > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, to_send)); + break; + } + assert(i->used_size > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, i->used_size)); + to_send -= i->used_size; + } + return m_tmp_vec; + } + + ~chained_buffer() + { + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); i != end; ++i) + { + i->free(i->buf); + } + } + + private: + + // this is the list of all the buffers we want to + // send + std::list m_vec; + + // this is the number of bytes in the send buf. + // this will always be equal to the sum of the + // size of all buffers in vec + int m_bytes; + + // the total size of all buffers in the chain + // including unused space + int m_capacity; + + // this is the vector of buffers used when + // invoking the async write call + std::list m_tmp_vec; + }; +} + +#endif + diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index 61ca9bc53..b893aaf60 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -94,6 +94,11 @@ namespace libtorrent disk_io_thread(int block_size = 16 * 1024); ~disk_io_thread(); +#ifdef TORRENT_STATS + int disk_allocations() const + { return m_allocations; } +#endif + // aborts read operations void stop(boost::intrusive_ptr s); void add_job(disk_io_job const& j @@ -110,6 +115,7 @@ namespace libtorrent void operator()(); char* allocate_buffer(); + void free_buffer(char* buf); private: @@ -129,6 +135,9 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS std::ofstream m_log; #endif +#ifdef TORRENT_STATS + int m_allocations; +#endif // thread for performing blocking disk io operations boost::thread m_disk_io_thread; diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index ac9aa0322..ce7e61ec5 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -51,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -73,6 +74,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket_type.hpp" #include "libtorrent/intrusive_ptr_base.hpp" #include "libtorrent/assert.hpp" +#include "libtorrent/chained_buffer.hpp" namespace libtorrent { @@ -356,14 +358,23 @@ namespace libtorrent virtual boost::optional downloading_piece_progress() const { - #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "downloading_piece_progress() dispatched to the base class!\n"; - #endif +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "downloading_piece_progress() dispatched to the base class!\n"; +#endif return boost::optional(); } - void send_buffer(char const* begin, char const* end); + void send_buffer(char const* begin, int size); buffer::interval allocate_send_buffer(int size); + template + void append_send_buffer(char* buffer, int size, Destructor const& destructor) + { + m_send_buffer.append_buffer(buffer, size, size, destructor); +#ifdef TORRENT_STATS + m_ses.m_buffer_usage_logger << log_time() << " append_send_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + } void setup_send(); #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES @@ -376,6 +387,12 @@ namespace libtorrent bool has_country() const { return m_country[0] != 0; } #endif + int send_buffer_size() const + { return m_send_buffer.size(); } + + int send_buffer_capacity() const + { return m_send_buffer.capacity(); } + protected: virtual void get_specific_peer_info(peer_info& p) const = 0; @@ -388,7 +405,7 @@ namespace libtorrent virtual void write_cancel(peer_request const& r) = 0; virtual void write_have(int index) = 0; virtual void write_keepalive() = 0; - virtual void write_piece(peer_request const& r, char const* buffer) = 0; + virtual void write_piece(peer_request const& r, char* buffer) = 0; virtual void write_reject_request(peer_request const& r) = 0; virtual void write_allow_fast(int piece) = 0; @@ -401,13 +418,6 @@ namespace libtorrent virtual void on_sent(asio::error_code const& error , std::size_t bytes_transferred) = 0; - int send_buffer_size() const - { - return (int)m_send_buffer[0].size() - + (int)m_send_buffer[1].size() - - m_write_pos; - } - #ifndef TORRENT_DISABLE_ENCRYPTION buffer::interval wr_recv_buffer() { @@ -512,31 +522,13 @@ namespace libtorrent int m_recv_pos; buffer m_recv_buffer; - // this is the buffer where data that is - // to be sent is stored until it gets - // consumed by send(). Since asio requires - // the memory buffer that is given to async. - // operations to remain valid until the operation - // finishes, there has to be two buffers. While - // waiting for a async_write operation on one - // buffer, the other is used to write data to - // be queued up. - buffer m_send_buffer[2]; - // the current send buffer is the one to write to. - // (m_current_send_buffer + 1) % 2 is the - // buffer we're currently waiting for. - int m_current_send_buffer; + chained_buffer m_send_buffer; // the number of bytes we are currently reading // from disk, that will be added to the send // buffer as soon as they complete int m_reading_bytes; - // if the sending buffer doesn't finish in one send - // operation, this is the position within that buffer - // where the next operation should continue - int m_write_pos; - // timeouts ptime m_last_receive; ptime m_last_sent; diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 9db79ea3d..67d74153d 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -184,6 +184,9 @@ namespace libtorrent std::pair check_files(std::vector& pieces , int& num_pieces, boost::recursive_mutex& mutex); + // frees a buffer that was returned from a read operation + void free_buffer(char* buf); + void write_resume_data(entry& rd) const; bool verify_resume_data(entry& rd, std::string& error); diff --git a/include/libtorrent/time.hpp b/include/libtorrent/time.hpp index 27d61af9d..d4a63cc5a 100644 --- a/include/libtorrent/time.hpp +++ b/include/libtorrent/time.hpp @@ -389,5 +389,18 @@ namespace libtorrent #endif #endif + +namespace libtorrent +{ + inline std::string log_time() + { + static ptime start = time_now(); + char ret[200]; + std::sprintf(ret, "%d", total_milliseconds(time_now() - start)); + return ret; + } +} + + #endif diff --git a/include/libtorrent/web_peer_connection.hpp b/include/libtorrent/web_peer_connection.hpp index 1290f14a1..8c9360e8f 100755 --- a/include/libtorrent/web_peer_connection.hpp +++ b/include/libtorrent/web_peer_connection.hpp @@ -122,7 +122,7 @@ namespace libtorrent void write_request(peer_request const& r); void write_cancel(peer_request const& r) {} void write_have(int index) {} - void write_piece(peer_request const& r, char const* buffer) { assert(false); } + void write_piece(peer_request const& r, char* buffer) { assert(false); } void write_keepalive() {} void on_connected(); void write_reject_request(peer_request const&) {} diff --git a/parse_buffer_log.py b/parse_buffer_log.py new file mode 100644 index 000000000..b424ea899 --- /dev/null +++ b/parse_buffer_log.py @@ -0,0 +1,83 @@ +import os, sys, time + +lines = open(sys.argv[1], 'rb').readlines() + +#keys = ['send_buffer_utilization'] +keys = ['send_buffer_size', 'used_send_buffer', 'protocol_buffer'] +#keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer'] +#keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer', 'append_send_buffer'] + +average = ['send_buffer_utilization', 'send_buffer_size', 'used_send_buffer'] +average_interval = 120000 +render = 'lines' + +time_limit = -1 +if len(sys.argv) > 2: + time_limit = long(sys.argv[2]) + + +# logfile format: +# +# example: +# 16434 allocate_buffer: 17 +for k in keys: + + last_sample = 0 + average_accumulator = 0 + average_samples = 0 + peak = 0 + + out = open(k + '.dat', 'wb') + eval_average = False + if k in average: + eval_average = True + peak_out = open(k + '_peak.dat', 'wb') + + for l in lines: + l = l.split(' ') + if len(l) != 3: + print l + continue + try: + if l[1] == k + ':': + if time_limit != -1 and long(l[0]) > time_limit: break + time = l[0] + value = l[2] + if eval_average: + while long(time) > last_sample + average_interval: + last_sample = last_sample + average_interval + if average_samples < 1: average_samples = 1 + print >>out, '%d %f' % (last_sample, average_accumulator / average_samples) + print >>peak_out, '%d %f' % (last_sample, peak) + average_accumulator = 0 + average_samples = 0 + peak = 0 + average_accumulator = average_accumulator + float(value) + average_samples = average_samples + 1 + if float(value) > peak: peak = float(value) + else: + print >>out, time + ' ' + value, + except: + print l + + +out = open('send_buffer.gnuplot', 'wb') +print >>out, "set term png size 1200,700" +print >>out, 'set output "send_buffer.png"' +print >>out, 'set xrange [0:*]' +print >>out, 'set xlabel "time (ms)"' +print >>out, 'set ylabel "bytes (B)"' +print >>out, "set style data lines" +print >>out, "set key box" +print >>out, 'plot', +for k in keys: + if k in average: + print >>out, ' "%s.dat" using 1:2 title "%s %d seconds average" with %s,' % (k, k, average_interval / 1000., render), + print >>out, ' "%s_peak.dat" using 1:2 title "%s %d seconds peak" with %s,' % (k, k, average_interval / 1000., render), + else: + print >>out, ' "%s.dat" using 1:2 title "%s" with %s,' % (k, k, render), +print >>out, 'x=0' +out.close() + +os.system('gnuplot send_buffer.gnuplot'); + diff --git a/parse_session_stats.py b/parse_session_stats.py new file mode 100644 index 000000000..a26f131bf --- /dev/null +++ b/parse_session_stats.py @@ -0,0 +1,28 @@ +import os, sys, time + +keys = ['upload rate', 'download rate', 'downloading torrents', \ + 'seeding torrents', 'peers', 'connecting peers', 'disk block buffers'] + +axes = ['x1y2', 'x1y2', 'x1y1', 'x1y1', 'x1y1', 'x1y1', 'x1y1'] + +out = open('session_stats.gnuplot', 'wb') +print >>out, "set term png size 1200,700" +print >>out, 'set output "session_stats.png"' +print >>out, 'set xrange [0:*]' +print >>out, 'set xlabel "time (s)"' +print >>out, 'set ylabel "number"' +print >>out, 'set y2label "Rate (B/s)"' +print >>out, 'set y2range [0:*]' +print >>out, 'set y2tics 20000' +print >>out, "set style data lines" +print >>out, "set key box" +print >>out, 'plot', +column = 2 +for k in keys: + print >>out, ' "%s" using 1:%d title "%s" axes %s with lines,' % (sys.argv[1], column, k, axes[column-2]), + column = column + 1 +print >>out, 'x=0' +out.close() + +os.system('gnuplot session_stats.gnuplot'); + diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index e27a7f4c3..da7fd8937 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -251,12 +251,10 @@ namespace libtorrent (*m_logger) << time_now_string() << " ==> DHT_PORT [ " << listen_port << " ]\n"; #endif - buffer::interval packet = allocate_send_buffer(7); - detail::write_uint32(3, packet.begin); - detail::write_uint8(msg_dht_port, packet.begin); - detail::write_uint16(listen_port, packet.begin); - assert(packet.begin == packet.end); - setup_send(); + char msg[] = {0,0,0,3, msg_dht_port, 0, 0}; + char* ptr = msg + 5; + detail::write_uint16(listen_port, ptr); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_have_all() @@ -270,8 +268,8 @@ namespace libtorrent (*m_logger) << time_now_string() << " ==> HAVE_ALL\n"; #endif - char buf[] = {0,0,0,1, msg_have_all}; - send_buffer(buf, buf + sizeof(buf)); + char msg[] = {0,0,0,1, msg_have_all}; + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_have_none() @@ -285,8 +283,8 @@ namespace libtorrent (*m_logger) << time_now_string() << " ==> HAVE_NONE\n"; #endif - char buf[] = {0,0,0,1, msg_have_none}; - send_buffer(buf, buf + sizeof(buf)); + char msg[] = {0,0,0,1, msg_have_none}; + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_reject_request(peer_request const& r) @@ -296,22 +294,12 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); - char buf[] = {0,0,0,13, msg_reject_request}; - - buffer::interval i = allocate_send_buffer(17); - - std::copy(buf, buf + 5, i.begin); - i.begin += 5; - - // index - detail::write_int32(r.piece, i.begin); - // begin - detail::write_int32(r.start, i.begin); - // length - detail::write_int32(r.length, i.begin); - assert(i.begin == i.end); - - setup_send(); + char msg[] = {0,0,0,13, msg_reject_request,0,0,0,0, 0,0,0,0, 0,0,0,0}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_allow_fast(int piece) @@ -321,11 +309,10 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); - char buf[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0}; - - char* ptr = buf + 5; + char msg[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0}; + char* ptr = msg + 5; detail::write_int32(piece, ptr); - send_buffer(buf, buf + sizeof(buf)); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::get_specific_peer_info(peer_info& p) const @@ -556,8 +543,8 @@ namespace libtorrent assert(secret); hasher h; - const char keyA[] = "keyA"; - const char keyB[] = "keyB"; + static const char keyA[] = "keyA"; + static const char keyB[] = "keyB"; // encryption rc4 longkeys // outgoing connection : hash ('keyA',S,SKEY) @@ -587,17 +574,16 @@ namespace libtorrent #endif } - void bt_peer_connection::send_buffer(char* begin, char* end) + void bt_peer_connection::send_buffer(char* buf, int size) { - assert (begin); - assert (end); - assert (end > begin); - assert (!m_rc4_encrypted || m_encrypted); + assert(buf); + assert(size > 0); + assert(!m_rc4_encrypted || m_encrypted); if (m_rc4_encrypted) - m_RC4_handler->encrypt(begin, end - begin); + m_RC4_handler->encrypt(buf, size); - peer_connection::send_buffer(begin, end); + peer_connection::send_buffer(buf, size); } buffer::interval bt_peer_connection::allocate_send_buffer(int size) @@ -606,6 +592,7 @@ namespace libtorrent if (m_rc4_encrypted) { + assert(m_enc_send_buffer.left() == 0); m_enc_send_buffer = peer_connection::allocate_send_buffer(size); return m_enc_send_buffer; } @@ -620,24 +607,24 @@ namespace libtorrent { assert(!m_rc4_encrypted || m_encrypted); - if (m_rc4_encrypted) + if (m_rc4_encrypted && m_enc_send_buffer.left()) { - assert (m_enc_send_buffer.begin); - assert (m_enc_send_buffer.end); - assert (m_enc_send_buffer.left() > 0); + assert(m_enc_send_buffer.begin); + assert(m_enc_send_buffer.end); - m_RC4_handler->encrypt (m_enc_send_buffer.begin, m_enc_send_buffer.left()); + m_RC4_handler->encrypt(m_enc_send_buffer.begin, m_enc_send_buffer.left()); + m_enc_send_buffer.end = m_enc_send_buffer.begin; } peer_connection::setup_send(); } int bt_peer_connection::get_syncoffset(char const* src, int src_size, - char const* target, int target_size) const + char const* target, int target_size) const { - assert (target_size >= src_size); - assert (src_size > 0); - assert (src); - assert (target); + assert(target_size >= src_size); + assert(src_size > 0); + assert(src); + assert(target); int traverse_limit = target_size - src_size; @@ -1288,8 +1275,8 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); - char buf[] = {0,0,0,0}; - send_buffer(buf, buf + sizeof(buf)); + char msg[] = {0,0,0,0}; + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_cancel(peer_request const& r) @@ -1299,22 +1286,12 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); - char buf[] = {0,0,0,13, msg_cancel}; - - buffer::interval i = allocate_send_buffer(17); - - std::copy(buf, buf + 5, i.begin); - i.begin += 5; - - // index - detail::write_int32(r.piece, i.begin); - // begin - detail::write_int32(r.start, i.begin); - // length - detail::write_int32(r.length, i.begin); - assert(i.begin == i.end); - - setup_send(); + char msg[17] = {0,0,0,13, msg_cancel}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_request(peer_request const& r) @@ -1324,22 +1301,13 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); - char buf[] = {0,0,0,13, msg_request}; + char msg[17] = {0,0,0,13, msg_request}; + char* ptr = msg + 5; - buffer::interval i = allocate_send_buffer(17); - - std::copy(buf, buf + 5, i.begin); - i.begin += 5; - - // index - detail::write_int32(r.piece, i.begin); - // begin - detail::write_int32(r.start, i.begin); - // length - detail::write_int32(r.length, i.begin); - assert(i.begin == i.end); - - setup_send(); + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_bitfield(std::vector const& bitfield) @@ -1526,7 +1494,7 @@ namespace libtorrent if (is_choked()) return; char msg[] = {0,0,0,1,msg_choke}; - send_buffer(msg, msg + sizeof(msg)); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_unchoke() @@ -1536,7 +1504,7 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_unchoke}; - send_buffer(msg, msg + sizeof(msg)); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_interested() @@ -1546,7 +1514,7 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_interested}; - send_buffer(msg, msg + sizeof(msg)); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_not_interested() @@ -1556,7 +1524,7 @@ namespace libtorrent assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_not_interested}; - send_buffer(msg, msg + sizeof(msg)); + send_buffer(msg, sizeof(msg)); } void bt_peer_connection::write_have(int index) @@ -1567,34 +1535,39 @@ namespace libtorrent assert(index < associated_torrent().lock()->torrent_file().num_pieces()); assert(m_sent_handshake && m_sent_bitfield); - const int packet_size = 9; - char msg[packet_size] = {0,0,0,5,msg_have}; + char msg[] = {0,0,0,5,msg_have,0,0,0,0}; char* ptr = msg + 5; detail::write_int32(index, ptr); - send_buffer(msg, msg + packet_size); + send_buffer(msg, sizeof(msg)); } - void bt_peer_connection::write_piece(peer_request const& r, char const* buffer) + void bt_peer_connection::write_piece(peer_request const& r, char* buffer) { INVARIANT_CHECK; assert(m_sent_handshake && m_sent_bitfield); - const int packet_size = 4 + 5 + 4 + r.length; - boost::shared_ptr t = associated_torrent().lock(); assert(t); - buffer::interval i = allocate_send_buffer(packet_size); - - detail::write_int32(packet_size-4, i.begin); - detail::write_uint8(msg_piece, i.begin); - detail::write_int32(r.piece, i.begin); - detail::write_int32(r.start, i.begin); + char msg[4 + 1 + 4 + 4]; + char* ptr = msg; + assert(r.length <= 16 * 1024); + detail::write_int32(r.length + 1 + 4 + 4, ptr); + detail::write_uint8(msg_piece, ptr); + detail::write_int32(r.piece, ptr); + detail::write_int32(r.start, ptr); + send_buffer(msg, sizeof(msg)); + + append_send_buffer(buffer, r.length + , boost::bind(&session_impl::free_disk_buffer + , boost::ref(m_ses), _1)); + +/* + buffer::interval i = allocate_send_buffer(r.length); std::memcpy(i.begin, buffer, r.length); - - assert(i.begin + r.length == i.end); - + t->filesystem().free_buffer(buffer); +*/ m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); setup_send(); } diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 4fb2cfb3a..c7f766ac4 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -37,18 +37,6 @@ POSSIBILITY OF SUCH DAMAGE. #ifdef TORRENT_DISK_STATS #include "libtorrent/time.hpp" -#include - -namespace -{ - std::string log_time() - { - using namespace libtorrent; - static ptime start = time_now(); - return boost::lexical_cast( - total_milliseconds(time_now() - start)); - } -} #endif @@ -64,7 +52,9 @@ namespace libtorrent #endif , m_disk_io_thread(boost::ref(*this)) { - +#ifdef TORRENT_STATS + m_allocations = 0; +#endif #ifdef TORRENT_DISK_STATS m_log.open("disk_io_thread.log", std::ios::trunc); #endif @@ -188,9 +178,21 @@ namespace libtorrent char* disk_io_thread::allocate_buffer() { boost::mutex::scoped_lock l(m_mutex); +#ifdef TORRENT_STATS + ++m_allocations; +#endif return (char*)m_pool.ordered_malloc(); } + void disk_io_thread::free_buffer(char* buf) + { + boost::mutex::scoped_lock l(m_mutex); +#ifdef TORRENT_STATS + --m_allocations; +#endif + m_pool.ordered_free(buf); + } + void disk_io_thread::operator()() { for (;;) @@ -225,10 +227,14 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " read " << j.buffer_size << std::endl; #endif + free_buffer = false; if (j.buffer == 0) { l.lock(); j.buffer = (char*)m_pool.ordered_malloc(); +#ifdef TORRENT_STATS + ++m_allocations; +#endif l.unlock(); assert(j.buffer_size <= m_block_size); if (j.buffer == 0) @@ -238,10 +244,6 @@ namespace libtorrent break; } } - else - { - free_buffer = false; - } ret = j.storage->read_impl(j.buffer, j.piece, j.offset , j.buffer_size); @@ -301,6 +303,9 @@ namespace libtorrent { l.lock(); m_pool.ordered_free(j.buffer); +#ifdef TORRENT_STATS + --m_allocations; +#endif } } } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index ad5087325..535a90142 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -81,9 +81,7 @@ namespace libtorrent , m_last_unchoke(min_time()) , m_packet_size(0) , m_recv_pos(0) - , m_current_send_buffer(0) , m_reading_bytes(0) - , m_write_pos(0) , m_last_receive(time_now()) , m_last_sent(time_now()) , m_socket(s) @@ -161,9 +159,7 @@ namespace libtorrent , m_last_unchoke(min_time()) , m_packet_size(0) , m_recv_pos(0) - , m_current_send_buffer(0) , m_reading_bytes(0) - , m_write_pos(0) , m_last_receive(time_now()) , m_last_sent(time_now()) , m_socket(s) @@ -2082,8 +2078,7 @@ namespace libtorrent p.remote_dl_rate = 0; } - p.send_buffer_size = int(m_send_buffer[0].capacity() - + m_send_buffer[1].capacity()); + p.send_buffer_size = m_send_buffer.capacity(); } void peer_connection::cut_receive_buffer(int size, int packet_size) @@ -2386,8 +2381,7 @@ namespace libtorrent shared_ptr t = m_torrent.lock(); if (m_bandwidth_limit[upload_channel].quota_left() == 0 - && (!m_send_buffer[m_current_send_buffer].empty() - || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + && !m_send_buffer.empty() && !m_connecting && t && !m_ignore_bandwidth_limits) @@ -2415,32 +2409,21 @@ namespace libtorrent assert(!m_writing); - int sending_buffer = (m_current_send_buffer + 1) & 1; - if (m_send_buffer[sending_buffer].empty()) - { - // this means we have to swap buffer, because there's no - // previous buffer we're still waiting for. - std::swap(m_current_send_buffer, sending_buffer); - m_write_pos = 0; - } - // send the actual buffer - if (!m_send_buffer[sending_buffer].empty()) + if (!m_send_buffer.empty()) { - int amount_to_send = (int)m_send_buffer[sending_buffer].size() - m_write_pos; + int amount_to_send = m_send_buffer.size(); int quota_left = m_bandwidth_limit[upload_channel].quota_left(); if (!m_ignore_bandwidth_limits && amount_to_send > quota_left) amount_to_send = quota_left; assert(amount_to_send > 0); - assert(m_write_pos < (int)m_send_buffer[sending_buffer].size()); #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << "async_write " << amount_to_send << " bytes\n"; #endif - m_socket->async_write_some(asio::buffer( - &m_send_buffer[sending_buffer][m_write_pos], amount_to_send) - , bind(&peer_connection::on_send_data, self(), _1, _2)); + std::list const& vec = m_send_buffer.build_iovec(amount_to_send); + m_socket->async_write_some(vec, bind(&peer_connection::on_send_data, self(), _1, _2)); m_writing = true; } @@ -2511,10 +2494,32 @@ namespace libtorrent m_recv_buffer.resize(m_packet_size); } - void peer_connection::send_buffer(char const* begin, char const* end) + void peer_connection::send_buffer(char const* buf, int size) { - buffer& buf = m_send_buffer[m_current_send_buffer]; - buf.insert(buf.end(), begin, end); + int free_space = m_send_buffer.space_in_last_buffer(); + if (free_space > size) free_space = size; + if (free_space > 0) + { + m_send_buffer.append(buf, free_space); + size -= free_space; + buf += free_space; +#ifdef TORRENT_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer: " + << free_space << std::endl; + m_ses.log_buffer_usage(); +#endif + } + if (size <= 0) return; + + std::pair buffer = m_ses.allocate_buffer(size); + assert(buffer.second >= size); + std::memcpy(buffer.first, buf, size); + m_send_buffer.append_buffer(buffer.first, buffer.second, size + , bind(&session_impl::free_buffer, boost::ref(m_ses), _1, buffer.second)); +#ifdef TORRENT_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer_alloc: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif setup_send(); } @@ -2522,10 +2527,29 @@ namespace libtorrent // return value is destructed buffer::interval peer_connection::allocate_send_buffer(int size) { - buffer& buf = m_send_buffer[m_current_send_buffer]; - buf.resize(buf.size() + size); - buffer::interval ret(&buf[0] + buf.size() - size, &buf[0] + buf.size()); - return ret; + char* insert = m_send_buffer.allocate_appendix(size); + if (insert == 0) + { + std::pair buffer = m_ses.allocate_buffer(size); + assert(buffer.second >= size); + m_send_buffer.append_buffer(buffer.first, buffer.second, size + , bind(&session_impl::free_buffer, boost::ref(m_ses), _1, buffer.second)); + buffer::interval ret(buffer.first, buffer.first + size); +#ifdef TORRENT_STATS + m_ses.m_buffer_usage_logger << log_time() << " allocate_buffer_alloc: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + return ret; + } + else + { +#ifdef TORRENT_STATS + m_ses.m_buffer_usage_logger << log_time() << " allocate_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + buffer::interval ret(insert, insert + size); + return ret; + } } template @@ -2647,8 +2671,7 @@ namespace libtorrent // if we have requests or pending data to be sent or announcements to be made // we want to send data - return (!m_send_buffer[m_current_send_buffer].empty() - || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + return !m_send_buffer.empty() && (m_bandwidth_limit[upload_channel].quota_left() > 0 || m_ignore_bandwidth_limits) && !m_connecting; @@ -2763,6 +2786,9 @@ namespace libtorrent INVARIANT_CHECK; assert(m_writing); + + m_send_buffer.pop_front(bytes_transferred); + m_writing = false; if (!m_ignore_bandwidth_limits) @@ -2772,9 +2798,6 @@ namespace libtorrent (*m_logger) << "wrote " << bytes_transferred << " bytes\n"; #endif - m_write_pos += bytes_transferred; - - if (error) { #ifdef TORRENT_VERBOSE_LOGGING @@ -2787,34 +2810,11 @@ namespace libtorrent assert(!m_connecting); assert(bytes_transferred > 0); - int sending_buffer = (m_current_send_buffer + 1) & 1; - - assert(int(m_send_buffer[sending_buffer].size()) >= m_write_pos); - if (int(m_send_buffer[sending_buffer].size()) == m_write_pos) - { - m_send_buffer[sending_buffer].clear(); - m_write_pos = 0; - } - m_last_sent = time_now(); on_sent(error, bytes_transferred); fill_send_buffer(); - if (m_choked) - { - for (int i = 0; i < 2; ++i) - { - if (int(m_send_buffer[i].size()) < 64 - && int(m_send_buffer[i].capacity()) > 128) - { - buffer tmp(m_send_buffer[i]); - tmp.swap(m_send_buffer[i]); - assert(m_send_buffer[i].capacity() == m_send_buffer[i].size()); - } - } - } - setup_send(); } catch (std::exception& e) @@ -2876,8 +2876,6 @@ namespace libtorrent } } */ - assert(m_write_pos <= int(m_send_buffer[ - (m_current_send_buffer + 1) & 1].size())); // extremely expensive invariant check /* diff --git a/src/policy.cpp b/src/policy.cpp index 49671c5db..014d3ccaa 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -1426,16 +1426,16 @@ namespace libtorrent int nonempty_connections = 0; std::set
unique_test; - std::set unique_test2; +// std::set unique_test2; for (const_iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer const& p = *i; - if (!m_torrent->settings().allow_multiple_connections_per_ip) - assert(unique_test.find(p.ip.address()) == unique_test.end()); - assert(unique_test2.find(p.ip) == unique_test2.end()); - unique_test.insert(p.ip.address()); - unique_test2.insert(p.ip); +// if (!m_torrent->settings().allow_multiple_connections_per_ip) +// assert(unique_test.find(p.ip.address()) == unique_test.end()); +// assert(unique_test2.find(p.ip) == unique_test2.end()); +// unique_test.insert(p.ip.address()); +// unique_test2.insert(p.ip); ++total_connections; if (!p.connection) { diff --git a/src/session_impl.cpp b/src/session_impl.cpp index a58906156..18ae5287e 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -507,7 +507,8 @@ namespace detail std::pair listen_port_range , fingerprint const& cl_fprint , char const* listen_interface) - : m_strand(m_io_service) + : m_send_buffers(send_buffer_size) + , m_strand(m_io_service) , m_files(40) , m_half_open(m_io_service) , m_download_channel(m_io_service, peer_connection::download_channel) @@ -546,7 +547,7 @@ namespace detail #endif #ifdef TORRENT_STATS - m_stats_logger.open("session_stats.log"); + m_stats_logger.open("session_stats.log", std::ios::trunc); m_stats_logger << "1. second\n" "2. upload rate\n" @@ -555,7 +556,9 @@ namespace detail "5. seeding torrents\n" "6. peers\n" "7. connecting peers\n" + "8. disk block buffers\n" "\n"; + m_buffer_usage_logger.open("buffer_stats.log", std::ios::trunc); m_second_counter = 0; #endif @@ -999,7 +1002,8 @@ namespace detail { session_impl::mutex_t::scoped_lock l(m_mutex); - INVARIANT_CHECK; +// too expensive +// INVARIANT_CHECK; if (e) { @@ -1031,7 +1035,7 @@ namespace detail else ++downloading_torrents; } - int num_connections = 0; + int num_complete_connections = 0; int num_half_open = 0; for (connection_map::iterator i = m_connections.begin() , end(m_connections.end()); i != end; ++i) @@ -1039,7 +1043,7 @@ namespace detail if (i->second->is_connecting()) ++num_half_open; else - ++num_connections; + ++num_complete_connections; } m_stats_logger @@ -1048,8 +1052,9 @@ namespace detail << m_stat.download_rate() << "\t" << downloading_torrents << "\t" << seeding_torrents << "\t" - << num_connections << "\t" + << num_complete_connections << "\t" << num_half_open << "\t" + << m_disk_thread.disk_allocations() << "\t" << std::endl; #endif @@ -2259,6 +2264,35 @@ namespace detail m_upnp.reset(); } + void session_impl::free_disk_buffer(char* buf) + { + m_disk_thread.free_buffer(buf); + } + + std::pair session_impl::allocate_buffer(int size) + { + int num_buffers = (size + send_buffer_size - 1) / send_buffer_size; +#ifdef TORRENT_STATS + m_buffer_allocations += num_buffers; + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif + return std::make_pair((char*)m_send_buffers.ordered_malloc(num_buffers) + , num_buffers * send_buffer_size); + } + + void session_impl::free_buffer(char* buf, int size) + { + assert(size % send_buffer_size == 0); + int num_buffers = size / send_buffer_size; +#ifdef TORRENT_STATS + m_buffer_allocations -= num_buffers; + assert(m_buffer_allocations >= 0); + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif + m_send_buffers.ordered_free(buf, num_buffers); + } #ifndef NDEBUG void session_impl::check_invariant() const diff --git a/src/storage.cpp b/src/storage.cpp index dbf6a9382..8c8d62a68 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -1065,6 +1065,11 @@ namespace libtorrent return m_storage->verify_resume_data(rd, error); } + void piece_manager::free_buffer(char* buf) + { + m_io_thread.free_buffer(buf); + } + void piece_manager::async_release_files( boost::function const& handler) { diff --git a/src/torrent.cpp b/src/torrent.cpp index d1b429b55..e9b2cd79e 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1464,9 +1464,6 @@ namespace libtorrent m_policy->connection_closed(*p); p->set_peer_info(0); m_connections.erase(i); -#ifndef NDEBUG - m_policy->check_invariant(); -#endif } catch (std::exception& e) { diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 24b301e92..bc09b4935 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -297,7 +297,7 @@ namespace libtorrent (*m_logger) << request << "\n"; #endif - send_buffer(request.c_str(), request.c_str() + request.size()); + send_buffer(request.c_str(), request.size()); } // -------------------------- diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 34c2b5fa3..29ece394d 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -34,12 +34,16 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "libtorrent/buffer.hpp" +#include "libtorrent/chained_buffer.hpp" #include "test.hpp" using libtorrent::buffer; +using libtorrent::chained_buffer; + /* template T const& min_(T const& x, T const& y) @@ -113,7 +117,9 @@ void test_speed() } */ -int test_main() +// -- test buffer -- + +void test_buffer() { char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; @@ -157,6 +163,160 @@ int test_main() buffer().swap(b); TEST_CHECK(b.capacity() == 0); +} + +// -- test chained buffer -- + +std::set buffer_list; + +void free_buffer(char* m) +{ + std::set::iterator i = buffer_list.find(m); + TEST_CHECK(i != buffer_list.end()); + + buffer_list.erase(i); + std::free(m); +} + +char* allocate_buffer(int size) +{ + char* mem = (char*)std::malloc(size); + buffer_list.insert(mem); + return mem; +} + +template +int copy_buffers(T const& b, char* target) +{ + int copied = 0; + for (typename T::const_iterator i = b.begin() + , end(b.end()); i != end; ++i) + { + memcpy(target, asio::buffer_cast(*i), asio::buffer_size(*i)); + target += asio::buffer_size(*i); + copied += asio::buffer_size(*i); + } + return copied; +} + +bool compare_chained_buffer(chained_buffer& b, char const* mem, int size) +{ + std::vector flat(size); + std::list const& iovec2 = b.build_iovec(size); + int copied = copy_buffers(iovec2, &flat[0]); + TEST_CHECK(copied == size); + return std::memcmp(&flat[0], mem, size) == 0; +} + +void test_chained_buffer() +{ + char data[] = "foobar"; + { + chained_buffer b; + + TEST_CHECK(b.empty()); + TEST_CHECK(b.capacity() == 0); + TEST_CHECK(b.size() == 0); + TEST_CHECK(b.space_in_last_buffer() == 0); + TEST_CHECK(buffer_list.empty()); + + char* b1 = allocate_buffer(512); + std::memcpy(b1, data, 6); + b.append_buffer(b1, 512, 6, (void(*)(char*))&free_buffer); + TEST_CHECK(buffer_list.size() == 1); + + TEST_CHECK(b.capacity() == 512); + TEST_CHECK(b.size() == 6); + TEST_CHECK(!b.empty()); + TEST_CHECK(b.space_in_last_buffer() == 512 - 6); + + b.pop_front(3); + + TEST_CHECK(b.capacity() == 512); + TEST_CHECK(b.size() == 3); + TEST_CHECK(!b.empty()); + TEST_CHECK(b.space_in_last_buffer() == 512 - 6); + + bool ret = b.append(data, 6); + + TEST_CHECK(ret == true); + TEST_CHECK(b.capacity() == 512); + TEST_CHECK(b.size() == 9); + TEST_CHECK(!b.empty()); + TEST_CHECK(b.space_in_last_buffer() == 512 - 12); + + ret = b.append(data, 1024); + + TEST_CHECK(ret == false); + + char* b2 = allocate_buffer(512); + std::memcpy(b2, data, 6); + b.append_buffer(b2, 512, 6, (void(*)(char*))&free_buffer); + TEST_CHECK(buffer_list.size() == 2); + + char* b3 = allocate_buffer(512); + std::memcpy(b3, data, 6); + b.append_buffer(b3, 512, 6, (void(*)(char*))&free_buffer); + TEST_CHECK(buffer_list.size() == 3); + + TEST_CHECK(b.capacity() == 512 * 3); + TEST_CHECK(b.size() == 21); + TEST_CHECK(!b.empty()); + TEST_CHECK(b.space_in_last_buffer() == 512 - 6); + + TEST_CHECK(compare_chained_buffer(b, "barfoobar", 9)); + + for (int i = 1; i < 21; ++i) + TEST_CHECK(compare_chained_buffer(b, "barfoobarfoobarfoobar", i)); + + b.pop_front(5 + 6); + + TEST_CHECK(buffer_list.size() == 2); + TEST_CHECK(b.capacity() == 512 * 2); + TEST_CHECK(b.size() == 10); + TEST_CHECK(!b.empty()); + TEST_CHECK(b.space_in_last_buffer() == 512 - 6); + + char const* str = "obarfooba"; + TEST_CHECK(compare_chained_buffer(b, str, 9)); + + for (int i = 0; i < 9; ++i) + { + b.pop_front(1); + ++str; + TEST_CHECK(compare_chained_buffer(b, str, 8 - i)); + TEST_CHECK(b.size() == 9 - i); + } + + char* b4 = allocate_buffer(20); + std::memcpy(b4, data, 6); + std::memcpy(b4 + 6, data, 6); + b.append_buffer(b4, 20, 12, (void(*)(char*))&free_buffer); + TEST_CHECK(b.space_in_last_buffer() == 8); + + ret = b.append(data, 6); + TEST_CHECK(ret == true); + TEST_CHECK(b.space_in_last_buffer() == 2); + std::cout << b.space_in_last_buffer() << std::endl; + ret = b.append(data, 2); + TEST_CHECK(ret == true); + TEST_CHECK(b.space_in_last_buffer() == 0); + std::cout << b.space_in_last_buffer() << std::endl; + + char* b5 = allocate_buffer(20); + std::memcpy(b4, data, 6); + b.append_buffer(b5, 20, 6, (void(*)(char*))&free_buffer); + + b.pop_front(22); + TEST_CHECK(b.size() == 5); + } + TEST_CHECK(buffer_list.empty()); +} + +int test_main() +{ + test_buffer(); + test_chained_buffer(); return 0; }