<commit_before>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fix for rate limited http_connection<commit_after>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_limiter_timer_active = false;
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fixes for the GET request and passes that data through the data pointer in bottled mode too<commit_after>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"\r\nConnection: close\r\n"
		"\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		char const* data = 0;
		std::size_t size = 0;
		if (m_bottled)
		{
			data = m_parser.get_body().begin;
			size = m_parser.get_body().left();
		}
		m_handler(asio::error_code(), m_parser, data, size);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left());
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string auth;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"\r\nConnection: close\r\n";
	if (!auth.empty())
		headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
	headers << "\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_connect_timeout()
{
	if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
	m_connection_ticket = -1;

	if (m_bottled && m_called) return;
	m_called = true;
	m_handler(asio::error::timed_out, m_parser, 0, 0);
	close();
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket);
	c->m_connection_ticket = -1;

	if (e == asio::error::operation_aborted) return;

	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();

	if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
	m_connection_ticket = -1;
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
		, bind(&http_connection::on_connect_timeout, shared_from_this())
		, m_timeout);
}

void http_connection::connect(int ticket, tcp::endpoint target_address)
{
	m_connection_ticket = ticket;
	m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
			, bind(&http_connection::on_connect_timeout, shared_from_this())
			, m_timeout);
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		char const* data = 0;
		std::size_t size = 0;
		if (m_bottled)
		{
			data = m_parser.get_body().begin;
			size = m_parser.get_body().left();
		}
		m_handler(asio::error_code(), m_parser, data, size);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			close();

			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left());
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fixed semantics in http_connection<commit_after>/*

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.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string auth;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"\r\nConnection: close\r\n";
	if (!auth.empty())
		headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
	headers << "\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_connect_timeout()
{
	if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
	m_connection_ticket = -1;

	if (m_bottled && m_called) return;
	m_called = true;
	m_handler(asio::error::timed_out, m_parser, 0, 0);
	close();
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket);
	c->m_connection_ticket = -1;

	if (e == asio::error::operation_aborted) return;

	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();

	if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
	m_connection_ticket = -1;
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
		, bind(&http_connection::on_connect_timeout, shared_from_this())
		, m_timeout);
}

void http_connection::connect(int ticket, tcp::endpoint target_address)
{
	m_connection_ticket = ticket;
	m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
			, bind(&http_connection::on_connect_timeout, shared_from_this())
			, m_timeout);
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		char const* data = 0;
		std::size_t size = 0;
		if (m_bottled)
		{
			data = m_parser.get_body().begin;
			size = m_parser.get_body().left();
		}
		m_handler(e, m_parser, data, size);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			close();

			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left());
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>#pragma once

#include "cell_id.hpp"

#include "../coding/write_to_sink.hpp"
#include "../coding/varint.hpp"

#include "../geometry/point2d.hpp"

namespace feature
{
  namespace pts
  {
    inline int64_t FromPoint(m2::PointD const & p)
    {
      return PointToInt64(p.x, p.y);
    }

    inline m2::PointD ToPoint(int64_t i)
    {
      CoordPointT const pt = Int64ToPoint(i);
      return m2::PointD(pt.first, pt.second);
    }
  }

  namespace detail
  {
    inline void TransformPoints(vector<m2::PointD> const & points, vector<int64_t> & cells)
    {
      cells.reserve(points.size());
      transform(points.begin(), points.end(), back_inserter(cells), &pts::FromPoint);
    }

    template <class TSink>
    void WriteCells(vector<int64_t> & cells, TSink & sink)
    {
      vector<char> buffer;
      MemWriter<vector<char> > writer(buffer);

      for (size_t i = 0; i < cells.size(); ++i)
        WriteVarInt(writer, i == 0 ? cells[0] : cells[i] - cells[i-1]);

      uint32_t const count = static_cast<uint32_t>(buffer.size());
      WriteVarUint(sink, count);
      sink.Write(&buffer[0], count);
    }

    class points_emitter
    {
      vector<m2::PointD> & m_points;
      int64_t m_id;

    public:
      points_emitter(vector<m2::PointD> & points, uint32_t count)
        : m_points(points), m_id(0)
      {
        m_points.reserve(count / 2);
      }
      void operator() (int64_t id)
      {
        m_points.push_back(pts::ToPoint(m_id += id));
      }
    };

    template <class TSource>
    void ReadPoints(vector<m2::PointD> & points, TSource & src)
    {
      uint32_t const count = ReadVarUint<uint32_t>(src);
      vector<char> buffer(count);
      char * p = &buffer[0];
      src.Read(p, count);

      ReadVarInt64Array(p, p + count, points_emitter(points, count));
    }
  }

  template <class TSink>
  void SavePoints(vector<m2::PointD> const & points, TSink & sink)
  {
    ASSERT_GREATER(points.size(), 1, ());

    vector<int64_t> cells;
    detail::TransformPoints(points, cells);

    detail::WriteCells(cells, sink);
  }

  template <class TSource>
  void LoadPoints(vector<m2::PointD> & points, TSource & src)
  {
    detail::ReadPoints(points, src);
  }

  template <class TSink>
  void SaveTriangles(vector<m2::PointD> const & triangles, TSink & sink)
  {
    uint32_t const count = triangles.size();
    ASSERT_GREATER(count, 0, ());
    ASSERT_EQUAL(count % 3, 0, (count));

    vector<int64_t> cells;
    detail::TransformPoints(triangles, cells);

    detail::WriteCells(cells, sink);
  }

  template <class TSource>
  void LoadTriangles(vector<m2::PointD> & points, TSource & src)
  {
    detail::ReadPoints(points, src);
  }

  static int g_arrScales[] = { 5, 10, 14, 17 };  // 17 = scales::GetUpperScale()

  inline string GetTagForScale(char const * prefix, int scale)
  {
    string str;
    str.reserve(strlen(prefix) + 1);
    str = prefix;

    static char arrChar[] = { '0', '1', '2', '3' };
    STATIC_ASSERT ( ARRAY_SIZE(arrChar) == ARRAY_SIZE(g_arrScales) );

    for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i)
      if (scale <= feature::g_arrScales[i])
      {
        str += arrChar[i];
        break;
      }

    return str;
  }
}
<commit_msg>Add some asserts.<commit_after>#pragma once

#include "cell_id.hpp"

#include "../coding/write_to_sink.hpp"
#include "../coding/varint.hpp"

#include "../geometry/point2d.hpp"

namespace feature
{
  namespace pts
  {
    inline int64_t FromPoint(m2::PointD const & p)
    {
      return PointToInt64(p.x, p.y);
    }

    inline m2::PointD ToPoint(int64_t i)
    {
      CoordPointT const pt = Int64ToPoint(i);
      return m2::PointD(pt.first, pt.second);
    }
  }

  namespace detail
  {
    inline void TransformPoints(vector<m2::PointD> const & points, vector<int64_t> & cells)
    {
      cells.reserve(points.size());
      transform(points.begin(), points.end(), back_inserter(cells), &pts::FromPoint);
    }

    template <class TSink>
    void WriteCells(vector<int64_t> & cells, TSink & sink)
    {
      vector<char> buffer;
      MemWriter<vector<char> > writer(buffer);

      for (size_t i = 0; i < cells.size(); ++i)
        WriteVarInt(writer, i == 0 ? cells[0] : cells[i] - cells[i-1]);

      uint32_t const count = static_cast<uint32_t>(buffer.size());
      WriteVarUint(sink, count);
      sink.Write(&buffer[0], count);
    }

    class points_emitter
    {
      vector<m2::PointD> & m_points;
      int64_t m_id;

    public:
      points_emitter(vector<m2::PointD> & points, uint32_t count)
        : m_points(points), m_id(0)
      {
        m_points.reserve(count / 2);
      }
      void operator() (int64_t id)
      {
        m_points.push_back(pts::ToPoint(m_id += id));
      }
    };

    template <class TSource>
    void ReadPoints(vector<m2::PointD> & points, TSource & src)
    {
      uint32_t const count = ReadVarUint<uint32_t>(src);
      vector<char> buffer(count);
      char * p = &buffer[0];
      src.Read(p, count);

      ReadVarInt64Array(p, p + count, points_emitter(points, count));
    }
  }

  template <class TSink>
  void SavePoints(vector<m2::PointD> const & points, TSink & sink)
  {
    ASSERT_GREATER(points.size(), 1, ());

    vector<int64_t> cells;
    detail::TransformPoints(points, cells);

    detail::WriteCells(cells, sink);
  }

  template <class TSource>
  void LoadPoints(vector<m2::PointD> & points, TSource & src)
  {
    detail::ReadPoints(points, src);

    ASSERT_GREATER ( points.size(), 1, () );
  }

  template <class TSink>
  void SaveTriangles(vector<m2::PointD> const & triangles, TSink & sink)
  {
    uint32_t const count = triangles.size();
    ASSERT_GREATER(count, 0, ());
    ASSERT_EQUAL(count % 3, 0, (count));

    vector<int64_t> cells;
    detail::TransformPoints(triangles, cells);

    detail::WriteCells(cells, sink);
  }

  template <class TSource>
  void LoadTriangles(vector<m2::PointD> & points, TSource & src)
  {
    detail::ReadPoints(points, src);

    uint32_t const count = points.size();
    ASSERT_GREATER(count, 0, ());
    ASSERT_EQUAL(count % 3, 0, (count));
  }

  static int g_arrScales[] = { 5, 10, 14, 17 };  // 17 = scales::GetUpperScale()

  inline string GetTagForScale(char const * prefix, int scale)
  {
    string str;
    str.reserve(strlen(prefix) + 1);
    str = prefix;

    static char arrChar[] = { '0', '1', '2', '3' };
    STATIC_ASSERT ( ARRAY_SIZE(arrChar) == ARRAY_SIZE(g_arrScales) );

    for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i)
      if (scale <= feature::g_arrScales[i])
      {
        str += arrChar[i];
        break;
      }

    return str;
  }
}
<|endoftext|>


<commit_before>// $Id: timer.C,v 1.3 2000/01/10 15:51:16 oliver Exp $

#include <BALL/SYSTEM/timer.h>

#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>

using std::cout;
using std::endl;
using std::ostream;

namespace BALL
{

	long Timer::cpu_speed_ = 0L;

	Timer::Timer()
	{
		if (cpu_speed_ == 0L)
		{
			cpu_speed_ = sysconf(_SC_CLK_TCK);
		}

		clear();
	}

	Timer::Timer(Timer& timer)
	{
		*this = timer;
	}

	Timer::~Timer()
	{
	}

	void Timer::clear()
	{
		is_running_ = false;
		current_secs_ =
		current_usecs_ = 0L;
		current_user_time_ =
		current_system_time_ = (clock_t)0;
	}

	bool Timer::start()
	{
		if (is_running_ == true)
		{ /* tried to start a running timer */
			return false;
		}

		struct tms tms_buffer;
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;

		gettimeofday(&timeval_buffer, &timezone_buffer);
		times(&tms_buffer);

		last_secs_ = timeval_buffer.tv_sec;
		last_usecs_ = timeval_buffer.tv_usec;
		last_user_time_ = tms_buffer.tms_utime;
		last_system_time_ = tms_buffer.tms_stime;
		is_running_ = true;

		return true;
	}

	bool Timer::stop()
	{
		if (is_running_ == false)
		{ /* tried to stop a stopped timer */
			return false;
		}

		struct tms tms_buffer;
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;

		gettimeofday(&timeval_buffer, &timezone_buffer);
		times(&tms_buffer);

		current_secs_ += timeval_buffer.tv_sec - last_secs_;
		current_usecs_ += timeval_buffer.tv_usec - last_usecs_;
		current_user_time_ += tms_buffer.tms_utime - last_user_time_;
		current_system_time_ += tms_buffer.tms_stime - last_system_time_;
		is_running_ = false;

		return true;
	}

	void Timer::reset()
	{
		if (is_running_ == false)
		{
			clear();
		} else {
			stop();
			clear();
			start();
		}
	}

	/************************************************************************/
	/*																																			*/
	/*     getClockTime returns the current amount of real (clock) time			*/
	/*  accumulated by this timer.  If the timer is stopped, this is just		*/
	/*  the total accumulated time.  If the timer is running, this is the		*/
	/*  accumulated time + the time since the timer was last started.				*/
	/*																																			*/
	/************************************************************************/
	float Timer::getClockTime() const
	{
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;
		long elapsed_seconds;
		long micro_useconds;

		if (is_running_ == false)
		{ /* timer is currently off, so just return accumulated time */
			elapsed_seconds = current_secs_;
			micro_useconds = current_usecs_;
		} else {
			/* timer is currently running, so add the elapsed time since */
			/* the timer was last started to the accumulated time        */

			gettimeofday(&timeval_buffer, &timezone_buffer);

			elapsed_seconds = current_secs_ + timeval_buffer.tv_sec - last_secs_;
			micro_useconds = current_usecs_ + timeval_buffer.tv_usec - last_usecs_;
		}

		/* Adjust for the fact that the useconds may be negative. */
		/* If they are, take away 1 second and add 1 million      */
		/* microseconds until they are positive.                  */
		while (micro_useconds < 0L)
		{
			micro_useconds += 1000000L;
			elapsed_seconds--;
		}

		/* convert into floating point number of seconds */
		return (float)((float)elapsed_seconds + (float) micro_useconds / 1000000.0);
	}

	/************************************************************************/
	/*																																			*/
	/*      getUserTime reports the current amount of user cpu time         */
	/*   accumulated by this Timer.  If the timer is currently off,					*/
	/*   this is just the accumulated time.  If the Timer is running, this	*/
	/*   is the accumulated time plust the time since the timer was last    */
	/*   started.																														*/
	/*																																			*/
	/************************************************************************/
	float Timer::getUserTime() const
	{
		float temp_value;
		struct tms tms_buffer;

		if (is_running_ == false)
		{ /* timer is off, just return accumulated time */
			temp_value = (float)current_user_time_;
		}	else {
			/* timer is on, add current running time to accumulated time */
			times(&tms_buffer);

			temp_value = (float)(current_user_time_ + tms_buffer.tms_utime - last_user_time_);
		}

		/* convert from clock ticks to seconds using the */
		/* cpu-speed value obtained by the constructor   */
		return (float)(temp_value / (float)cpu_speed_);
	}

	/************************************************************************/
	/*																																			*/
	/*      system_time reports the current amount of system cpu time       */
	/*   accumulated by this Timer.  If the timer is currently off,					*/
	/*   this is just the accumulated time.  If the Timer is running, this	*/
	/*   is the accumulated time plus  the time since the timer was last    */
	/*   started.																														*/
	/*																																			*/
	/************************************************************************/
	float Timer::getSystemTime() const
	{
		float temp_value;
		struct tms tms_buffer;

		if (is_running_ == false)
		{ /* timer is off, just return accumulated time */
			temp_value = (float)current_system_time_;
		} else {
			/* timer is on, return accumulated plus current */
			times(&tms_buffer);

			temp_value = (float)(current_system_time_ + tms_buffer.tms_stime - last_system_time_);
		}

		/* convert from clock ticks to seconds using the */
		/* cpu-speed value obtained by the constructor   */
		return (temp_value / (float)cpu_speed_);
	}

	Timer& Timer::operator = (const Timer& timer)
	{
		if (this == &timer)
		{
			return *this;
		}

		is_running_ = timer.is_running_;
		last_secs_ = timer.last_secs_;
		last_usecs_ = timer.last_usecs_;
		last_user_time_ = timer.last_user_time_;
		last_system_time_ = timer.last_system_time_;
		current_secs_ = timer.current_secs_;
		current_usecs_ = timer.current_usecs_;
		current_user_time_ = timer.current_user_time_;
		current_system_time_ = timer.current_system_time_;

		return *this;
	}

	bool Timer::operator == (const Timer& timer) const
	{
		return (bool)(last_secs_ == timer.last_secs_
									&& last_usecs_ == timer.last_usecs_
									&& last_user_time_ == timer.last_user_time_
									&& last_system_time_ == timer.last_system_time_
									&& current_secs_ == timer.current_secs_
									&& current_usecs_ == timer.current_usecs_
									&& current_user_time_ == timer.current_user_time_
									&& current_system_time_ == timer.current_system_time_);
	}

	bool Timer::operator < (const Timer& timer) const
	{
		return (bool)(current_secs_ + last_secs_ < timer.current_secs_ + timer.last_secs_
									&& current_usecs_ + last_usecs_ < timer.current_usecs_ + timer.last_usecs_
									&& current_user_time_ + last_user_time_ < timer.current_user_time_ + timer.last_user_time_
									&& current_system_time_ + last_system_time_ < timer.current_system_time_ + timer.last_system_time_);
	}

	void Timer::dump(ostream& s, Size depth) const
	{
		BALL_DUMP_STREAM_PREFIX(s);

		BALL_DUMP_DEPTH(s, depth);
		BALL_DUMP_CLASS_HEADER(s, Timer, this);

		BALL_DUMP_DEPTH(s, depth);
		s << "CPU speed: " << cpu_speed_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "is running: " << (is_running_ ? "true" : "false") << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last clock seconds: " << last_secs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last user seconds: " << last_usecs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last user time: " << last_user_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last system time: " << last_system_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current clock seconds: " << current_secs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current user seconds: " << current_usecs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current user time: " << current_user_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current system time: " << current_system_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective clock time: " << getClockTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective user time: " << getUserTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective system time: " << getSystemTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective CPU time: " << getCPUTime() << endl;

		BALL_DUMP_STREAM_SUFFIX(s);
	}

#	ifdef BALL_NO_INLINE_FUNCTIONS
#		include <BALL/SYSTEM/timer.iC>
#	endif

} // namespace BALL
<commit_msg>added commentary<commit_after>// $Id: timer.C,v 1.4 2000/06/25 12:51:48 amoll Exp $

#include <BALL/SYSTEM/timer.h>

#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>

using std::cout;
using std::endl;
using std::ostream;

namespace BALL
{

	long Timer::cpu_speed_ = 0L;

	Timer::Timer()
	{
		if (cpu_speed_ == 0L)
		{
			cpu_speed_ = sysconf(_SC_CLK_TCK);
		}

		clear();
	}

	Timer::Timer(Timer& timer)
	{
		*this = timer;
	}

	Timer::~Timer()
	{
	}

	void Timer::clear()
	{
		is_running_ = false;
		current_secs_ =
		current_usecs_ = 0L;
		current_user_time_ =
		current_system_time_ = (clock_t)0;
	}

	bool Timer::start()
	{
		if (is_running_ == true)
		{ /* tried to start a running timer */
			return false;
		}

		struct tms tms_buffer;
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;

		gettimeofday(&timeval_buffer, &timezone_buffer);
		times(&tms_buffer);

		last_secs_ = timeval_buffer.tv_sec;
		last_usecs_ = timeval_buffer.tv_usec;
		last_user_time_ = tms_buffer.tms_utime;
		last_system_time_ = tms_buffer.tms_stime;
		is_running_ = true;

		return true;
	}

	bool Timer::stop()
	{
		if (is_running_ == false)
		{ /* tried to stop a stopped timer */
			return false;
		}

		struct tms tms_buffer;
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;

		gettimeofday(&timeval_buffer, &timezone_buffer);
		times(&tms_buffer);

		current_secs_ += timeval_buffer.tv_sec - last_secs_;
		current_usecs_ += timeval_buffer.tv_usec - last_usecs_;
		current_user_time_ += tms_buffer.tms_utime - last_user_time_;
		current_system_time_ += tms_buffer.tms_stime - last_system_time_;
		is_running_ = false;

		return true;
	}

	void Timer::reset()
	{
		if (is_running_ == false)
		{
			clear();
		} else {
			stop();
			clear();
			start();
		}
	}

	/************************************************************************/
	/*																																			*/
	/*     getClockTime returns the current amount of real (clock) time			*/
	/*  accumulated by this timer.  If the timer is stopped, this is just		*/
	/*  the total accumulated time.  If the timer is running, this is the		*/
	/*  accumulated time + the time since the timer was last started.				*/
	/*																																			*/
	/************************************************************************/
	float Timer::getClockTime() const
	{
		struct timeval timeval_buffer;
		struct timezone timezone_buffer;
		long elapsed_seconds;
		long micro_useconds;

		if (is_running_ == false)
		{ /* timer is currently off, so just return accumulated time */
			elapsed_seconds = current_secs_;
			micro_useconds = current_usecs_;
		} else {
			/* timer is currently running, so add the elapsed time since */
			/* the timer was last started to the accumulated time        */

			gettimeofday(&timeval_buffer, &timezone_buffer);

			elapsed_seconds = current_secs_ + timeval_buffer.tv_sec - last_secs_;
			micro_useconds = current_usecs_ + timeval_buffer.tv_usec - last_usecs_;
		}

		/* Adjust for the fact that the useconds may be negative. */
		/* If they are, take away 1 second and add 1 million      */
		/* microseconds until they are positive.                  */
		while (micro_useconds < 0L)
		{
			micro_useconds += 1000000L;
			elapsed_seconds--;
		}

		/* convert into floating point number of seconds */
		return (float)((float)elapsed_seconds + (float) micro_useconds / 1000000.0);
	}

	/************************************************************************/
	/*																																			*/
	/*      getUserTime reports the current amount of user cpu time         */
	/*   accumulated by this Timer.  If the timer is currently off,					*/
	/*   this is just the accumulated time.  If the Timer is running, this	*/
	/*   is the accumulated time plust the time since the timer was last    */
	/*   started.																														*/
	/*																																			*/
	/************************************************************************/
	float Timer::getUserTime() const
	{
		float temp_value;
		struct tms tms_buffer;

		if (is_running_ == false)
		{ /* timer is off, just return accumulated time */
			temp_value = (float)current_user_time_;
		}	else {
			/* timer is on, add current running time to accumulated time */
			times(&tms_buffer);

			temp_value = (float)(current_user_time_ + tms_buffer.tms_utime - last_user_time_);
		}

		/* convert from clock ticks to seconds using the */
		/* cpu-speed value obtained by the constructor   */
		return (float)(temp_value / (float)cpu_speed_);
	}

	/************************************************************************/
	/*																																			*/
	/*      system_time reports the current amount of system cpu time       */
	/*   accumulated by this Timer.  If the timer is currently off,					*/
	/*   this is just the accumulated time.  If the Timer is running, this	*/
	/*   is the accumulated time plus  the time since the timer was last    */
	/*   started.																														*/
	/*																																			*/
	/************************************************************************/
	float Timer::getSystemTime() const
	{
		float temp_value;
		struct tms tms_buffer;

		if (is_running_ == false)
		{ /* timer is off, just return accumulated time */
			temp_value = (float)current_system_time_;
		} else {
			/* timer is on, return accumulated plus current */
			times(&tms_buffer);

			temp_value = (float)(current_system_time_ + tms_buffer.tms_stime - last_system_time_);
		}

		/* convert from clock ticks to seconds using the */
		/* cpu-speed value obtained by the constructor   */
		return (temp_value / (float)cpu_speed_);
	}

	Timer& Timer::operator = (const Timer& timer)
	{
		if (this == &timer)
		{
			return *this;
		}

		is_running_ = timer.is_running_;
		last_secs_ = timer.last_secs_;
		last_usecs_ = timer.last_usecs_;
		last_user_time_ = timer.last_user_time_;
		last_system_time_ = timer.last_system_time_;
		current_secs_ = timer.current_secs_;
		current_usecs_ = timer.current_usecs_;
		current_user_time_ = timer.current_user_time_;
		current_system_time_ = timer.current_system_time_;

		return *this;
	}

	bool Timer::operator == (const Timer& timer) const
	{
		return (bool)(last_secs_ == timer.last_secs_
									&& last_usecs_ == timer.last_usecs_
									&& last_user_time_ == timer.last_user_time_
									&& last_system_time_ == timer.last_system_time_
									&& current_secs_ == timer.current_secs_
									&& current_usecs_ == timer.current_usecs_
									&& current_user_time_ == timer.current_user_time_
									&& current_system_time_ == timer.current_system_time_);
	}

	void Timer::dump(ostream& s, Size depth) const
	{
		BALL_DUMP_STREAM_PREFIX(s);

		BALL_DUMP_DEPTH(s, depth);
		BALL_DUMP_CLASS_HEADER(s, Timer, this);

		BALL_DUMP_DEPTH(s, depth);
		s << "CPU speed: " << cpu_speed_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "is running: " << (is_running_ ? "true" : "false") << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last clock seconds: " << last_secs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last user seconds: " << last_usecs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last user time: " << last_user_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "last system time: " << last_system_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current clock seconds: " << current_secs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current user seconds: " << current_usecs_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current user time: " << current_user_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "current system time: " << current_system_time_ << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective clock time: " << getClockTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective user time: " << getUserTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective system time: " << getSystemTime() << endl;

		BALL_DUMP_DEPTH(s, depth);
		s << "effective CPU time: " << getCPUTime() << endl;

		BALL_DUMP_STREAM_SUFFIX(s);
	}

#	ifdef BALL_NO_INLINE_FUNCTIONS
#		include <BALL/SYSTEM/timer.iC>
#	endif

} // namespace BALL
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  testC3DFileAdapter.cpp                     *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "OpenSim/Common/C3DFileAdapter.h"
#include "OpenSim/Common/STOFileAdapter.h"
#include "OpenSim/Common/TRCFileAdapter.h"
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <thread>
#include <unordered_map>
#include <vector>

#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>
#include <OpenSim/Common/Stopwatch.h>

template<typename ETY = SimTK::Real>
void compare_tables(const OpenSim::TimeSeriesTable_<ETY>& table1,
            const OpenSim::TimeSeriesTable_<ETY>& table2,
            const double tolerance = SimTK::SignificantReal) {
    using namespace OpenSim;
    try {
        OPENSIM_THROW_IF(table1.getColumnLabels() != table2.getColumnLabels(),
                         Exception,
                         "Column labels are not the same for tables.");

        ASSERT_EQUAL( table1.getIndependentColumn(),
                      table2.getIndependentColumn(), tolerance,
                       __FILE__, __LINE__,
                         "Independent columns are not equivalent.");
    } catch (const OpenSim::KeyNotFound&) {}

    const auto& matrix1 = table1.getMatrix();
    const auto& matrix2 = table2.getMatrix();

    for(int r = 0; r < matrix1.nrow(); ++r)
        for(int c = 0; c < matrix1.ncol(); ++c) {
            auto elt1 = matrix1.getElt(r, c);
            auto elt2 = matrix2.getElt(r, c);

            ASSERT_EQUAL(elt1, elt2, tolerance, __FILE__, __LINE__,
                "Element at row, " + std::to_string(r) + " col, " +
                std::to_string(c) + " failed to have matching value.");
        }
}

template<typename ETY = SimTK::Real>
void downsample_table(OpenSim::TimeSeriesTable_<ETY>& table,
    const unsigned int increment) {
    for (size_t r = table.getNumRows() - 2; r > 0; --r) {
        if (r%increment)
            table.removeRowAtIndex(r);
    }
}

void test(const std::string filename) {
    using namespace OpenSim;
    using namespace std;

    // The walking C3D files included in this test should not take more
    // than 40ms on most hardware. We make the max time 100ms to account
    // for potentially slower CI machines.
    const long long MaximumLoadTimeInNs = SimTK::secToNs(0.100);

    Stopwatch watch;
    C3DFileAdapter c3dFileAdapter{};
    auto tables = c3dFileAdapter.read(filename);
    long long loadTime = watch.getElapsedTimeInNs();

    cout << "\tC3DFileAdapter '" << filename << "' loaded in "
        << watch.formatNs(loadTime) << endl;

/*  Disabled performance test because Travis CI is consistently unable to
    meet this timing requirement. Consider PR#2221 to address this issue
    longer term.
    #ifdef NDEBUG
    ASSERT(loadTime < MaximumLoadTimeInNs, __FILE__, __LINE__,
        "Unable to load '" + filename + "' within " +
        to_string(MaximumLoadTimeInNs) + "ns.");
    #endif
*/

    std::shared_ptr<TimeSeriesTableVec3> marker_table = c3dFileAdapter.getMarkersTable(tables);
    std::shared_ptr<TimeSeriesTableVec3> force_table = c3dFileAdapter.getForcesTable(tables);
    downsample_table(*marker_table, 10);
    downsample_table(*force_table, 100);

    size_t ext = filename.rfind(".");
    std::string base = filename.substr(0, ext);

    const std::string marker_file = base + "_markers.trc";
    const std::string forces_file = base + "_grfs.sto";

    ASSERT(marker_table->getNumRows() > 0, __FILE__, __LINE__,
        "Failed to read marker data from " + filename);

    marker_table->updTableMetaData().setValueForKey("Units",
                                                    std::string{"mm"});
    TRCFileAdapter trc_adapter{};
    watch.reset();
    trc_adapter.write(*marker_table, marker_file);
    cout << "\tWrote '" << marker_file << "' in "
        << watch.getElapsedTimeFormatted() << endl;

    ASSERT(force_table->getNumRows() > 0, __FILE__, __LINE__,
        "Failed to read forces data from " + filename);

    force_table->updTableMetaData().setValueForKey("Units",
                                                    std::string{"mm"});
    STOFileAdapter sto_adapter{};
    watch.reset();
    sto_adapter.write((force_table->flatten()), forces_file);
    cout << "\tWrote'" << forces_file << "' in "
        << watch.getElapsedTimeFormatted() << endl;

    // Verify that marker data was written out and can be read in
    watch.reset();
    TimeSeriesTable_<SimTK::Vec3> markers(marker_file);
    TimeSeriesTable_<SimTK::Vec3> std_markers("std_" + marker_file);
    cout << "\tRead'" << marker_file << "' and its standard in "
        << watch.getElapsedTimeFormatted() << endl;

    // Compare C3DFileAdapter read-in and written marker data
    compare_tables<SimTK::Vec3>(markers, *marker_table);
    // Compare C3DFileAdapter written marker data to standard
    // Note std exported from Mokka with only 5 decimal places
    compare_tables<SimTK::Vec3>(markers, std_markers, 1e-4);

    cout << "\tMarkers " << marker_file << " equivalent to standard." << endl;

    // Verify that grfs data was written out and can be read in
    TimeSeriesTable forces(forces_file);
    TimeSeriesTable std_forces("std_" + forces_file);
    // Compare C3DFileAdapter read-in and written forces data
    compare_tables<SimTK::Vec3>(forces.pack<SimTK::Vec3>(),
                                *force_table,
                                SimTK::SqrtEps);
    // Compare C3DFileAdapter written forces data to standard
    // Note std generated using MATLAB C3D processing scripts
    compare_tables(forces, std_forces, SimTK::SqrtEps);

    cout << "\tForces " << forces_file << " equivalent to standard." << endl;

    watch.reset();
    // Reread in C3D file with forces resolved to the COP
    auto c3dFileAdapter2 = C3DFileAdapter{};
    c3dFileAdapter2.setLocationForForceExpression(
            C3DFileAdapter::ForceLocation::CenterOfPressure);
    auto tables2 = c3dFileAdapter2.read(filename);

    loadTime = watch.getElapsedTimeInNs();
    cout << "\tC3DFileAdapter '" << filename << "' read with forces at COP in "
        << watch.formatNs(loadTime) << endl;
    // on ci-biulds will define SKIP_TIMING as it is unpredictably slow on some machines
    #if defined(NDEBUG) && !defined(SKIP_TIMING)
    ASSERT(loadTime < MaximumLoadTimeInNs, __FILE__, __LINE__,
        "Unable to load '" + filename + "' within " +
        to_string(MaximumLoadTimeInNs) + "ns.");
    #endif
    std::shared_ptr<TimeSeriesTableVec3> force_table_cop =
            c3dFileAdapter.getForcesTable(tables2);
    downsample_table(*force_table_cop, 100);

    sto_adapter.write(force_table_cop->flatten(), "cop_"+ forces_file);

    TimeSeriesTable std_forces_cop("std_cop_" + forces_file);
    // Compare C3DFileAdapter written forces data to standard
    // Note std generated using MATLAB C3D processing scripts
    compare_tables<SimTK::Vec3>(*force_table_cop,
                                std_forces_cop.pack<SimTK::Vec3>(),
                                SimTK::SqrtEps);

    cout << "\tcop_" << forces_file << " is equivalent to its standard."<< endl;
}

int main() {
    SimTK_START_TEST("testC3DFileAdapter");
        SimTK_SUBTEST1(test, "walking2.c3d");
        SimTK_SUBTEST1(test, "walking5.c3d");
    SimTK_END_TEST();
}
<commit_msg>Make C3DFileAdapter timing more lenient.<commit_after>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  testC3DFileAdapter.cpp                     *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "OpenSim/Common/C3DFileAdapter.h"
#include "OpenSim/Common/STOFileAdapter.h"
#include "OpenSim/Common/TRCFileAdapter.h"
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <thread>
#include <unordered_map>
#include <vector>

#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>
#include <OpenSim/Common/Stopwatch.h>

template<typename ETY = SimTK::Real>
void compare_tables(const OpenSim::TimeSeriesTable_<ETY>& table1,
            const OpenSim::TimeSeriesTable_<ETY>& table2,
            const double tolerance = SimTK::SignificantReal) {
    using namespace OpenSim;
    try {
        OPENSIM_THROW_IF(table1.getColumnLabels() != table2.getColumnLabels(),
                         Exception,
                         "Column labels are not the same for tables.");

        ASSERT_EQUAL( table1.getIndependentColumn(),
                      table2.getIndependentColumn(), tolerance,
                       __FILE__, __LINE__,
                         "Independent columns are not equivalent.");
    } catch (const OpenSim::KeyNotFound&) {}

    const auto& matrix1 = table1.getMatrix();
    const auto& matrix2 = table2.getMatrix();

    for(int r = 0; r < matrix1.nrow(); ++r)
        for(int c = 0; c < matrix1.ncol(); ++c) {
            auto elt1 = matrix1.getElt(r, c);
            auto elt2 = matrix2.getElt(r, c);

            ASSERT_EQUAL(elt1, elt2, tolerance, __FILE__, __LINE__,
                "Element at row, " + std::to_string(r) + " col, " +
                std::to_string(c) + " failed to have matching value.");
        }
}

template<typename ETY = SimTK::Real>
void downsample_table(OpenSim::TimeSeriesTable_<ETY>& table,
    const unsigned int increment) {
    for (size_t r = table.getNumRows() - 2; r > 0; --r) {
        if (r%increment)
            table.removeRowAtIndex(r);
    }
}

void test(const std::string filename) {
    using namespace OpenSim;
    using namespace std;

    // The walking C3D files included in this test should not take more
    // than 40ms on most hardware. We make the max time 200ms to account
    // for potentially slower CI machines.
    const long long MaximumLoadTimeInNs = SimTK::secToNs(0.200);

    Stopwatch watch;
    C3DFileAdapter c3dFileAdapter{};
    auto tables = c3dFileAdapter.read(filename);
    long long loadTime = watch.getElapsedTimeInNs();

    cout << "\tC3DFileAdapter '" << filename << "' loaded in "
        << watch.formatNs(loadTime) << endl;

/*  Disabled performance test because Travis CI is consistently unable to
    meet this timing requirement. Consider PR#2221 to address this issue
    longer term.
    #ifdef NDEBUG
    ASSERT(loadTime < MaximumLoadTimeInNs, __FILE__, __LINE__,
        "Unable to load '" + filename + "' within " +
        to_string(MaximumLoadTimeInNs) + "ns.");
    #endif
*/

    std::shared_ptr<TimeSeriesTableVec3> marker_table = c3dFileAdapter.getMarkersTable(tables);
    std::shared_ptr<TimeSeriesTableVec3> force_table = c3dFileAdapter.getForcesTable(tables);
    downsample_table(*marker_table, 10);
    downsample_table(*force_table, 100);

    size_t ext = filename.rfind(".");
    std::string base = filename.substr(0, ext);

    const std::string marker_file = base + "_markers.trc";
    const std::string forces_file = base + "_grfs.sto";

    ASSERT(marker_table->getNumRows() > 0, __FILE__, __LINE__,
        "Failed to read marker data from " + filename);

    marker_table->updTableMetaData().setValueForKey("Units",
                                                    std::string{"mm"});
    TRCFileAdapter trc_adapter{};
    watch.reset();
    trc_adapter.write(*marker_table, marker_file);
    cout << "\tWrote '" << marker_file << "' in "
        << watch.getElapsedTimeFormatted() << endl;

    ASSERT(force_table->getNumRows() > 0, __FILE__, __LINE__,
        "Failed to read forces data from " + filename);

    force_table->updTableMetaData().setValueForKey("Units",
                                                    std::string{"mm"});
    STOFileAdapter sto_adapter{};
    watch.reset();
    sto_adapter.write((force_table->flatten()), forces_file);
    cout << "\tWrote'" << forces_file << "' in "
        << watch.getElapsedTimeFormatted() << endl;

    // Verify that marker data was written out and can be read in
    watch.reset();
    TimeSeriesTable_<SimTK::Vec3> markers(marker_file);
    TimeSeriesTable_<SimTK::Vec3> std_markers("std_" + marker_file);
    cout << "\tRead'" << marker_file << "' and its standard in "
        << watch.getElapsedTimeFormatted() << endl;

    // Compare C3DFileAdapter read-in and written marker data
    compare_tables<SimTK::Vec3>(markers, *marker_table);
    // Compare C3DFileAdapter written marker data to standard
    // Note std exported from Mokka with only 5 decimal places
    compare_tables<SimTK::Vec3>(markers, std_markers, 1e-4);

    cout << "\tMarkers " << marker_file << " equivalent to standard." << endl;

    // Verify that grfs data was written out and can be read in
    TimeSeriesTable forces(forces_file);
    TimeSeriesTable std_forces("std_" + forces_file);
    // Compare C3DFileAdapter read-in and written forces data
    compare_tables<SimTK::Vec3>(forces.pack<SimTK::Vec3>(),
                                *force_table,
                                SimTK::SqrtEps);
    // Compare C3DFileAdapter written forces data to standard
    // Note std generated using MATLAB C3D processing scripts
    compare_tables(forces, std_forces, SimTK::SqrtEps);

    cout << "\tForces " << forces_file << " equivalent to standard." << endl;

    watch.reset();
    // Reread in C3D file with forces resolved to the COP
    auto c3dFileAdapter2 = C3DFileAdapter{};
    c3dFileAdapter2.setLocationForForceExpression(
            C3DFileAdapter::ForceLocation::CenterOfPressure);
    auto tables2 = c3dFileAdapter2.read(filename);

    loadTime = watch.getElapsedTimeInNs();
    cout << "\tC3DFileAdapter '" << filename << "' read with forces at COP in "
        << watch.formatNs(loadTime) << endl;
    // on ci-biulds will define SKIP_TIMING as it is unpredictably slow on some machines
    #if defined(NDEBUG) && !defined(SKIP_TIMING)
    ASSERT(loadTime < MaximumLoadTimeInNs, __FILE__, __LINE__,
        "Unable to load '" + filename + "' within " +
        to_string(MaximumLoadTimeInNs) + "ns.");
    #endif
    std::shared_ptr<TimeSeriesTableVec3> force_table_cop =
            c3dFileAdapter.getForcesTable(tables2);
    downsample_table(*force_table_cop, 100);

    sto_adapter.write(force_table_cop->flatten(), "cop_"+ forces_file);

    TimeSeriesTable std_forces_cop("std_cop_" + forces_file);
    // Compare C3DFileAdapter written forces data to standard
    // Note std generated using MATLAB C3D processing scripts
    compare_tables<SimTK::Vec3>(*force_table_cop,
                                std_forces_cop.pack<SimTK::Vec3>(),
                                SimTK::SqrtEps);

    cout << "\tcop_" << forces_file << " is equivalent to its standard."<< endl;
}

int main() {
    SimTK_START_TEST("testC3DFileAdapter");
        SimTK_SUBTEST1(test, "walking2.c3d");
        SimTK_SUBTEST1(test, "walking5.c3d");
    SimTK_END_TEST();
}
<|endoftext|>


<commit_before>// $Header: /raid/cvs-server/REPOSITORY/software/MOOS/interface/general/iGPS/iGPSMain.cpp,v 5.1 2005/04/27 20:41:40 anrp Exp $
// copyright (2001-2003) Massachusetts Institute of Technology (pnewman et al.)

#include "GPS_MB1.h"

int main(int argc , char * argv[])
{
	const char * sMissionFile = "Mission.moos";

	if (argc > 1) {
		sMissionFile = argv[1];
	}

	CGPS_MB1 GPSInstrument;

	GPSInstrument.Run("iGPS_MB1", sMissionFile);

	return 0;
}
<commit_msg>Removed unneeded main for iSonar<commit_after><|endoftext|>