text:
#pragma once

#include <nano/lib/config.hpp>
#include <nano/lib/memory.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/versioning.hpp>

#include <boost/endian/conversion.hpp>
#include <boost/polymorphic_cast.hpp>

#include <stack>

namespace nano
{
// Generic container to be used when templated db types cannot
class DB_val
{
public:
	DB_val () = default;
	DB_val (size_t size_a, void * data_a) :
	size (size_a),
	data (data_a)
	{
	}

	size_t size;
	void * data;
};

/**
 * Encapsulates database specific container and provides uint256_union conversion of the data.
 */
template <typename Val>
class db_val
{
public:
	db_val (nano::epoch epoch_a = nano::epoch::unspecified) :
	value ({ 0, nullptr }),
	epoch (epoch_a)
	{
	}

	db_val (Val const & value_a, nano::epoch epoch_a = nano::epoch::unspecified) :
	value (value_a),
	epoch (epoch_a)
	{
	}

	db_val (DB_val const & value_a, nano::epoch epoch_a = nano::epoch::unspecified);

	db_val (size_t size_a, void * data_a) :
	value ({ size_a, data_a })
	{
	}

	db_val (nano::uint128_union const & val_a) :
	db_val (sizeof (val_a), const_cast<nano::uint128_union *> (&val_a))
	{
	}

	db_val (nano::uint256_union const & val_a) :
	db_val (sizeof (val_a), const_cast<nano::uint256_union *> (&val_a))
	{
	}

	db_val (nano::account_info const & val_a) :
	db_val (val_a.db_size (), const_cast<nano::account_info *> (&val_a))
	{
	}

	db_val (nano::account_info_v13 const & val_a) :
	db_val (val_a.db_size (), const_cast<nano::account_info_v13 *> (&val_a))
	{
	}

	db_val (nano::pending_info const & val_a) :
	db_val (sizeof (val_a.source) + sizeof (val_a.amount), const_cast<nano::pending_info *> (&val_a))
	{
		static_assert (std::is_standard_layout<nano::pending_info>::value, "Standard layout is required");
	}

	db_val (nano::pending_key const & val_a) :
	db_val (sizeof (val_a), const_cast<nano::pending_key *> (&val_a))
	{
		static_assert (std::is_standard_layout<nano::pending_key>::value, "Standard layout is required");
	}

	db_val (nano::unchecked_info const & val_a) :
	buffer (std::make_shared<std::vector<uint8_t>> ())
	{
		{
			nano::vectorstream stream (*buffer);
			val_a.serialize (stream);
		}
		value = { buffer->size (), const_cast<uint8_t *> (buffer->data ()) };
	}

	db_val (nano::block_info const & val_a) :
	db_val (sizeof (val_a), const_cast<nano::block_info *> (&val_a))
	{
		static_assert (std::is_standard_layout<nano::block_info>::value, "Standard layout is required");
	}

	db_val (nano::endpoint_key const & val_a) :
	db_val (sizeof (val_a), const_cast<nano::endpoint_key *> (&val_a))
	{
		static_assert (std::is_standard_layout<nano::endpoint_key>::value, "Standard layout is required");
	}

	db_val (std::shared_ptr<nano::block> const & val_a) :
	buffer (std::make_shared<std::vector<uint8_t>> ())
	{
		{
			nano::vectorstream stream (*buffer);
			nano::serialize_block (stream, *val_a);
		}
		value = { buffer->size (), const_cast<uint8_t *> (buffer->data ()) };
	}

	db_val (uint64_t val_a) :
	buffer (std::make_shared<std::vector<uint8_t>> ())
	{
		{
			boost::endian::native_to_big_inplace (val_a);
			nano::vectorstream stream (*buffer);
			nano::write (stream, val_a);
		}
		value = { buffer->size (), const_cast<uint8_t *> (buffer->data ()) };
	}

	explicit operator nano::account_info () const
	{
		nano::account_info result;
		result.epoch = epoch;
		assert (size () == result.db_size ());
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + result.db_size (), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::account_info_v13 () const
	{
		nano::account_info_v13 result;
		result.epoch = epoch;
		assert (size () == result.db_size ());
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + result.db_size (), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::block_info () const
	{
		nano::block_info result;
		assert (size () == sizeof (result));
		static_assert (sizeof (nano::block_info::account) + sizeof (nano::block_info::balance) == sizeof (result), "Packed class");
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::pending_info () const
	{
		nano::pending_info result;
		result.epoch = epoch;
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (nano::pending_info::source) + sizeof (nano::pending_info::amount), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::pending_key () const
	{
		nano::pending_key result;
		assert (size () == sizeof (result));
		static_assert (sizeof (nano::pending_key::account) + sizeof (nano::pending_key::hash) == sizeof (result), "Packed class");
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::unchecked_info () const
	{
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		nano::unchecked_info result;
		bool error (result.deserialize (stream));
		(void)error;
		assert (!error);
		return result;
	}

	explicit operator nano::uint128_union () const
	{
		nano::uint128_union result;
		assert (size () == sizeof (result));
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), result.bytes.data ());
		return result;
	}

	explicit operator nano::uint256_union () const
	{
		nano::uint256_union result;
		assert (size () == sizeof (result));
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), result.bytes.data ());
		return result;
	}

	explicit operator std::array<char, 64> () const
	{
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		std::array<char, 64> result;
		auto error = nano::try_read (stream, result);
		(void)error;
		assert (!error);
		return result;
	}

	explicit operator nano::endpoint_key () const
	{
		nano::endpoint_key result;
		std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), reinterpret_cast<uint8_t *> (&result));
		return result;
	}

	explicit operator nano::no_value () const
	{
		return no_value::dummy;
	}

	explicit operator std::shared_ptr<nano::block> () const
	{
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		std::shared_ptr<nano::block> result (nano::deserialize_block (stream));
		return result;
	}

	template <typename Block>
	std::shared_ptr<Block> convert_to_block () const
	{
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		auto error (false);
		auto result (std::make_shared<Block> (error, stream));
		assert (!error);
		return result;
	}

	explicit operator std::shared_ptr<nano::send_block> () const
	{
		return convert_to_block<nano::send_block> ();
	}

	explicit operator std::shared_ptr<nano::receive_block> () const
	{
		return convert_to_block<nano::receive_block> ();
	}

	explicit operator std::shared_ptr<nano::open_block> () const
	{
		return convert_to_block<nano::open_block> ();
	}

	explicit operator std::shared_ptr<nano::change_block> () const
	{
		return convert_to_block<nano::change_block> ();
	}

	explicit operator std::shared_ptr<nano::state_block> () const
	{
		return convert_to_block<nano::state_block> ();
	}

	explicit operator std::shared_ptr<nano::vote> () const
	{
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		auto error (false);
		auto result (nano::make_shared<nano::vote> (error, stream));
		assert (!error);
		return result;
	}

	explicit operator uint64_t () const
	{
		uint64_t result;
		nano::bufferstream stream (reinterpret_cast<uint8_t const *> (data ()), size ());
		auto error (nano::try_read (stream, result));
		(void)error;
		assert (!error);
		boost::endian::big_to_native_inplace (result);
		return result;
	}

	operator Val * () const
	{
		// Allow passing a temporary to a non-c++ function which doesn't have constness
		return const_cast<Val *> (&value);
	}

	operator Val const & () const
	{
		return value;
	}

	/** Must be specialized in the sub-class */
	void * data () const;
	size_t size () const;

	Val value;
	std::shared_ptr<std::vector<uint8_t>> buffer;
	nano::epoch epoch{ nano::epoch::unspecified };
};

class block_sideband final
{
public:
	block_sideband () = default;
	block_sideband (nano::block_type, nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t);
	void serialize (nano::stream &) const;
	bool deserialize (nano::stream &);
	static size_t size (nano::block_type);
	nano::block_type type{ nano::block_type::invalid };
	nano::block_hash successor{ 0 };
	nano::account account{ 0 };
	nano::amount balance{ 0 };
	uint64_t height{ 0 };
	uint64_t timestamp{ 0 };
};
class transaction;
class block_store;

/**
 * Summation visitor for blocks, supporting amount and balance computations. These
 * computations are mutually dependant. The natural solution is to use mutual recursion
 * between balance and amount visitors, but this leads to very deep stacks. Hence, the
 * summation visitor uses an iterative approach.
 */
class summation_visitor final : public nano::block_visitor
{
	enum summation_type
	{
		invalid = 0,
		balance = 1,
		amount = 2
	};

	/** Represents an invocation frame */
	class frame final
	{
	public:
		frame (summation_type type_a, nano::block_hash balance_hash_a, nano::block_hash amount_hash_a) :
		type (type_a), balance_hash (balance_hash_a), amount_hash (amount_hash_a)
		{
		}

		/** The summation type guides the block visitor handlers */
		summation_type type{ invalid };
		/** Accumulated balance or amount */
		nano::uint128_t sum{ 0 };
		/** The current balance hash */
		nano::block_hash balance_hash{ 0 };
		/** The current amount hash */
		nano::block_hash amount_hash{ 0 };
		/** If true, this frame is awaiting an invocation result */
		bool awaiting_result{ false };
		/** Set by the invoked frame, representing the return value */
		nano::uint128_t incoming_result{ 0 };
	};

public:
	summation_visitor (nano::transaction const &, nano::block_store const &);
	virtual ~summation_visitor () = default;
	/** Computes the balance as of \p block_hash */
	nano::uint128_t compute_balance (nano::block_hash const & block_hash);
	/** Computes the amount delta between \p block_hash and its predecessor */
	nano::uint128_t compute_amount (nano::block_hash const & block_hash);

protected:
	nano::transaction const & transaction;
	nano::block_store const & store;
	nano::network_params network_params;

	/** The final result */
	nano::uint128_t result{ 0 };
	/** The current invocation frame */
	frame * current{ nullptr };
	/** Invocation frames */
	std::stack<frame> frames;
	/** Push a copy of \p hash of the given summation \p type */
	nano::summation_visitor::frame push (nano::summation_visitor::summation_type type, nano::block_hash const & hash);
	void sum_add (nano::uint128_t addend_a);
	void sum_set (nano::uint128_t value_a);
	/** The epilogue yields the result to previous frame, if any */
	void epilogue ();

	nano::uint128_t compute_internal (nano::summation_visitor::summation_type type, nano::block_hash const &);
	void send_block (nano::send_block const &) override;
	void receive_block (nano::receive_block const &) override;
	void open_block (nano::open_block const &) override;
	void change_block (nano::change_block const &) override;
	void state_block (nano::state_block const &) override;
};

/**
 * Determine the representative for this block
 */
class representative_visitor final : public nano::block_visitor
{
public:
	representative_visitor (nano::transaction const & transaction_a, nano::block_store & store_a);
	~representative_visitor () = default;
	void compute (nano::block_hash const & hash_a);
	void send_block (nano::send_block const & block_a) override;
	void receive_block (nano::receive_block const & block_a) override;
	void open_block (nano::open_block const & block_a) override;
	void change_block (nano::change_block const & block_a) override;
	void state_block (nano::state_block const & block_a) override;
	nano::transaction const & transaction;
	nano::block_store & store;
	nano::block_hash current;
	nano::block_hash result;
};
template <typename T, typename U>
class store_iterator_impl
{
public:
	virtual ~store_iterator_impl () = default;
	virtual nano::store_iterator_impl<T, U> & operator++ () = 0;
	virtual bool operator== (nano::store_iterator_impl<T, U> const & other_a) const = 0;
	virtual bool is_end_sentinal () const = 0;
	virtual void fill (std::pair<T, U> &) const = 0;
	nano::store_iterator_impl<T, U> & operator= (nano::store_iterator_impl<T, U> const &) = delete;
	bool operator== (nano::store_iterator_impl<T, U> const * other_a) const
	{
		return (other_a != nullptr && *this == *other_a) || (other_a == nullptr && is_end_sentinal ());
	}
	bool operator!= (nano::store_iterator_impl<T, U> const & other_a) const
	{
		return !(*this == other_a);
	}
};
/**
 * Iterates the key/value pairs of a transaction
 */
template <typename T, typename U>
class store_iterator final
{
public:
	store_iterator (std::nullptr_t)
	{
	}
	store_iterator (std::unique_ptr<nano::store_iterator_impl<T, U>> impl_a) :
	impl (std::move (impl_a))
	{
		impl->fill (current);
	}
	store_iterator (nano::store_iterator<T, U> && other_a) :
	current (std::move (other_a.current)),
	impl (std::move (other_a.impl))
	{
	}
	nano::store_iterator<T, U> & operator++ ()
	{
		++*impl;
		impl->fill (current);
		return *this;
	}
	nano::store_iterator<T, U> & operator= (nano::store_iterator<T, U> && other_a)
	{
		impl = std::move (other_a.impl);
		current = std::move (other_a.current);
		return *this;
	}
	nano::store_iterator<T, U> & operator= (nano::store_iterator<T, U> const &) = delete;
	std::pair<T, U> * operator-> ()
	{
		return &current;
	}
	bool operator== (nano::store_iterator<T, U> const & other_a) const
	{
		return (impl == nullptr && other_a.impl == nullptr) || (impl != nullptr && *impl == other_a.impl.get ()) || (other_a.impl != nullptr && *other_a.impl == impl.get ());
	}
	bool operator!= (nano::store_iterator<T, U> const & other_a) const
	{
		return !(*this == other_a);
	}

private:
	std::pair<T, U> current;
	std::unique_ptr<nano::store_iterator_impl<T, U>> impl;
};

class transaction_impl
{
public:
	virtual ~transaction_impl () = default;
	virtual void * get_handle () const = 0;
};

class read_transaction_impl : public transaction_impl
{
public:
	virtual void reset () const = 0;
	virtual void renew () const = 0;
};

class write_transaction_impl : public transaction_impl
{
public:
	virtual void commit () const = 0;
	virtual void renew () = 0;
};

class transaction
{
public:
	virtual ~transaction () = default;
	virtual void * get_handle () const = 0;
};

/**
 * RAII wrapper of a read MDB_txn where the constructor starts the transaction
 * and the destructor aborts it.
 */
class read_transaction final : public transaction
{
public:
	explicit read_transaction (std::unique_ptr<nano::read_transaction_impl> read_transaction_impl);
	void * get_handle () const override;
	void reset () const;
	void renew () const;
	void refresh () const;

private:
	std::unique_ptr<nano::read_transaction_impl> impl;
};

/**
 * RAII wrapper of a read-write MDB_txn where the constructor starts the transaction
 * and the destructor commits it.
 */
class write_transaction final : public transaction
{
public:
	explicit write_transaction (std::unique_ptr<nano::write_transaction_impl> write_transaction_impl);
	void * get_handle () const override;
	void commit () const;
	void renew ();

private:
	std::unique_ptr<nano::write_transaction_impl> impl;
};

/**
 * Manages block storage and iteration
 */
class block_store
{
public:
	virtual ~block_store () = default;
	virtual void initialize (nano::transaction const &, nano::genesis const &) = 0;
	virtual void block_put (nano::transaction const &, nano::block_hash const &, nano::block const &, nano::block_sideband const &, nano::epoch version = nano::epoch::epoch_0) = 0;
	virtual nano::block_hash block_successor (nano::transaction const &, nano::block_hash const &) const = 0;
	virtual void block_successor_clear (nano::transaction const &, nano::block_hash const &) = 0;
	virtual std::shared_ptr<nano::block> block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) const = 0;
	virtual std::shared_ptr<nano::block> block_random (nano::transaction const &) = 0;
	virtual void block_del (nano::transaction const &, nano::block_hash const &) = 0;
	virtual bool block_exists (nano::transaction const &, nano::block_hash const &) = 0;
	virtual bool block_exists (nano::transaction const &, nano::block_type, nano::block_hash const &) = 0;
	virtual nano::block_counts block_count (nano::transaction const &) = 0;
	virtual bool root_exists (nano::transaction const &, nano::uint256_union const &) = 0;
	virtual bool source_exists (nano::transaction const &, nano::block_hash const &) = 0;
	virtual nano::account block_account (nano::transaction const &, nano::block_hash const &) const = 0;

	virtual void frontier_put (nano::transaction const &, nano::block_hash const &, nano::account const &) = 0;
	virtual nano::account frontier_get (nano::transaction const &, nano::block_hash const &) const = 0;
	virtual void frontier_del (nano::transaction const &, nano::block_hash const &) = 0;

	virtual void account_put (nano::transaction const &, nano::account const &, nano::account_info const &) = 0;
	virtual bool account_get (nano::transaction const &, nano::account const &, nano::account_info &) = 0;
	virtual void account_del (nano::transaction const &, nano::account const &) = 0;
	virtual bool account_exists (nano::transaction const &, nano::account const &) = 0;
	virtual size_t account_count (nano::transaction const &) = 0;
	virtual void confirmation_height_clear (nano::transaction const &, nano::account const & account, nano::account_info const & account_info) = 0;
	virtual void confirmation_height_clear (nano::transaction const &) = 0;
	virtual uint64_t cemented_count (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v0_begin (nano::transaction const &, nano::account const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v0_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v0_end () = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v1_begin (nano::transaction const &, nano::account const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v1_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_v1_end () = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_begin (nano::transaction const &, nano::account const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, nano::account_info> latest_end () = 0;

	virtual void pending_put (nano::transaction const &, nano::pending_key const &, nano::pending_info const &) = 0;
	virtual void pending_del (nano::transaction const &, nano::pending_key const &) = 0;
	virtual bool pending_get (nano::transaction const &, nano::pending_key const &, nano::pending_info &) = 0;
	virtual bool pending_exists (nano::transaction const &, nano::pending_key const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v0_begin (nano::transaction const &, nano::pending_key const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v0_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v0_end () = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v1_begin (nano::transaction const &, nano::pending_key const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v1_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_v1_end () = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_begin (nano::transaction const &, nano::pending_key const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::pending_key, nano::pending_info> pending_end () = 0;

	virtual bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) const = 0;
	virtual nano::uint128_t block_balance (nano::transaction const &, nano::block_hash const &) = 0;
	virtual nano::uint128_t block_balance_calculated (std::shared_ptr<nano::block>, nano::block_sideband const &) const = 0;
	virtual nano::epoch block_version (nano::transaction const &, nano::block_hash const &) = 0;

	virtual nano::uint128_t representation_get (nano::transaction const &, nano::account const &) = 0;
	virtual void representation_put (nano::transaction const &, nano::account const &, nano::uint128_t const &) = 0;
	virtual void representation_add (nano::transaction const &, nano::account const &, nano::uint128_t const &) = 0;
	virtual nano::store_iterator<nano::account, nano::uint128_union> representation_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, nano::uint128_union> representation_end () = 0;

	virtual void unchecked_clear (nano::transaction const &) = 0;
	virtual void unchecked_put (nano::transaction const &, nano::unchecked_key const &, nano::unchecked_info const &) = 0;
	virtual void unchecked_put (nano::transaction const &, nano::block_hash const &, std::shared_ptr<nano::block> const &) = 0;
	virtual std::vector<nano::unchecked_info> unchecked_get (nano::transaction const &, nano::block_hash const &) = 0;
	virtual void unchecked_del (nano::transaction const &, nano::unchecked_key const &) = 0;
	virtual nano::store_iterator<nano::unchecked_key, nano::unchecked_info> unchecked_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::unchecked_key, nano::unchecked_info> unchecked_begin (nano::transaction const &, nano::unchecked_key const &) = 0;
	virtual nano::store_iterator<nano::unchecked_key, nano::unchecked_info> unchecked_end () = 0;
	virtual size_t unchecked_count (nano::transaction const &) = 0;

	// Return latest vote for an account from store
	virtual std::shared_ptr<nano::vote> vote_get (nano::transaction const &, nano::account const &) = 0;
	// Populate vote with the next sequence number
	virtual std::shared_ptr<nano::vote> vote_generate (nano::transaction const &, nano::account const &, nano::raw_key const &, std::shared_ptr<nano::block>) = 0;
	virtual std::shared_ptr<nano::vote> vote_generate (nano::transaction const &, nano::account const &, nano::raw_key const &, std::vector<nano::block_hash>) = 0;
	// Return either vote or the stored vote with a higher sequence number
	virtual std::shared_ptr<nano::vote> vote_max (nano::transaction const &, std::shared_ptr<nano::vote>) = 0;
	// Return latest vote for an account considering the vote cache
	virtual std::shared_ptr<nano::vote> vote_current (nano::transaction const &, nano::account const &) = 0;
	virtual void flush (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, std::shared_ptr<nano::vote>> vote_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<nano::account, std::shared_ptr<nano::vote>> vote_end () = 0;

	virtual void online_weight_put (nano::transaction const &, uint64_t, nano::amount const &) = 0;
	virtual void online_weight_del (nano::transaction const &, uint64_t) = 0;
	virtual nano::store_iterator<uint64_t, nano::amount> online_weight_begin (nano::transaction const &) = 0;
	virtual nano::store_iterator<uint64_t, nano::amount> online_weight_end () = 0;
	virtual size_t online_weight_count (nano::transaction const &) const = 0;
	virtual void online_weight_clear (nano::transaction const &) = 0;

	virtual void version_put (nano::transaction const &, int) = 0;
	virtual int version_get (nano::transaction const &) const = 0;

	virtual void peer_put (nano::transaction const & transaction_a, nano::endpoint_key const & endpoint_a) = 0;
	virtual void peer_del (nano::transaction const & transaction_a, nano::endpoint_key const & endpoint_a) = 0;
	virtual bool peer_exists (nano::transaction const & transaction_a, nano::endpoint_key const & endpoint_a) const = 0;
	virtual size_t peer_count (nano::transaction const & transaction_a) const = 0;
	virtual void peer_clear (nano::transaction const & transaction_a) = 0;
	virtual nano::store_iterator<nano::endpoint_key, nano::no_value> peers_begin (nano::transaction const & transaction_a) = 0;
	virtual nano::store_iterator<nano::endpoint_key, nano::no_value> peers_end () = 0;

	virtual uint64_t block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const = 0;
	virtual void serialize_mdb_tracker (boost::property_tree::ptree &, std::chrono::milliseconds, std::chrono::milliseconds) = 0;

	/** Start read-write transaction */
	virtual nano::write_transaction tx_begin_write () = 0;

	/** Start read-only transaction */
	virtual nano::read_transaction tx_begin_read () = 0;
};

template <typename Val>
class block_predecessor_set;

/** This base class implements the block_store interface functions which have DB agnostic functionality */
template <class Val>
class block_store_partial : public block_store
{
public:
	using block_store::block_exists;
	using block_store::unchecked_put;

	friend class nano::block_predecessor_set<Val>;

	std::mutex cache_mutex;

	/**
	 * If using a different store version than the latest then you may need
	 * to modify some of the objects in the store to be appropriate for the version before an upgrade.
	 */
	void initialize (nano::transaction const & transaction_a, nano::genesis const & genesis_a) override
	{
		auto hash_l (genesis_a.hash ());
		assert (latest_v0_begin (transaction_a) == latest_v0_end ());
		assert (latest_v1_begin (transaction_a) == latest_v1_end ());
		nano::block_sideband sideband (nano::block_type::open, network_params.ledger.genesis_account, 0, network_params.ledger.genesis_amount, 1, nano::seconds_since_epoch ());
		block_put (transaction_a, hash_l, *genesis_a.open, sideband);
		account_put (transaction_a, network_params.ledger.genesis_account, { hash_l, genesis_a.open->hash (), genesis_a.open->hash (), std::numeric_limits<nano::uint128_t>::max (), nano::seconds_since_epoch (), 1, 1, nano::epoch::epoch_0 });
		representation_put (transaction_a, network_params.ledger.genesis_account, std::numeric_limits<nano::uint128_t>::max ());
		frontier_put (transaction_a, hash_l, network_params.ledger.genesis_account);
	}

	nano::uint128_t block_balance (nano::transaction const & transaction_a, nano::block_hash const & hash_a) override
	{
		nano::block_sideband sideband;
		auto block (block_get (transaction_a, hash_a, &sideband));
		nano::uint128_t result (block_balance_calculated (block, sideband));
		return result;
	}

	void representation_add (nano::transaction const & transaction_a, nano::block_hash const & source_a, nano::uint128_t const & amount_a) override
	{
		auto source_block (block_get (transaction_a, source_a));
		assert (source_block != nullptr);
		auto source_rep (source_block->representative ());
		auto source_previous (representation_get (transaction_a, source_rep));
		representation_put (transaction_a, source_rep, source_previous + amount_a);
	}

	bool account_exists (nano::transaction const & transaction_a, nano::account const & account_a) override
	{
		auto iterator (latest_begin (transaction_a, account_a));
		return iterator != latest_end () && nano::account (iterator->first) == account_a;
	}

	void confirmation_height_clear (nano::transaction const & transaction_a, nano::account const & account, nano::account_info const & account_info) override
	{
		nano::account_info info_copy (account_info);
		if (info_copy.confirmation_height > 0)
		{
			info_copy.confirmation_height = 0;
			account_put (transaction_a, account, info_copy);
		}
	}

	void confirmation_height_clear (nano::transaction const & transaction_a) override
	{
		for (auto i (latest_begin (transaction_a)), n (latest_end ()); i != n; ++i)
		{
			confirmation_height_clear (transaction_a, i->first, i->second);
		}
	}

	bool pending_exists (nano::transaction const & transaction_a, nano::pending_key const & key_a) override
	{
		auto iterator (pending_begin (transaction_a, key_a));
		return iterator != pending_end () && nano::pending_key (iterator->first) == key_a;
	}

	std::vector<nano::unchecked_info> unchecked_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a) override
	{
		std::vector<nano::unchecked_info> result;
		for (auto i (unchecked_begin (transaction_a, nano::unchecked_key (hash_a, 0))), n (unchecked_end ()); i != n && nano::block_hash (i->first.key ()) == hash_a; ++i)
		{
			nano::unchecked_info const & unchecked_info (i->second);
			result.push_back (unchecked_info);
		}
		return result;
	}

	void block_put (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a, nano::block_sideband const & sideband_a, nano::epoch epoch_a = nano::epoch::epoch_0) override
	{
		assert (block_a.type () == sideband_a.type);
		assert (sideband_a.successor.is_zero () || block_exists (transaction_a, sideband_a.successor));
		std::vector<uint8_t> vector;
		{
			nano::vectorstream stream (vector);
			block_a.serialize (stream);
			sideband_a.serialize (stream);
		}
		block_raw_put (transaction_a, vector, block_a.type (), epoch_a, hash_a);
		nano::block_predecessor_set<Val> predecessor (transaction_a, *this);
		block_a.visit (predecessor);
		assert (block_a.previous ().is_zero () || block_successor (transaction_a, block_a.previous ()) == hash_a);
	}

	// Converts a block hash to a block height
	uint64_t block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override
	{
		nano::block_sideband sideband;
		auto block = block_get (transaction_a, hash_a, &sideband);
		assert (block != nullptr);
		return sideband.height;
	}

	std::shared_ptr<nano::block> block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_sideband * sideband_a = nullptr) const override
	{
		nano::block_type type;
		auto value (block_raw_get (transaction_a, hash_a, type));
		std::shared_ptr<nano::block> result;
		if (value.size () != 0)
		{
			nano::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
			result = nano::deserialize_block (stream, type);
			assert (result != nullptr);
			if (sideband_a)
			{
				sideband_a->type = type;
				if (full_sideband (transaction_a) || entry_has_sideband (value.size (), type))
				{
					auto error (sideband_a->deserialize (stream));
					(void)error;
					assert (!error);
				}
				else
				{
					// Reconstruct sideband data for block.
					sideband_a->account = block_account_computed (transaction_a, hash_a);
					sideband_a->balance = block_balance_computed (transaction_a, hash_a);
					sideband_a->successor = block_successor (transaction_a, hash_a);
					sideband_a->height = 0;
					sideband_a->timestamp = 0;
				}
			}
		}
		return result;
	}

	bool block_exists (nano::transaction const & tx_a, nano::block_hash const & hash_a) override
	{
		// Table lookups are ordered by match probability
		// clang-format off
		return
			block_exists (tx_a, nano::block_type::state, hash_a) ||
			block_exists (tx_a, nano::block_type::send, hash_a) ||
			block_exists (tx_a, nano::block_type::receive, hash_a) ||
			block_exists (tx_a, nano::block_type::open, hash_a) ||
			block_exists (tx_a, nano::block_type::change, hash_a);
		// clang-format on
	}

	bool root_exists (nano::transaction const & transaction_a, nano::uint256_union const & root_a) override
	{
		return block_exists (transaction_a, root_a) || account_exists (transaction_a, root_a);
	}

	bool source_exists (nano::transaction const & transaction_a, nano::block_hash const & source_a) override
	{
		return block_exists (transaction_a, nano::block_type::state, source_a) || block_exists (transaction_a, nano::block_type::send, source_a);
	}

	nano::account block_account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override
	{
		nano::block_sideband sideband;
		auto block (block_get (transaction_a, hash_a, &sideband));
		nano::account result (block->account ());
		if (result.is_zero ())
		{
			result = sideband.account;
		}
		assert (!result.is_zero ());
		return result;
	}

	nano::uint128_t block_balance_calculated (std::shared_ptr<nano::block> block_a, nano::block_sideband const & sideband_a) const override
	{
		nano::uint128_t result;
		switch (block_a->type ())
		{
			case nano::block_type::open:
			case nano::block_type::receive:
			case nano::block_type::change:
				result = sideband_a.balance.number ();
				break;
			case nano::block_type::send:
				result = boost::polymorphic_downcast<nano::send_block *> (block_a.get ())->hashables.balance.number ();
				break;
			case nano::block_type::state:
				result = boost::polymorphic_downcast<nano::state_block *> (block_a.get ())->hashables.balance.number ();
				break;
			case nano::block_type::invalid:
			case nano::block_type::not_a_block:
				release_assert (false);
				break;
		}
		return result;
	}

	nano::block_hash block_successor (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override
	{
		nano::block_type type;
		auto value (block_raw_get (transaction_a, hash_a, type));
		nano::block_hash result;
		if (value.size () != 0)
		{
			assert (value.size () >= result.bytes.size ());
			nano::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()) + block_successor_offset (transaction_a, value.size (), type), result.bytes.size ());
			auto error (nano::try_read (stream, result.bytes));
			(void)error;
			assert (!error);
		}
		else
		{
			result.clear ();
		}
		return result;
	}

	bool full_sideband (nano::transaction const & transaction_a) const
	{
		return version_get (transaction_a) > 12;
	}

	void block_successor_clear (nano::transaction const & transaction_a, nano::block_hash const & hash_a) override
	{
		nano::block_type type;
		auto value (block_raw_get (transaction_a, hash_a, type));
		auto version (block_version (transaction_a, hash_a));
		assert (value.size () != 0);
		std::vector<uint8_t> data (static_cast<uint8_t *> (value.data ()), static_cast<uint8_t *> (value.data ()) + value.size ());
		std::fill_n (data.begin () + block_successor_offset (transaction_a, value.size (), type), sizeof (nano::uint256_union), uint8_t{ 0 });
		block_raw_put (transaction_a, data, type, version, hash_a);
	}

	uint64_t cemented_count (nano::transaction const & transaction_a) override
	{
		uint64_t sum = 0;
		for (auto i (latest_begin (transaction_a)), n (latest_end ()); i != n; ++i)
		{
			nano::account_info const & info (i->second);
			sum += info.confirmation_height;
		}
		return sum;
	}

	void unchecked_put (nano::transaction const & transaction_a, nano::block_hash const & hash_a, std::shared_ptr<nano::block> const & block_a) override
	{
		nano::unchecked_key key (hash_a, block_a->hash ());
		nano::unchecked_info info (block_a, block_a->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown);
		unchecked_put (transaction_a, key, info);
	}

	std::shared_ptr<nano::vote> vote_current (nano::transaction const & transaction_a, nano::account const & account_a) override
	{
		assert (!cache_mutex.try_lock ());
		std::shared_ptr<nano::vote> result;
		auto existing (vote_cache_l1.find (account_a));
		auto have_existing (true);
		if (existing == vote_cache_l1.end ())
		{
			existing = vote_cache_l2.find (account_a);
			if (existing == vote_cache_l2.end ())
			{
				have_existing = false;
			}
		}
		if (have_existing)
		{
			result = existing->second;
		}
		else
		{
			result = vote_get (transaction_a, account_a);
		}
		return result;
	}

	std::shared_ptr<nano::vote> vote_generate (nano::transaction const & transaction_a, nano::account const & account_a, nano::raw_key const & key_a, std::shared_ptr<nano::block> block_a) override
	{
		std::lock_guard<std::mutex> lock (cache_mutex);
		auto result (vote_current (transaction_a, account_a));
		uint64_t sequence ((result ? result->sequence : 0) + 1);
		result = std::make_shared<nano::vote> (account_a, key_a, sequence, block_a);
		vote_cache_l1[account_a] = result;
		return result;
	}

	std::shared_ptr<nano::vote> vote_generate (nano::transaction const & transaction_a, nano::account const & account_a, nano::raw_key const & key_a, std::vector<nano::block_hash> blocks_a) override
	{
		std::lock_guard<std::mutex> lock (cache_mutex);
		auto result (vote_current (transaction_a, account_a));
		uint64_t sequence ((result ? result->sequence : 0) + 1);
		result = std::make_shared<nano::vote> (account_a, key_a, sequence, blocks_a);
		vote_cache_l1[account_a] = result;
		return result;
	}

	std::shared_ptr<nano::vote> vote_max (nano::transaction const & transaction_a, std::shared_ptr<nano::vote> vote_a) override
	{
		std::lock_guard<std::mutex> lock (cache_mutex);
		auto current (vote_current (transaction_a, vote_a->account));
		auto result (vote_a);
		if (current != nullptr && current->sequence > result->sequence)
		{
			result = current;
		}
		vote_cache_l1[vote_a->account] = result;
		return result;
	}

	virtual void block_raw_put (nano::transaction const & transaction_a, std::vector<uint8_t> const & data, nano::block_type block_type_a, nano::epoch epoch_a, nano::block_hash const & hash_a) = 0;

protected:
	nano::network_params network_params;
	std::unordered_map<nano::account, std::shared_ptr<nano::vote>> vote_cache_l1;
	std::unordered_map<nano::account, std::shared_ptr<nano::vote>> vote_cache_l2;

	bool entry_has_sideband (size_t entry_size_a, nano::block_type type_a) const
	{
		return entry_size_a == nano::block::size (type_a) + nano::block_sideband::size (type_a);
	}

	nano::db_val<Val> block_raw_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) const
	{
		nano::db_val<Val> result;
		// Table lookups are ordered by match probability
		nano::block_type block_types[]{ nano::block_type::state, nano::block_type::send, nano::block_type::receive, nano::block_type::open, nano::block_type::change };
		for (auto current_type : block_types)
		{
			auto db_val (block_raw_get_by_type (transaction_a, hash_a, current_type));
			if (db_val.is_initialized ())
			{
				type_a = current_type;
				result = db_val.get ();
				break;
			}
		}

		return result;
	}

	// Return account containing hash
	nano::account block_account_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
	{
		assert (!full_sideband (transaction_a));
		nano::account result (0);
		auto hash (hash_a);
		while (result.is_zero ())
		{
			auto block (block_get (transaction_a, hash));
			assert (block);
			result = block->account ();
			if (result.is_zero ())
			{
				auto type (nano::block_type::invalid);
				auto value (block_raw_get (transaction_a, block->previous (), type));
				if (entry_has_sideband (value.size (), type))
				{
					result = block_account (transaction_a, block->previous ());
				}
				else
				{
					nano::block_info block_info;
					if (!block_info_get (transaction_a, hash, block_info))
					{
						result = block_info.account;
					}
					else
					{
						result = frontier_get (transaction_a, hash);
						if (result.is_zero ())
						{
							auto successor (block_successor (transaction_a, hash));
							assert (!successor.is_zero ());
							hash = successor;
						}
					}
				}
			}
		}
		assert (!result.is_zero ());
		return result;
	}

	nano::uint128_t block_balance_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
	{
		assert (!full_sideband (transaction_a));
		summation_visitor visitor (transaction_a, *this);
		return visitor.compute_balance (hash_a);
	}

	size_t block_successor_offset (nano::transaction const & transaction_a, size_t entry_size_a, nano::block_type type_a) const
	{
		size_t result;
		if (full_sideband (transaction_a) || entry_has_sideband (entry_size_a, type_a))
		{
			result = entry_size_a - nano::block_sideband::size (type_a);
		}
		else
		{
			// Read old successor-only sideband
			assert (entry_size_a == nano::block::size (type_a) + sizeof (nano::uint256_union));
			result = entry_size_a - sizeof (nano::uint256_union);
		}
		return result;
	}

	virtual boost::optional<DB_val> block_raw_get_by_type (nano::transaction const &, nano::block_hash const &, nano::block_type &) const = 0;
};

/**
 * Fill in our predecessors
 */
template <class Val>
class block_predecessor_set : public nano::block_visitor
{
public:
	block_predecessor_set (nano::transaction const & transaction_a, nano::block_store_partial<Val> & store_a) :
	transaction (transaction_a),
	store (store_a)
	{
	}
	virtual ~block_predecessor_set () = default;
	void fill_value (nano::block const & block_a)
	{
		auto hash (block_a.hash ());
		nano::block_type type;
		auto value (store.block_raw_get (transaction, block_a.previous (), type));
		auto version (store.block_version (transaction, block_a.previous ()));
		assert (value.size () != 0);
		std::vector<uint8_t> data (static_cast<uint8_t *> (value.data ()), static_cast<uint8_t *> (value.data ()) + value.size ());
		std::copy (hash.bytes.begin (), hash.bytes.end (), data.begin () + store.block_successor_offset (transaction, value.size (), type));
		store.block_raw_put (transaction, data, type, version, block_a.previous ());
	}
	void send_block (nano::send_block const & block_a) override
	{
		fill_value (block_a);
	}
	void receive_block (nano::receive_block const & block_a) override
	{
		fill_value (block_a);
	}
	void open_block (nano::open_block const & block_a) override
	{
		// Open blocks don't have a predecessor
	}
	void change_block (nano::change_block const & block_a) override
	{
		fill_value (block_a);
	}
	void state_block (nano::state_block const & block_a) override
	{
		if (!block_a.previous ().is_zero ())
		{
			fill_value (block_a);
		}
	}
	nano::transaction const & transaction;
	nano::block_store_partial<Val> & store;
};
}

max_stars_repo_name: dpball/NanoLink-Cash

max_issues_repo_name: dpball/NanoLink-Cash

max_forks_repo_name: dpball/NanoLink-Cash


text:
/**
 * @file Function.hpp
 * @author Zachary Frost
 * @date Created on 2021-04-28
 * @copyright See LICENSE.md at repo root
 * @brief Type aliases for function objects
 */

#pragma once
#include <boost/function.hpp>

namespace pr {
    template <typename... Sig>
    using Function = boost::function<Sig...>;

}

ext: hpp

max_stars_repo_name: PrismalEngine/prismal-engine

max_issues_repo_name: PrismalEngine/prismal-engine

max_forks_repo_name: PrismalEngine/prismal-engine


text:
/*

Copyright (c) 2008, 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 <fstream>
#include <deque>

#include "setup_transfer.hpp"

#include "libtorrent/session.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/http_parser.hpp"
#include "libtorrent/thread.hpp"

#include "libtorrent/thread.hpp"
#include <boost/tuple/tuple.hpp>
#include <boost/bind.hpp>

#include "test.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/socket_io.hpp" // print_endpoint
#include "libtorrent/socket_type.hpp"
#include "libtorrent/instantiate_connection.hpp"

#ifdef TORRENT_USE_OPENSSL
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/ssl/context.hpp>
#endif

#include <boost/detail/atomic_count.hpp>

#define DEBUG_WEB_SERVER 0

#define DLOG if (DEBUG_WEB_SERVER) fprintf

using namespace libtorrent;

bool tests_failure = false;

void report_failure(char const* err, char const* file, int line)
{
	fprintf(stderr, "\n***** %s:%d \"%s\" *****\n\n", file, line, err);
	tests_failure = true;
}

std::auto_ptr<alert> wait_for_alert(session& ses, int type)
{
	std::auto_ptr<alert> ret;
	while (!ret.get())
	{
		ses.wait_for_alert(milliseconds(5000));
		std::deque<alert*> alerts;
		ses.pop_alerts(&alerts);
		for (std::deque<alert*>::iterator i = alerts.begin()
			, end(alerts.end()); i != end; ++i)
		{
			if (!ret.get() && (*i)->type() == type)
			{
				ret = std::auto_ptr<alert>(*i);
			}
			else
				delete *i;
		}
	}
	return ret;
}

int load_file(std::string const& filename, std::vector<char>& v, libtorrent::error_code& ec, int limit)
{
	ec.clear();
	FILE* f = fopen(filename.c_str(), "rb");
	if (f == NULL)
	{
		ec.assign(errno, boost::system::get_generic_category());
		return -1;
	}

	int r = fseek(f, 0, SEEK_END);
	if (r != 0)
	{
		ec.assign(errno, boost::system::get_generic_category());
		fclose(f);
		return -1;
	}
	long s = ftell(f);
	if (s < 0)
	{
		ec.assign(errno, boost::system::get_generic_category());
		fclose(f);
		return -1;
	}

	if (s > limit)
	{
		fclose(f);
		return -2;
	}

	r = fseek(f, 0, SEEK_SET);
	if (r != 0)
	{
		ec.assign(errno, boost::system::get_generic_category());
		fclose(f);
		return -1;
	}

	v.resize(s);
	if (s == 0)
	{
		fclose(f);
		return 0;
	}

	r = fread(&v[0], 1, v.size(), f);
	if (r < 0)
	{
		ec.assign(errno, boost::system::get_generic_category());
		fclose(f);
		return -1;
	}

	fclose(f);

	if (r != s) return -3;

	return 0;
}

bool print_alerts(libtorrent::session& ses, char const* name
	, bool allow_disconnects, bool allow_no_torrents, bool allow_failed_fastresume
	, bool (*predicate)(libtorrent::alert*), bool no_output)
{
	bool ret = false;
	std::vector<torrent_handle> handles = ses.get_torrents();
	TEST_CHECK(!handles.empty() || allow_no_torrents);
	torrent_handle h;
	if (!handles.empty()) h = handles[0];
	std::deque<alert*> alerts;
	ses.pop_alerts(&alerts);
	for (std::deque<alert*>::iterator i = alerts.begin(); i != alerts.end(); ++i)
	{
		if (predicate && predicate(*i)) ret = true;
		if (peer_disconnected_alert* p = alert_cast<peer_disconnected_alert>(*i))
		{
			fprintf(stderr, "    %s(%s): %s\n", name, print_endpoint(p->ip).c_str(), p->message().c_str());
		}
		else if ((*i)->message() != "block downloading"
			&& (*i)->message() != "block finished"
			&& (*i)->message() != "piece finished"
			&& !no_output)
		{
			fprintf(stderr, "    %s: %s\n", name, (*i)->message().c_str());
		}

		TEST_CHECK(alert_cast<fastresume_rejected_alert>(*i) == 0 || allow_failed_fastresume);

		peer_error_alert* pea = alert_cast<peer_error_alert>(*i);
		if (pea)
		{
			fprintf(stderr, "    peer error: %s\n", pea->error.message().c_str());
			TEST_CHECK((!handles.empty() && h.status().is_seeding)
				|| pea->error.message() == "connecting to peer"
				|| pea->error.message() == "closing connection to ourself"
				|| pea->error.message() == "duplicate connection"
				|| pea->error.message() == "duplicate peer-id"
				|| pea->error.message() == "upload to upload connection"
				|| pea->error.message() == "stopping torrent"
				|| (allow_disconnects && pea->error.message() == "Broken pipe")
				|| (allow_disconnects && pea->error.message() == "Connection reset by peer")
				|| (allow_disconnects && pea->error.message() == "End of file."));
		}
		delete *i;
	}
	return ret;
}

bool listen_done = false;
bool listen_alert(libtorrent::alert* a)
{
	if (alert_cast<listen_failed_alert>(a)
		|| alert_cast<listen_succeeded_alert>(a))
		listen_done = true;
	return true;
}

void wait_for_listen(libtorrent::session& ses, char const* name)
{
	listen_done = false;
	alert const* a = 0;
	do
	{
		print_alerts(ses, name, true, true, true, &listen_alert, false);
		if (listen_done) break;
		a = ses.wait_for_alert(milliseconds(500));
	} while (a);
}

void print_ses_rate(float time
	, libtorrent::torrent_status const* st1
	, libtorrent::torrent_status const* st2
	, libtorrent::torrent_status const* st3)
{
	fprintf(stderr, "%3.1fs | %dkB/s %dkB/s %d%% %d", time
		, int(st1->download_payload_rate / 1000)
		, int(st1->upload_payload_rate / 1000)
		, int(st1->progress * 100)
		, st1->num_peers);
	if (st2)
		std::cerr << " : "
			<< int(st2->download_payload_rate / 1000.f) << "kB/s "
			<< int(st2->upload_payload_rate / 1000.f) << "kB/s "
			<< int(st2->progress * 100) << "% "
			<< st2->num_peers
			<< " cc: " << st2->connect_candidates;
	if (st3)
		std::cerr << " : "
			<< int(st3->download_payload_rate / 1000.f) << "kB/s "
			<< int(st3->upload_payload_rate / 1000.f) << "kB/s "
			<< int(st3->progress * 100) << "% "
			<< st3->num_peers
			<< " cc: " << st3->connect_candidates;

	fprintf(stderr, "\n");
}

void test_sleep(int millisec)
{
	libtorrent::sleep(millisec);
}

void stop_proxy(int port)
{
	char buf[100];
	snprintf(buf, sizeof(buf), "delegated -P%d -Fkill", port);
	int ret = system(buf);
	if (ret == 0)
	{
		perror("system");
	}
}

int start_proxy(int proxy_type)
{
	using namespace libtorrent;

	int port = 10000 + (rand() % 50000);

	stop_proxy(port);

	char const* type = "";
	char const* auth = "";

	switch (proxy_type)
	{
		case proxy_settings::socks4:
			type = "socks4";
			break;
		case proxy_settings::socks5:
			type = "socks5";
			break;
		case proxy_settings::socks5_pw:
			type = "socks5";
			auth = "AUTHORIZER=-list{testuser:testpass}";
			break;
		case proxy_settings::http:
			type = "http";
			break;
		case proxy_settings::http_pw:
			type = "http";
			auth = "AUTHORIZER=-list{testuser:testpass}";
			break;
	}

	char buf[512];
	// we need to echo n since dg will ask us to configure it
	snprintf(buf, sizeof(buf), "echo n | delegated -P%d ADMIN=test@test.com "
		"PERMIT=\"*:*:localhost\" REMITTABLE=\"*\" RELAY=proxy,delegate "
		"SERVER=%s %s"
		, port, type, auth);

	fprintf(stderr, "starting delegated proxy on port %d (%s %s)...\n", port, type, auth);
	int r = system(buf);
	if (r != 0)
	{
		fprintf(stderr, "failed (%d) %s\n", errno, strerror(errno));
		exit(1);
	}
	fprintf(stderr, "launched\n");
	// apparently delegate takes a while to open its listen port
	test_sleep(500);
	return port;
}

using namespace libtorrent;

template <class T>
boost::intrusive_ptr<T> clone_ptr(boost::intrusive_ptr<T> const& ptr)
{
	return boost::intrusive_ptr<T>(new T(*ptr));
}

void create_random_files(std::string const& path, const int file_sizes[], int num_files)
{
	error_code ec;
	char* random_data = (char*)malloc(300000);
	for (int i = 0; i != num_files; ++i)
	{
		std::generate(random_data, random_data + 300000, &std::rand);
		char filename[200];
		snprintf(filename, sizeof(filename), "test%d", i);
		std::string full_path = combine_path(path, filename);
		int to_write = file_sizes[i];
		file f(full_path, file::write_only, ec);
		if (ec) fprintf(stderr, "failed to create file \"%s\": (%d) %s\n"
			, full_path.c_str(), ec.value(), ec.message().c_str());
		size_type offset = 0;
		while (to_write > 0)
		{
			int s = (std::min)(to_write, 300000);
			file::iovec_t b = { random_data, size_t(s)};
			f.writev(offset, &b, 1, ec);
			if (ec) fprintf(stderr, "failed to write file \"%s\": (%d) %s\n"
				, full_path.c_str(), ec.value(), ec.message().c_str());
			offset += s;
			to_write -= s;
		}
	}
	free(random_data);
}

boost::intrusive_ptr<torrent_info> create_torrent(std::ostream* file, int piece_size
	, int num_pieces, bool add_tracker, bool encrypted_torrent)
{
	char const* tracker_url = "http://non-existent-name.com/announce";
	// excercise the path when encountering invalid urls
	char const* invalid_tracker_url = "http:";
	char const* invalid_tracker_protocol = "foo://non/existent-name.com/announce";

	file_storage fs;
	int total_size = piece_size * num_pieces;
	fs.add_file("temporary", total_size);
	libtorrent::create_torrent t(fs, piece_size);
	if (add_tracker)
	{
		t.add_tracker(tracker_url);
		t.add_tracker(invalid_tracker_url);
		t.add_tracker(invalid_tracker_protocol);
	}

	std::vector<char> piece(piece_size);
	for (int i = 0; i < int(piece.size()); ++i)
		piece[i] = (i % 26) + 'A';

	// calculate the hash for all pieces
	int num = t.num_pieces();
	sha1_hash ph = hasher(&piece[0], piece.size()).final();
	for (int i = 0; i < num; ++i)
		t.set_hash(i, ph);

	if (file)
	{
		while (total_size > 0)
		{
			file->write(&piece[0], (std::min)(int(piece.size()), total_size));
			total_size -= piece.size();
		}
	}

	std::vector<char> tmp;
	std::back_insert_iterator<std::vector<char> > out(tmp);

	entry tor = t.generate();

	if (encrypted_torrent)
	{
		std::string key;
		key.resize(32);
		std::generate(key.begin(), key.end(), &std::rand);
		tor["info"]["encryption-key"] = key;
	}

	bencode(out, tor);
	error_code ec;
	return boost::intrusive_ptr<torrent_info>(new torrent_info(
		&tmp[0], tmp.size(), ec));
}

boost::tuple<torrent_handle, torrent_handle, torrent_handle>
setup_transfer(session* ses1, session* ses2, session* ses3
	, bool clear_files, bool use_metadata_transfer, bool connect_peers
	, std::string suffix, int piece_size
	, boost::intrusive_ptr<torrent_info>* torrent, bool super_seeding
	, add_torrent_params const* p, bool stop_lsd, bool encrypted_torrent)
{
	assert(ses1);
	assert(ses2);

	if (stop_lsd)
	{
		ses1->stop_lsd();
		ses2->stop_lsd();
		if (ses3) ses3->stop_lsd();
	}

	session_settings sess_set = ses1->settings();
	if (ses3) sess_set.allow_multiple_connections_per_ip = true;
	sess_set.ignore_limits_on_local_network = false;
	sess_set.mixed_mode_algorithm = session_settings::prefer_tcp;
	sess_set.max_failcount = 1;
	ses1->set_settings(sess_set);
	ses2->set_settings(sess_set);
	if (ses3) ses3->set_settings(sess_set);
	ses1->set_alert_mask(~(alert::progress_notification | alert::stats_notification));
	ses2->set_alert_mask(~(alert::progress_notification | alert::stats_notification));
	if (ses3) ses3->set_alert_mask(~(alert::progress_notification | alert::stats_notification));

	std::srand((unsigned int)time(0));
	peer_id pid;
	std::generate(&pid[0], &pid[0] + 20, std::rand);
	ses1->set_peer_id(pid);
	std::generate(&pid[0], &pid[0] + 20, std::rand);
	ses2->set_peer_id(pid);
	assert(ses1->id() != ses2->id());
	if (ses3)
	{
		std::generate(&pid[0], &pid[0] + 20, std::rand);
		ses3->set_peer_id(pid);
		assert(ses3->id() != ses2->id());
	}

	boost::intrusive_ptr<torrent_info> t;
	if (torrent == 0)
	{
		error_code ec;
		create_directory("tmp1" + suffix, ec);
		std::ofstream file(combine_path("tmp1" + suffix, "temporary").c_str());
		t = ::create_torrent(&file, piece_size, 19, true, encrypted_torrent);
		file.close();
		if (clear_files)
		{
			remove_all(combine_path("tmp2" + suffix, "temporary"), ec);
			remove_all(combine_path("tmp3" + suffix, "temporary"), ec);
		}
		char ih_hex[41];
		to_hex((char const*)&t->info_hash()[0], 20, ih_hex);
		fprintf(stderr, "generated torrent: %s tmp1%s/temporary\n", ih_hex, suffix.c_str());
	}
	else
	{
		t = *torrent;
	}

	// they should not use the same save dir, because the
	// file pool will complain if two torrents are trying to
	// use the same files
	add_torrent_params param;
	param.flags &= ~add_torrent_params::flag_paused;
	param.flags &= ~add_torrent_params::flag_auto_managed;
	if (p) param = *p;
	param.ti = clone_ptr(t);
	param.save_path = "tmp1" + suffix;
	param.flags |= add_torrent_params::flag_seed_mode;
	error_code ec;
	torrent_handle tor1 = ses1->add_torrent(param, ec);
	tor1.super_seeding(super_seeding);

	// the downloader cannot use seed_mode
	param.flags &= ~add_torrent_params::flag_seed_mode;

	TEST_CHECK(!ses1->get_torrents().empty());
	torrent_handle tor2;
	torrent_handle tor3;

	if (ses3)
	{
		param.ti = clone_ptr(t);
		param.save_path = "tmp3" + suffix;
		tor3 = ses3->add_torrent(param, ec);
		TEST_CHECK(!ses3->get_torrents().empty());
	}

  	if (use_metadata_transfer)
	{
		param.ti = 0;
		param.info_hash = t->info_hash();
	}
	else
	{
		param.ti = clone_ptr(t);
	}
	param.save_path = "tmp2" + suffix;

	tor2 = ses2->add_torrent(param, ec);
	TEST_CHECK(!ses2->get_torrents().empty());

	assert(ses1->get_torrents().size() == 1);
	assert(ses2->get_torrents().size() == 1);

//	test_sleep(100);

	if (connect_peers)
	{
		fprintf(stderr, "connecting peer\n");
		error_code ec;
		tor1.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec)
			, ses2->listen_port()));

		if (ses3)
		{
			// give the other peers some time to get an initial
			// set of pieces before they start sharing with each-other
			tor3.connect_peer(tcp::endpoint(
				address::from_string("127.0.0.1", ec)
				, ses2->listen_port()));
			tor3.connect_peer(tcp::endpoint(
				address::from_string("127.0.0.1", ec)
				, ses1->listen_port()));
		}
	}

	return boost::make_tuple(tor1, tor2, tor3);
}

boost::asio::io_service* tracker_ios = 0;
boost::shared_ptr<libtorrent::thread> tracker_server;
libtorrent::mutex tracker_lock;
libtorrent::event tracker_initialized;

bool udp_failed = false;

void stop_tracker()
{
	fprintf(stderr, "stop_tracker()\n");
	if (tracker_server && tracker_ios)
	{
		tracker_ios->stop();
		tracker_server->join();
		tracker_server.reset();
		delete tracker_ios;
		tracker_ios = 0;
	}
	fprintf(stderr, "done\n");
}

void udp_tracker_thread(int* port);

int start_tracker()
{
	stop_tracker();

	{
		libtorrent::mutex::scoped_lock l(tracker_lock);
		tracker_initialized.clear(l);
	}

	int port = 0;

	tracker_server.reset(new libtorrent::thread(boost::bind(&udp_tracker_thread, &port)));

	{
		libtorrent::mutex::scoped_lock l(tracker_lock);
		tracker_initialized.wait(l);
	}
//	test_sleep(100);
	return port;
}

boost::detail::atomic_count g_udp_tracker_requests(0);
boost::detail::atomic_count g_http_tracker_requests(0);

void on_udp_receive(error_code const& ec, size_t bytes_transferred, udp::endpoint const* from, char* buffer, udp::socket* sock)
{
	if (ec)
	{
		fprintf(stderr, "UDP tracker, read failed: %s\n", ec.message().c_str());
		return;
	}

	udp_failed = false;

	if (bytes_transferred < 16)
	{
		fprintf(stderr, "UDP message too short\n");
		return;
	}

	fprintf(stderr, "UDP message %d bytes\n", int(bytes_transferred));

	char* ptr = buffer;
	detail::read_uint64(ptr);
	boost::uint32_t action = detail::read_uint32(ptr);
	boost::uint32_t transaction_id = detail::read_uint32(ptr);

	error_code e;

	switch (action)
	{
		case 0: // connect

			fprintf(stderr, "UDP connect\n");
			ptr = buffer;
			detail::write_uint32(0, ptr); // action = connect
			detail::write_uint32(transaction_id, ptr); // transaction_id
			detail::write_uint64(10, ptr); // connection_id
			sock->send_to(asio::buffer(buffer, 16), *from, 0, e);
			break;

		case 1: // announce

			fprintf(stderr, "UDP announce\n");
			ptr = buffer;
			detail::write_uint32(1, ptr); // action = announce
			detail::write_uint32(transaction_id, ptr); // transaction_id
			detail::write_uint32(1800, ptr); // interval
			detail::write_uint32(1, ptr); // incomplete
			detail::write_uint32(1, ptr); // complete
			++g_udp_tracker_requests;
			// 0 peers
			sock->send_to(asio::buffer(buffer, 20), *from, 0, e);
			break;
		case 2:
			// ignore scrapes
			fprintf(stderr, "UDP scrape\n");
			break;
		default:
			fprintf(stderr, "UDP unknown message: %d\n", action);
			break;
	}
}

void udp_tracker_thread(int* port)
{
	tracker_ios = new io_service;

	udp::socket acceptor(*tracker_ios);
	error_code ec;
	acceptor.open(udp::v4(), ec);
	if (ec)
	{
		fprintf(stderr, "Error opening listen UDP socket: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(tracker_lock);
		tracker_initialized.signal(l);
		return;
	}
	acceptor.bind(udp::endpoint(address_v4::any(), 0), ec);
	if (ec)
	{
		fprintf(stderr, "Error binding UDP socket to port 0: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(tracker_lock);
		tracker_initialized.signal(l);
		return;
	}
	*port = acceptor.local_endpoint().port();

	fprintf(stderr, "UDP tracker initialized on port %d\n", *port);

	{
		libtorrent::mutex::scoped_lock l(tracker_lock);
		tracker_initialized.signal(l);
	}

	char buffer[2000];

	for (;;)
	{
		error_code ec;
		udp::endpoint from;
		udp_failed = true;
		acceptor.async_receive_from(
			asio::buffer(buffer, sizeof(buffer)), from, boost::bind(
				&on_udp_receive, _1, _2, &from, &buffer[0], &acceptor));
		tracker_ios->run_one(ec);
		if (udp_failed) return;

		if (ec)
		{
			fprintf(stderr, "Error receiving on UDP socket: %s\n", ec.message().c_str());
			libtorrent::mutex::scoped_lock l(tracker_lock);
			tracker_initialized.signal(l);
			return;
		}
		tracker_ios->reset();
	}
}

boost::asio::io_service* web_ios = 0;
boost::shared_ptr<libtorrent::thread> web_server;
libtorrent::mutex web_lock;
libtorrent::event web_initialized;
bool stop_thread = false;

static void terminate_web_thread()
{
	stop_thread = true;
	web_ios->stop();
	web_ios = 0;
}

void stop_web_server()
{
	fprintf(stderr, "stop_web_server()\n");
	if (web_server && web_ios)
	{
		fprintf(stderr, "stopping web server thread\n");
		web_ios->post(&terminate_web_thread);
		web_server->join();
		web_server.reset();
	}
	remove("server.pem");
	fprintf(stderr, "done\n");
}

void web_server_thread(int* port, bool ssl, bool chunked);

int start_web_server(bool ssl, bool chunked_encoding)
{
	stop_web_server();

	stop_thread = false;

	{
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.clear(l);
	}

	if (ssl)
	{
		system("echo . > tmp");
		system("echo test province >>tmp");
		system("echo test city >> tmp");
		system("echo test company >> tmp");
		system("echo test department >> tmp");
		system("echo 127.0.0.1 >> tmp");
		system("echo test@test.com >> tmp");
		system("openssl req -new -x509 -keyout server.pem -out server.pem "
			"-days 365 -nodes <tmp");
	}

	int port = 0;

	web_server.reset(new libtorrent::thread(boost::bind(
		&web_server_thread, &port, ssl, chunked_encoding)));

	{
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.wait(l);
	}

	// create this directory so that the path
	// "relative/../test_file" can resolve
	error_code ec;
	create_directory("relative", ec);
//	test_sleep(100);
	return port;
}

void send_response(socket_type& s, error_code& ec
	, int code, char const* status_message, char const** extra_header
	, int len)
{
	char msg[600];
	int pkt_len = snprintf(msg, sizeof(msg), "HTTP/1.1 %d %s\r\n"
		"content-length: %d\r\n"
		"%s"
		"%s"
		"%s"
		"%s"
		"\r\n"
		, code, status_message, len
		, extra_header[0]
		, extra_header[1]
		, extra_header[2]
		, extra_header[3]);
	DLOG(stderr, ">> %s\n", msg);
	write(s, boost::asio::buffer(msg, pkt_len), boost::asio::transfer_all(), ec);
	if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
}

void on_accept(error_code& accept_ec, error_code const& ec, bool* done)
{
	accept_ec = ec;
	*done = true;
}

void send_content(socket_type& s, char const* file, int size, bool chunked)
{
	error_code ec;
	if (chunked)
	{
		int chunk_size = 13;
		char head[20];
		std::vector<boost::asio::const_buffer> bufs(3);
		bufs[2] = asio::const_buffer("\r\n", 2);
		while (chunk_size > 0)
		{
			chunk_size = std::min(chunk_size, size);
			int len = snprintf(head, sizeof(head), "%x\r\n", chunk_size);
			bufs[0] = asio::const_buffer(head, len);
			if (chunk_size == 0)
			{
				// terminate
				bufs.erase(bufs.begin()+1);
			}
			else
			{
				bufs[1] = asio::const_buffer(file, chunk_size);
			}
			write(s, bufs, boost::asio::transfer_all(), ec);
			if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
			size -= chunk_size;
			file += chunk_size;
			chunk_size *= 2;
		}
	}
	else
	{
		write(s, boost::asio::buffer(file, size), boost::asio::transfer_all(), ec);
		if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
	}
}

void on_read(error_code const& ec, size_t bytes_transferred, size_t* bt, error_code* e, bool* done)
{
	DLOG(stderr, "on_read %d [ ec: %s ]\n", int(bytes_transferred), ec.message().c_str());
	*bt = bytes_transferred;
	*e = ec;
	*done = true;
}

void on_read_timeout(error_code const& ec, bool* timed_out)
{
	if (ec) return;
	fprintf(stderr, "read timed out\n");
	*timed_out = true;
}

void web_server_thread(int* port, bool ssl, bool chunked)
{
	io_service ios;
	socket_acceptor acceptor(ios);
	error_code ec;
	acceptor.open(tcp::v4(), ec);
	if (ec)
	{
		fprintf(stderr, "Error opening listen socket: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.signal(l);
		return;
	}
	acceptor.set_option(socket_acceptor::reuse_address(true), ec);
	if (ec)
	{
		fprintf(stderr, "Error setting listen socket to reuse addr: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.signal(l);
		return;
	}
	acceptor.bind(tcp::endpoint(address_v4::any(), 0), ec);
	if (ec)
	{
		fprintf(stderr, "Error binding listen socket to port 0: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.signal(l);
		return;
	}
	*port = acceptor.local_endpoint().port();
	acceptor.listen(10, ec);
	if (ec)
	{
		fprintf(stderr, "Error listening on socket: %s\n", ec.message().c_str());
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.signal(l);
		return;
	}

	web_ios = &ios;

	char buf[10000];
	int len = 0;
	int offset = 0;
	bool connection_close = false;
	socket_type s(ios);
	void* ctx = 0;
#ifdef TORRENT_USE_OPENSSL
	boost::asio::ssl::context ssl_ctx(ios, boost::asio::ssl::context::sslv23_server);
	if (ssl)
	{
		ssl_ctx.use_certificate_chain_file("server.pem");
		ssl_ctx.use_private_key_file("server.pem", asio::ssl::context::pem);
		ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none);

		ctx = &ssl_ctx;
	}
#endif

	proxy_settings p;
	instantiate_connection(ios, p, s, ctx);

	fprintf(stderr, "web server initialized on port %d%s\n", *port, ssl ? " [SSL]" : "");

	{
		libtorrent::mutex::scoped_lock l(web_lock);
		web_initialized.signal(l);
	}

	for (;;)
	{
		if (connection_close)
		{
			error_code ec;
#ifdef TORRENT_USE_OPENSSL
			if (ssl)
			{
				DLOG(stderr, "shutting down SSL connection\n");
				s.get<ssl_stream<stream_socket> >()->shutdown(ec);
				if (ec) fprintf(stderr, "SSL shutdown failed: %s\n", ec.message().c_str());
				ec.clear();
			}
#endif
			DLOG(stderr, "closing connection\n");
			s.close(ec);
			if (ec) fprintf(stderr, "close failed: %s\n", ec.message().c_str());
			connection_close = false;
		}

		if (!s.is_open())
		{
			len = 0;
			offset = 0;

			error_code ec;
			instantiate_connection(ios, p, s, ctx);
			stream_socket* sock;
#ifdef TORRENT_USE_OPENSSL
			if (ssl) sock = &s.get<ssl_stream<stream_socket> >()->next_layer();
			else
#endif
			sock = s.get<stream_socket>();

			bool accept_done = false;
			DLOG(stderr, "waiting for incoming connection\n");
			acceptor.async_accept(*sock, boost::bind(&on_accept, boost::ref(ec), _1, &accept_done));
			while (!accept_done)
			{
				error_code e;
				ios.reset();
				if (stop_thread || ios.run_one(e) == 0)
				{
					fprintf(stderr, "io_service stopped: %s\n", e.message().c_str());
					break;
				}
			}
			if (stop_thread) break;

			if (ec)
			{
				fprintf(stderr, "accept failed: %s\n", ec.message().c_str());
				return;
			}
			DLOG(stderr, "accepting incoming connection\n");
			if (!s.is_open())
			{
				fprintf(stderr, "incoming connection closed\n");
				continue;
			}

#ifdef TORRENT_USE_OPENSSL
			if (ssl)
			{
				DLOG(stderr, "SSL handshake\n");
				s.get<ssl_stream<stream_socket> >()->accept_handshake(ec);
				if (ec)
				{
					fprintf(stderr, "SSL handshake failed: %s\n", ec.message().c_str());
					connection_close = true;
					continue;
				}
			}
#endif

		}

		http_parser p;
		bool failed = false;

		do
		{
			p.reset();
			bool error = false;

			p.incoming(buffer::const_interval(buf + offset, buf + len), error);

			char const* extra_header[4] = {"","","",""};

			TEST_CHECK(error == false);
			if (error)
			{
				fprintf(stderr, "parse failed\n");
				failed = true;
				break;
			}

			while (!p.finished())
			{
				TORRENT_ASSERT(len < int(sizeof(buf)));
				size_t received = 0;
				bool done = false;
				bool timed_out = false;
				DLOG(stderr, "async_read_some %d bytes [ len: %d ]\n", int(sizeof(buf) - len), len);
				s.async_read_some(boost::asio::buffer(&buf[len]
					, sizeof(buf) - len), boost::bind(&on_read, _1, _2, &received, &ec, &done));
				deadline_timer timer(ios);
				timer.expires_at(time_now_hires() + seconds(2));
				timer.async_wait(boost::bind(&on_read_timeout, _1, &timed_out));

				while (!done && !timed_out)
				{
					error_code e;
					ios.reset();
					if (stop_thread || ios.run_one(e) == 0)
					{
						fprintf(stderr, "io_service stopped: %s\n", e.message().c_str());
						break;
					}
				}
				if (timed_out)
				{
					fprintf(stderr, "read timed out, closing connection\n");
					failed = true;
					break;
				}
//				fprintf(stderr, "read: %d\n", int(received));

				if (ec || received <= 0)
				{
					fprintf(stderr, "read failed: \"%s\" (%s) received: %d\n"
						, ec.message().c_str(), ec.category().name(), int(received));
					failed = true;
					break;
				}

				timer.cancel(ec);
				if (ec)
					fprintf(stderr, "timer.cancel failed: %s\n", ec.message().c_str());

				len += received;

				p.incoming(buffer::const_interval(buf + offset, buf + len), error);

				TEST_CHECK(error == false);
				if (error)
				{
					fprintf(stderr, "parse failed\n");
					failed = true;
					break;
				}
			}

			std::string connection = p.header("connection");
			std::string via = p.header("via");

			// The delegate proxy doesn't say connection close, but it expects it to be closed
			// the Via: header is an indicator of delegate making the request
			if (connection == "close" || !via.empty())
			{
				DLOG(stderr, "*** got connection close\n");
				connection_close = true;
			}

			if (p.protocol() == "HTTP/1.0")
			{
				DLOG(stderr, "*** HTTP/1.0, closing connection when done\n");
				connection_close = true;
			}

			DLOG(stderr, "REQ: %s", std::string(buf + offset, p.body_start()).c_str());

			if (failed)
			{
				fprintf(stderr, "*** connection failed\n");
				connection_close = true;
				break;
			}

			offset += int(p.body_start() + p.content_length());
//			fprintf(stderr, "offset: %d len: %d\n", offset, len);

			if (p.method() != "get" && p.method() != "post")
			{
				fprintf(stderr, "*** incorrect method: %s\n", p.method().c_str());
				connection_close = true;
				break;
			}

			std::string path = p.path();
			fprintf(stderr, "%s\n", path.c_str());

			if (path == "/redirect")
			{
				extra_header[0] = "Location: /test_file\r\n";
				send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
				break;
			}

			if (path == "/infinite_redirect")
			{
				extra_header[0] = "Location: /infinite_redirect\r\n";
				send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
				break;
			}

			if (path == "/relative/redirect")
			{
				extra_header[0] = "Location: ../test_file\r\n";
				send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
				break;
			}

			if (path.substr(0, 9) == "/announce")
			{
				fprintf(stderr, "%s\n", path.c_str());
				entry announce;
				announce["interval"] = 1800;
				announce["complete"] = 1;
				announce["incomplete"] = 1;
				announce["peers"].string();
				std::vector<char> buf;
				bencode(std::back_inserter(buf), announce);
				++g_http_tracker_requests;

				send_response(s, ec, 200, "OK", extra_header, buf.size());
				write(s, boost::asio::buffer(&buf[0], buf.size()), boost::asio::transfer_all(), ec);
				if (ec)
					fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
			}

			if (path.substr(0, 6) == "/seed?")
			{
				char const* piece = strstr(path.c_str(), "&piece=");
				if (piece == 0) piece = strstr(path.c_str(), "?piece=");
				if (piece == 0)
				{
					fprintf(stderr, "invalid web seed request: %s\n", path.c_str());
					break;
				}
				boost::uint64_t idx = atoi(piece + 7);
				char const* range = strstr(path.c_str(), "&ranges=");
				if (range == 0) range = strstr(path.c_str(), "?ranges=");
				int range_end = 0;
				int range_start = 0;
				if (range)
				{
					range_start = atoi(range + 8);
					range = strchr(range, '-');
					if (range == 0)
					{
						fprintf(stderr, "invalid web seed request: %s\n", path.c_str());
						break;
					}
					range_end = atoi(range + 1);
				}
				else
				{
					range_start = 0;
					// assume piece size of 64kiB
					range_end = 64*1024+1;
				}

				int size = range_end - range_start + 1;
				boost::uint64_t off = idx * 64 * 1024 + range_start;
				std::vector<char> file_buf;
				error_code ec;
				int res = load_file(combine_path("tmp1_web_seed", "seed"), file_buf, ec);

				if (res == -1 || file_buf.empty())
				{
					send_response(s, ec, 404, "Not Found", extra_header, 0);
					continue;
				}
				send_response(s, ec, 200, "OK", extra_header, size);
				DLOG(stderr, "sending %d bytes of payload [%d, %d) piece: %d\n"
					, size, int(off), int(off + size), int(idx));
				write(s, boost::asio::buffer(&file_buf[0] + off, size)
					, boost::asio::transfer_all(), ec);
				if (ec)
					fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
				else
				{
					DLOG(stderr, "*** done\n");
				}

				memmove(buf, buf + offset, len - offset);
				len -= offset;
				offset = 0;
				continue;
			}

//			fprintf(stderr, ">> serving file %s\n", path.c_str());
			std::vector<char> file_buf;
			// remove the / from the path
			path = path.substr(1);
			error_code ec;
			int res = load_file(path, file_buf, ec);
			if (res == -1)
			{
				fprintf(stderr, ">> file not found: %s\n", path.c_str());
				send_response(s, ec, 404, "Not Found", extra_header, 0);
				continue;
			}

			if (res != 0)
			{
				// this means the file was either too big or couldn't be read
				fprintf(stderr, ">> file too big: %s\n", path.c_str());
				send_response(s, ec, 503, "Internal Error", extra_header, 0);
				continue;
			}

			// serve file

			if (extension(path) == ".gz")
			{
				extra_header[0] = "Content-Encoding: gzip\r\n";
			}

			if (chunked)
			{
				extra_header[2] = "Transfer-Encoding: chunked\r\n";
			}

			if (!p.header("range").empty())
			{
				std::string range = p.header("range");
				int start, end;
				sscanf(range.c_str(), "bytes=%d-%d", &start, &end);
				char eh[400];
				snprintf(eh, sizeof(eh), "Content-Range: bytes %d-%d\r\n", start, end);
				extra_header[1] = eh;
				if (end - start + 1 >= 1000)
				{
					fprintf(stderr, "request size: %.2f kB\n", int(end - start + 1)/1000.f);
				}
				else
				{
					fprintf(stderr, "request size: %d Bytes\n", int(end - start + 1));
				}
				send_response(s, ec, 206, "Partial", extra_header, end - start + 1);
				if (!file_buf.empty())
				{
					send_content(s, &file_buf[0] + start, end - start + 1, chunked);
				}
				DLOG(stderr, "send %d bytes of payload\n", end - start + 1);
			}
			else
			{
				send_response(s, ec, 200, "OK", extra_header, file_buf.size());
				if (!file_buf.empty())
					send_content(s, &file_buf[0], file_buf.size(), chunked);
			}
			DLOG(stderr, "%d bytes left in receive buffer. offset: %d\n", len - offset, offset);
			memmove(buf, buf + offset, len - offset);
			len -= offset;
			offset = 0;
		} while (offset < len);
	}

	web_ios = 0;
	fprintf(stderr, "exiting web server thread\n");
}

ext: cpp

max_stars_repo_name: majestrate/twister-core

max_issues_repo_name: majestrate/twister-core

max_forks_repo_name: twisterarmy/twister-core


text:
/*
 * Copyright (c) CERN 2013-2015
 *
 * Copyright (c) Members of the EMI Collaboration. 2010-2013
 *  See  http://www.eu-emi.eu/partners for details on the copyright
 *  holders.
 *
 * 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 <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <fstream>
#include <boost/filesystem.hpp>
#include "Transfer.h"
#include "UrlCopyOpts.h"

const option UrlCopyOpts::long_options[] =
{
    {"job-id",            required_argument, 0, 100},
    {"file-id",           required_argument, 0, 101},
    {"source",            required_argument, 0, 102},
    {"destination",       required_argument, 0, 103},
    {"user-filesize",     required_argument, 0, 104},
    {"bulk-file",         required_argument, 0, 105},

    {"reuse",             no_argument,       0, 200},
    {"job_m_replica",     no_argument,       0, 202},
    {"last_replica",      no_argument,       0, 203},

    {"checksum",          required_argument, 0, 300},
    {"checksum-mode",     required_argument, 0, 301},
    {"strict-copy",       no_argument,       0, 302},

    {"token-bringonline", required_argument, 0, 400},
    {"dest-token-desc",   required_argument, 0, 401},
    {"source-token-desc", required_argument, 0, 402},

    {"vo",                required_argument, 0, 500},
    {"user-dn",           required_argument, 0, 501},
    {"proxy",             required_argument, 0, 502},
    {"oauth",             required_argument, 0, 503},
    {"source-issuer",     required_argument, 0, 504},
    {"dest-issuer",       required_argument, 0, 505},
	{"authMethod",        required_argument, 0, 506},

    {"infosystem",        required_argument, 0, 600},
    {"alias",             required_argument, 0, 601},
    {"monitoring",        no_argument,       0, 602},
    {"active",            required_argument, 0, 603},

    {"file-metadata",     required_argument, 0, 700},
    {"job-metadata",      required_argument, 0, 701},

    {"level",             required_argument, 0, 801},
    {"overwrite",         no_argument,       0, 802},
    {"nstreams",          required_argument, 0, 803},
    {"tcp-buffersize",    required_argument, 0, 804},
    {"timeout",           required_argument, 0, 805},
    {"udt",               no_argument,       0, 806},
    {"ipv6",              no_argument,       0, 807},
    {"sec-per-mb",        required_argument, 0, 808},
    {"ipv4",              no_argument,       0, 809},
    {"no-delegation",     no_argument,       0, 810},
    {"no-streaming",      no_argument,       0, 811},

    {"retry",             required_argument, 0, 820},
    {"retry_max-max",     required_argument, 0, 821},

    {"logDir",            required_argument, 0, 900},
    {"msgDir",            required_argument, 0, 901},

    {"help",              no_argument,       0, 0},
    {"debug",             required_argument, 0, 1},
    {"stderr",            no_argument,       0, 2},
    {0, 0, 0, 0}
};

const char UrlCopyOpts::short_options[] = "";

static void setChecksum(Transfer &transfer, const std::string &checksum)
{
    if (checksum == "x") {
        return;
    }

    size_t colon = checksum.find(':');
    if (colon == std::string::npos) {
        transfer.checksumAlgorithm.assign(checksum);
        transfer.checksumValue.clear();
    }
    else {
        transfer.checksumAlgorithm.assign(checksum.substr(0, colon));
        transfer.checksumValue.assign(checksum.substr(colon + 1));
    }
}

// Create a transfer out of a string
static Transfer createFromString(const Transfer &reference, const std::string &line)
{
    typedef boost::tokenizer <boost::char_separator<char>> tokenizer;

    std::string strArray[7];
    tokenizer tokens(line, boost::char_separator<char>(" "));
    std::copy(tokens.begin(), tokens.end(), strArray);

    Transfer t;
    t.jobId = reference.jobId;
    t.fileId = boost::lexical_cast<uint64_t>(strArray[0]);
    t.source = Uri::parse(strArray[1]);
    t.destination = Uri::parse(strArray[2]);
    t.checksumMode = reference.checksumMode;
    setChecksum(t, strArray[3]);
    t.userFileSize = boost::lexical_cast<uint64_t>(strArray[4]);
    t.fileMetadata = strArray[5];
    t.tokenBringOnline = strArray[6];
    if (t.tokenBringOnline == "x") {
        t.tokenBringOnline.clear();
    }
    t.sourceTokenDescription = reference.sourceTokenDescription;
    t.destTokenDescription = reference.destTokenDescription;
    t.sourceTokenIssuer = reference.sourceTokenIssuer;
    t.destTokenIssuer = reference.destTokenIssuer;
    t.isMultipleReplicaJob = false;
    t.isLastReplica = false;
    return t;
}

// Initialize a list from a file
static Transfer::TransferList initListFromFile(const Transfer &reference, const std::string &path)
{
    std::string line;
    std::ifstream infile(path.c_str(), std::ios_base::in);

    Transfer::TransferList list;
    while (std::getline(infile, line, '\n')) {
        if (!line.empty()) {
            list.push_back(createFromString(reference, line));
        }
    }

    infile.close();

    try {
        boost::filesystem::remove(path.c_str());
    }
    catch (const boost::filesystem::filesystem_error &ex) {
        std::cerr << "Failed to clean up the bulk file! " << ex.what() << std::endl;
    }

    return list;
}

UrlCopyOpts::UrlCopyOpts():
    isSessionReuse(false), isMultipleReplicaJob(false),
    strictCopy(false),
    optimizerLevel(0), overwrite(false), noDelegation(false), nStreams(0), tcpBuffersize(0),
    timeout(0), enableUdt(false), enableIpv6(boost::indeterminate), addSecPerMb(0),
    noStreaming(false), enableMonitoring(false), active(0), retry(0), retryMax(0),
    logDir("/var/log/fts3"), msgDir("/var/lib/fts3"),
    debugLevel(0), logToStderr(false)
{
}

void UrlCopyOpts::usage(const std::string &bin)
{
    std::cout << "Usage: " << bin << " [options]" << std::endl
    << "Options: " << std::endl;
    for (int i = 0; long_options[i].name; ++i) {
        std::cout << "\t--" << long_options[i].name
        << ",-" << static_cast<char>(long_options[i].val)
        << std::endl;
    }
    exit(0);
}

void UrlCopyOpts::parse(int argc, char * const argv[])
{
    int opt;
    Transfer referenceTransfer;

    try {
        while ((opt = getopt_long_only(argc, argv, short_options, long_options, NULL)) > -1) {
            switch (opt) {
                case 0:
                    usage(argv[0]);
                    break;
                case 1:
                    if (optarg) {
                        debugLevel = boost::lexical_cast<unsigned>(optarg);
                    }
                    else {
                        debugLevel = 0;
                    }
                    break;
                case 2:
                    logToStderr = true;
                    break;

                case 100:
                    referenceTransfer.jobId = optarg;
                    break;
                case 101:
                    referenceTransfer.fileId = boost::lexical_cast<unsigned long long>(optarg);
                    break;
                case 102:
                    referenceTransfer.source = Uri::parse(optarg);
                    break;
                case 103:
                    referenceTransfer.destination = Uri::parse(optarg);
                    break;
                case 104:
                    referenceTransfer.userFileSize = boost::lexical_cast<long long>(optarg);
                    break;
                case 105:
                    bulkFile = boost::lexical_cast<std::string>(optarg);
                    break;

                case 200:
                    isSessionReuse = true;
                    break;
                case 202:
                    referenceTransfer.isMultipleReplicaJob = true;
                    break;
                case 203:
                    referenceTransfer.isLastReplica = true;
                    break;

                case 300:
                    setChecksum(referenceTransfer, optarg);
                    break;
                case 301:
                    if (strncmp("target", optarg, strlen("target")) == 0 || strncmp("t", optarg, strlen("t")) == 0) {
                        referenceTransfer.checksumMode = Transfer::CHECKSUM_TARGET;
                    }
                    else if (strncmp("source", optarg, strlen("source")) == 0 || strncmp("s", optarg, strlen("s")) == 0) {
                        referenceTransfer.checksumMode = Transfer::CHECKSUM_SOURCE;
                    }
                    else if (strncmp("both", optarg, strlen("both")) == 0 || strncmp("b", optarg, strlen("b")) == 0) {
                        referenceTransfer.checksumMode = Transfer::CHECKSUM_BOTH;
                    }
                    else {
            			referenceTransfer.checksumMode = Transfer::CHECKSUM_NONE;
                    }
                    break;
                case 302:
                    strictCopy = true;
                    break;

                case 400:
                    referenceTransfer.tokenBringOnline = optarg;
                    break;
                case 401:
                    referenceTransfer.destTokenDescription = optarg;
                    break;
                case 402:
                    referenceTransfer.sourceTokenDescription = optarg;
                    break;

                case 500:
                    voName = optarg;
                    break;
                case 501:
                    userDn = optarg;
                    break;
                case 502:
                    proxy = optarg;
                    break;
                case 503:
                    oauthFile = optarg;
                    break;
                case 504:
                    referenceTransfer.sourceTokenIssuer = optarg;
                    break;
                case 505:
                    referenceTransfer.destTokenIssuer = optarg;
                    break;
                case 506:
                	authMethod = optarg;
                    break;
                case 600:
                    infosys = optarg;
                    break;
                case 601:
                    alias = optarg;
                    break;
                case 602:
                    enableMonitoring = true;
                    break;
                case 603:
                    active = boost::lexical_cast<unsigned>(optarg);
                    break;

                case 700:
                    referenceTransfer.fileMetadata = optarg;
                    break;
                case 701:
                    jobMetadata = optarg;
                    break;

                case 801:
                    optimizerLevel = boost::lexical_cast<int>(optarg);
                    break;
                case 802:
                    overwrite = true;
                    break;
                case 803:
                    nStreams = boost::lexical_cast<int>(optarg);
                    break;
                case 804:
                    tcpBuffersize = boost::lexical_cast<unsigned>(optarg);
                    break;
                case 805:
                    timeout = boost::lexical_cast<unsigned>(optarg);
                    break;
                case 806:
                    enableUdt = true;
                    break;
                case 807:
                    enableIpv6 = true;
                    break;
                case 808:
                    addSecPerMb = boost::lexical_cast<int>(optarg);
                    break;
                case 809:
                    enableIpv6 = false;
                    break;
                case 810:
                    noDelegation = true;
                    break;
                case 811:
                    noStreaming = true;
                    break;

                case 820:
                    retry = boost::lexical_cast<int>(optarg);
                    break;
                case 821:
                    retryMax = boost::lexical_cast<int>(optarg);
                    break;

                case 900:
                    logDir = boost::lexical_cast<std::string>(optarg);
                    break;
                case 901:
                    msgDir = boost::lexical_cast<std::string>(optarg);
                    break;

                default:
                    usage(argv[0]);
            }
        }
    }
    catch (boost::bad_lexical_cast &e) {
        std::cerr << "Expected a numeric value for option -" << static_cast<char>(opt) << std::endl;
        exit(-1);
    }

    if (bulkFile.empty() &&
        (!referenceTransfer.source.fullUri.empty() && !referenceTransfer.destination.fullUri.empty())) {
        transfers.push_back(referenceTransfer);
    }
    else {
        try {
            transfers = initListFromFile(referenceTransfer, bulkFile);
        }
        catch (const std::exception &ex) {
            std::cerr << "Failed to parse the bulk file! " << ex.what() << std::endl;
            exit(-1);
        }
    }

    validateRequired();
}

void UrlCopyOpts::validateRequired()
{
    if (transfers.empty()) {
        std::cerr << "Specify --source and --destination, or --bulk-file" << std::endl;
        exit(-1);
    }
}


text:
#if !defined(BALSA_EIGEN_TYPES_HPP)
#define BALSA_EIGEN_TYPES_HPP

// Verbose because clang/llvm pretend to have gnuc
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wint-in-bool-context"
#endif
#include <Eigen/Dense>
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic pop
#endif

namespace balsa::eigen {
template<typename T, int A, int B>
using Matrix = Eigen::Matrix<T, A, B>;

template<typename T, int A>
using SquareMatrix = Matrix<T, A, A>;
template<typename T>
using MatrixX = SquareMatrix<T, Eigen::Dynamic>;

template<typename T, int D>
using Vector = Matrix<T, D, 1>;
template<typename T, int D>
using RowVector = Matrix<T, 1, D>;
template<typename T>
using VectorX = Vector<T, Eigen::Dynamic>;
template<typename T>
using RowVectorX = RowVector<T, Eigen::Dynamic>;

template<typename T>
using Vector2 = Vector<T, 2>;
template<typename T>
using RowVector2 = RowVector<T, 2>;

template<typename T>
using Vector3 = Vector<T, 3>;
template<typename T>
using RowVector3 = RowVector<T, 3>;

template<typename T>
using Vector4 = Vector<T, 4>;
template<typename T>
using RowVector4 = RowVector<T, 4>;

template<typename T, int D>
using ColVectors = Matrix<T, D, Eigen::Dynamic>;
template<typename T, int D>
using RowVectors = Matrix<T, Eigen::Dynamic, D>;

using Mat3i = SquareMatrix<int, 3>;
using Mat2i = SquareMatrix<int, 2>;
using ColVecs4i = ColVectors<int, 4>;
using ColVecs3i = ColVectors<int, 3>;
using ColVecs2i = ColVectors<int, 2>;
using RowVecs3i = RowVectors<int, 3>;
using RowVecs2i = RowVectors<int, 2>;
using Vec3i = Vector<int, 3>;
using Vec2i = Vector<int, 2>;
using MatXi = MatrixX<int>;
using VecXi = VectorX<int>;
using RowVecXi = RowVectorX<int>;

using Mat4f = SquareMatrix<float, 4>;
using Mat3f = SquareMatrix<float, 3>;
using Mat2f = SquareMatrix<float, 2>;
using ColVecs4f = ColVectors<float, 4>;
using ColVecs3f = ColVectors<float, 3>;
using ColVecs2f = ColVectors<float, 2>;
using RowVecs3f = RowVectors<float, 3>;
using RowVecs2f = RowVectors<float, 2>;
using Vec4f = Vector<float, 4>;
using Vec3f = Vector<float, 3>;
using Vec2f = Vector<float, 2>;
using MatXf = MatrixX<float>;
using VecXf = VectorX<float>;
using RowVecXf = RowVectorX<float>;

using Mat4d = SquareMatrix<double, 4>;
using Mat3d = SquareMatrix<double, 3>;
using Mat2d = SquareMatrix<double, 2>;
using ColVecs4d = ColVectors<double, 4>;
using ColVecs3d = ColVectors<double, 3>;
using ColVecs2d = ColVectors<double, 2>;
using RowVecs3d = RowVectors<double, 3>;
using RowVecs2d = RowVectors<double, 2>;
using Vec4d = Vector<double, 4>;
using Vec3d = Vector<double, 3>;
using Vec2d = Vector<double, 2>;
using MatXd = MatrixX<double>;
using VecXd = VectorX<double>;
using RowVecXd = RowVectorX<double>;

using Vec2i = Vector2<int>;
using Vec3i = Vector3<int>;
using Vec4i = Vector4<int>;

}// namespace balsa::eigen

#endif

max_stars_repo_name: mtao/balsa

max_issues_repo_name: mtao/balsa

max_forks_repo_name: mtao/balsa


text:
/**
 * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved.
 * http://soramitsu.co.jp
 *
 * 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.
 */

#ifndef IROHA_TX_RESPONSE_HPP
#define IROHA_TX_RESPONSE_HPP

#include <boost/variant.hpp>
#include "interfaces/base/primitive.hpp"
#include "interfaces/transaction.hpp"
#include "interfaces/transaction_responses/committed_tx_response.hpp"
#include "interfaces/transaction_responses/not_received_tx_response.hpp"
#include "interfaces/transaction_responses/stateful_failed_tx_response.hpp"
#include "interfaces/transaction_responses/stateful_valid_tx_response.hpp"
#include "interfaces/transaction_responses/stateless_failed_tx_response.hpp"
#include "interfaces/transaction_responses/stateless_valid_tx_response.hpp"
#include "utils/polymorphic_wrapper.hpp"
#include "utils/visitor_apply_for_all.hpp"

#ifndef DISABLE_BACKWARD
#include "model/transaction_response.hpp"
#endif

namespace shared_model {
  namespace interface {
    /**
     * TransactionResponse is a status of transaction in system
     */
    class TransactionResponse : public PRIMITIVE(TransactionResponse) {
     private:
      /// PolymorphicWrapper shortcut type
      template <typename... Value>
      using wrap = boost::variant<detail::PolymorphicWrapper<Value>...>;

     public:
      /// Type of variant, that handle all concrete tx responses in the system
      using ResponseVariantType = wrap<StatelessFailedTxResponse,
                                       StatelessValidTxResponse,
                                       StatefulFailedTxResponse,
                                       StatefulValidTxResponse,
                                       CommittedTxResponse,
                                       NotReceivedTxResponse>;

      /// Type with list of types in ResponseVariantType
      using ResponseListType = ResponseVariantType::types;

      /**
       * @return hash of corresponding transaction
       */
      virtual const interface::types::HashType &transactionHash() const = 0;

      /**
       * @return attached concrete tx response
       */
      virtual const ResponseVariantType &get() const = 0;

      // ------------------------| Primitive override |-------------------------

      std::string toString() const override {
        return boost::apply_visitor(detail::ToStringVisitor(), get());
      }

#ifndef DISABLE_BACKWARD
      OldModelType *makeOldModel() const override {
        auto response = boost::apply_visitor(
            detail::OldModelCreatorVisitor<OldModelType *>(), get());
        response->tx_hash = crypto::toBinaryString(transactionHash());
        return response;
      }

#endif

      bool operator==(const ModelType &rhs) const override {
        return transactionHash() == rhs.transactionHash()
            and get() == rhs.get();
      }
    };
  }  // namespace interface
}  // namespace shared_model
#endif  // IROHA_TX_RESPONSE_HPP

max_stars_repo_name: steephengeorge/iroha

max_issues_repo_name: steephengeorge/iroha

max_forks_repo_name: steephengeorge/iroha