text:
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/common.hpp>
#include <nano/node/lmdb/lmdb.hpp>
#include <nano/node/lmdb/lmdb_iterator.hpp>
#include <nano/node/lmdb/wallet_value.hpp>
#include <nano/secure/buffer.hpp>
#include <nano/secure/versioning.hpp>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/polymorphic_cast.hpp>

#include <queue>

namespace nano
{
template <>
void * mdb_val::data () const
{
	return value.mv_data;
}

template <>
size_t mdb_val::size () const
{
	return value.mv_size;
}

template <>
mdb_val::db_val (size_t size_a, void * data_a) :
	value ({ size_a, data_a })
{
}

template <>
void mdb_val::convert_buffer_to_value ()
{
	value = { buffer->size (), const_cast<uint8_t *> (buffer->data ()) };
}
}

nano::mdb_store::mdb_store (nano::logger_mt & logger_a, boost::filesystem::path const & path_a, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, bool backup_before_upgrade_a) :
	logger (logger_a),
	env (error, path_a, nano::mdb_env::options::make ().set_config (lmdb_config_a).set_use_no_mem_init (true)),
	mdb_txn_tracker (logger_a, txn_tracking_config_a, block_processor_batch_max_time_a),
	txn_tracking_enabled (txn_tracking_config_a.enable)
{
	if (!error)
	{
		auto is_fully_upgraded (false);
		auto is_fresh_db (false);
		{
			auto transaction (tx_begin_read ());
			auto err = mdb_dbi_open (env.tx (transaction), "meta", 0, &meta);
			is_fresh_db = err != MDB_SUCCESS;
			if (err == MDB_SUCCESS)
			{
				is_fully_upgraded = (version_get (transaction) == version);
				mdb_dbi_close (env, meta);
			}
		}

		// Only open a write lock when upgrades are needed. This is because CLI commands
		// open inactive nodes which can otherwise be locked here if there is a long write
		// (can be a few minutes with the --fast_bootstrap flag for instance)
		if (!is_fully_upgraded)
		{
			nano::network_constants network_constants;
			if (!is_fresh_db)
			{
				if (!network_constants.is_dev_network ())
				{
					std::cout << "Upgrade in progress..." << std::endl;
				}
				if (backup_before_upgrade_a)
				{
					create_backup_file (env, path_a, logger_a);
				}
			}
			auto needs_vacuuming = false;
			{
				auto transaction (tx_begin_write ());
				open_databases (error, transaction, MDB_CREATE);
				if (!error)
				{
					error |= do_upgrades (transaction, needs_vacuuming);
				}
			}

			if (needs_vacuuming && !network_constants.is_dev_network ())
			{
				logger.always_log ("Preparing vacuum...");
				auto vacuum_success = vacuum_after_upgrade (path_a, lmdb_config_a);
				logger.always_log (vacuum_success ? "Vacuum succeeded." : "Failed to vacuum. (Optional) Ensure enough disk space is available for a copy of the database and try to vacuum after shutting down the node");
			}
		}
		else
		{
			auto transaction (tx_begin_read ());
			open_databases (error, transaction, 0);
		}
	}
}

bool nano::mdb_store::vacuum_after_upgrade (boost::filesystem::path const & path_a, nano::lmdb_config const & lmdb_config_a)
{
	// Vacuum the database. This is not a required step and may actually fail if there isn't enough storage space.
	auto vacuum_path = path_a.parent_path () / "vacuumed.ldb";

	auto vacuum_success = copy_db (vacuum_path);
	if (vacuum_success)
	{
		// Need to close the database to release the file handle
		mdb_env_sync (env.environment, true);
		mdb_env_close (env.environment);
		env.environment = nullptr;

		// Replace the ledger file with the vacuumed one
		boost::filesystem::rename (vacuum_path, path_a);

		// Set up the environment again
		auto options = nano::mdb_env::options::make ()
					   .set_config (lmdb_config_a)
					   .set_use_no_mem_init (true);
		env.init (error, path_a, options);
		if (!error)
		{
			auto transaction (tx_begin_read ());
			open_databases (error, transaction, 0);
		}
	}
	else
	{
		// The vacuum file can be in an inconsistent state if there wasn't enough space to create it
		boost::filesystem::remove (vacuum_path);
	}
	return vacuum_success;
}

void nano::mdb_store::serialize_mdb_tracker (boost::property_tree::ptree & json, std::chrono::milliseconds min_read_time, std::chrono::milliseconds min_write_time)
{
	mdb_txn_tracker.serialize_json (json, min_read_time, min_write_time);
}

void nano::mdb_store::serialize_memory_stats (boost::property_tree::ptree & json)
{
	MDB_stat stats;
	auto status (mdb_env_stat (env.environment, &stats));
	release_assert (status == 0);
	json.put ("branch_pages", stats.ms_branch_pages);
	json.put ("depth", stats.ms_depth);
	json.put ("entries", stats.ms_entries);
	json.put ("leaf_pages", stats.ms_leaf_pages);
	json.put ("overflow_pages", stats.ms_overflow_pages);
	json.put ("page_size", stats.ms_psize);
}

nano::write_transaction nano::mdb_store::tx_begin_write (std::vector<nano::tables> const &, std::vector<nano::tables> const &)
{
	return env.tx_begin_write (create_txn_callbacks ());
}

nano::read_transaction nano::mdb_store::tx_begin_read () const
{
	return env.tx_begin_read (create_txn_callbacks ());
}

std::string nano::mdb_store::vendor_get () const
{
	return boost::str (boost::format ("LMDB %1%.%2%.%3%") % MDB_VERSION_MAJOR % MDB_VERSION_MINOR % MDB_VERSION_PATCH);
}

nano::mdb_txn_callbacks nano::mdb_store::create_txn_callbacks () const
{
	nano::mdb_txn_callbacks mdb_txn_callbacks;
	if (txn_tracking_enabled)
	{
		mdb_txn_callbacks.txn_start = ([&mdb_txn_tracker = mdb_txn_tracker] (const nano::transaction_impl * transaction_impl) {
			mdb_txn_tracker.add (transaction_impl);
		});
		mdb_txn_callbacks.txn_end = ([&mdb_txn_tracker = mdb_txn_tracker] (const nano::transaction_impl * transaction_impl) {
			mdb_txn_tracker.erase (transaction_impl);
		});
	}
	return mdb_txn_callbacks;
}

void nano::mdb_store::open_databases (bool & error_a, nano::transaction const & transaction_a, unsigned flags)
{
	error_a |= mdb_dbi_open (env.tx (transaction_a), "frontiers", flags, &frontiers) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "unchecked", flags, &unchecked) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "online_weight", flags, &online_weight) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "meta", flags, &meta) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "peers", flags, &peers) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "pruned", flags, &pruned) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "confirmation_height", flags, &confirmation_height) != 0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "accounts", flags, &accounts_v0) != 0;
	accounts = accounts_v0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "pending", flags, &pending_v0) != 0;
	pending = pending_v0;
	error_a |= mdb_dbi_open (env.tx (transaction_a), "final_votes", flags, &final_votes) != 0;

	auto version_l = version_get (transaction_a);
	if (version_l < 19)
	{
		// These legacy (and state) block databases are no longer used, but need opening so they can be deleted during an upgrade
		error_a |= mdb_dbi_open (env.tx (transaction_a), "send", flags, &send_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "receive", flags, &receive_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "open", flags, &open_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "change", flags, &change_blocks) != 0;
		if (version_l >= 15)
		{
			error_a |= mdb_dbi_open (env.tx (transaction_a), "state_blocks", flags, &state_blocks) != 0;
			state_blocks_v0 = state_blocks;
		}
	}
	else
	{
		error_a |= mdb_dbi_open (env.tx (transaction_a), "blocks", MDB_CREATE, &blocks) != 0;
	}

	if (version_l < 16)
	{
		// The representation database is no longer used, but needs opening so that it can be deleted during an upgrade
		error_a |= mdb_dbi_open (env.tx (transaction_a), "representation", flags, &representation) != 0;
	}

	if (version_l < 15)
	{
		// These databases are no longer used, but need opening so they can be deleted during an upgrade
		error_a |= mdb_dbi_open (env.tx (transaction_a), "state", flags, &state_blocks_v0) != 0;
		state_blocks = state_blocks_v0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "accounts_v1", flags, &accounts_v1) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "pending_v1", flags, &pending_v1) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction_a), "state_v1", flags, &state_blocks_v1) != 0;
	}
}

bool nano::mdb_store::do_upgrades (nano::write_transaction & transaction_a, bool & needs_vacuuming)
{
	auto error (false);
	auto version_l = version_get (transaction_a);
	switch (version_l)
	{
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
			logger.always_log (boost::str (boost::format ("The version of the ledger (%1%) is lower than the minimum (%2%) which is supported for upgrades. Either upgrade to a v19, v20 or v21 node first or delete the ledger.") % version_l % minimum_version));
			error = true;
			break;
		case 14:
			upgrade_v14_to_v15 (transaction_a);
			[[fallthrough]];
			// Upgrades to version 16, 17 & 18 are all part of the v21 node release
		case 15:
			upgrade_v15_to_v16 (transaction_a);
			[[fallthrough]];
		case 16:
			upgrade_v16_to_v17 (transaction_a);
			[[fallthrough]];
		case 17:
			upgrade_v17_to_v18 (transaction_a);
			[[fallthrough]];
			// Upgrades to version 19 & 20 are both part of the v22 node release
		case 18:
			upgrade_v18_to_v19 (transaction_a);
			needs_vacuuming = true;
			[[fallthrough]];
		case 19:
			upgrade_v19_to_v20 (transaction_a);
			[[fallthrough]];
		case 20:
			upgrade_v20_to_v21 (transaction_a);
			[[fallthrough]];
		case 21:
			break;
		default:
			logger.always_log (boost::str (boost::format ("The version of the ledger (%1%) is too high for this node") % version_l));
			error = true;
			break;
	}
	return error;
}

void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction & transaction_a)
{
	logger.always_log ("Preparing v14 to v15 database upgrade...");

	std::vector<std::pair<nano::account, nano::account_info>> account_infos;
	upgrade_counters account_counters (count (transaction_a, accounts_v0), count (transaction_a, accounts_v1));
	account_infos.reserve (account_counters.before_v0 + account_counters.before_v1);

	nano::mdb_merge_iterator<nano::account, nano::account_info_v14> i_account (transaction_a, accounts_v0, accounts_v1);
	nano::mdb_merge_iterator<nano::account, nano::account_info_v14> n_account{};
	for (; i_account != n_account; ++i_account)
	{
		nano::account account (i_account->first);
		nano::account_info_v14 account_info_v14 (i_account->second);

		// Upgrade rep block to representative account
		auto rep_block = block_get_v14 (transaction_a, account_info_v14.rep_block);
		release_assert (rep_block != nullptr);
		account_infos.emplace_back (account, nano::account_info{ account_info_v14.head, rep_block->representative (), account_info_v14.open_block, account_info_v14.balance, account_info_v14.modified, account_info_v14.block_count, i_account.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
		// Move confirmation height from account_info database to its own table
		mdb_put (env.tx (transaction_a), confirmation_height, nano::mdb_val (account), nano::mdb_val (account_info_v14.confirmation_height), MDB_APPEND);
		i_account.from_first_database ? ++account_counters.after_v0 : ++account_counters.after_v1;
	}

	logger.always_log ("Finished extracting confirmation height to its own database");

	debug_assert (account_counters.are_equal ());
	// No longer need accounts_v1, keep v0 but clear it
	mdb_drop (env.tx (transaction_a), accounts_v1, 1);
	mdb_drop (env.tx (transaction_a), accounts_v0, 0);

	for (auto const & account_account_info_pair : account_infos)
	{
		auto const & account_info (account_account_info_pair.second);
		mdb_put (env.tx (transaction_a), accounts, nano::mdb_val (account_account_info_pair.first), nano::mdb_val (account_info), MDB_APPEND);
	}

	logger.always_log ("Epoch merge upgrade: Finished accounts, now doing state blocks");

	account_infos.clear ();

	// Have to create a new database as we are iterating over the existing ones and want to use MDB_APPEND for quick insertion
	MDB_dbi state_blocks_new;
	mdb_dbi_open (env.tx (transaction_a), "state_blocks", MDB_CREATE, &state_blocks_new);

	upgrade_counters state_counters (count (transaction_a, state_blocks_v0), count (transaction_a, state_blocks_v1));

	nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> i_state (transaction_a, state_blocks_v0, state_blocks_v1);
	nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> n_state{};
	auto num = 0u;
	for (; i_state != n_state; ++i_state, ++num)
	{
		nano::block_hash hash (i_state->first);
		nano::state_block_w_sideband_v14 state_block_w_sideband_v14 (i_state->second);
		auto & sideband_v14 = state_block_w_sideband_v14.sideband;

		nano::block_sideband_v18 sideband (sideband_v14.account, sideband_v14.successor, sideband_v14.balance, sideband_v14.height, sideband_v14.timestamp, i_state.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1, false, false, false);

		// Write these out
		std::vector<uint8_t> data;
		{
			nano::vectorstream stream (data);
			state_block_w_sideband_v14.state_block->serialize (stream);
			sideband.serialize (stream, sideband_v14.type);
		}

		nano::mdb_val value{ data.size (), (void *)data.data () };
		auto s = mdb_put (env.tx (transaction_a), state_blocks_new, nano::mdb_val (hash), value, MDB_APPEND);
		release_assert_success (*this, s);

		// Every so often output to the log to indicate progress
		constexpr auto output_cutoff = 1000000;
		if (num % output_cutoff == 0 && num != 0)
		{
			logger.always_log (boost::str (boost::format ("Database epoch merge upgrade %1% million state blocks upgraded") % (num / output_cutoff)));
		}
		i_state.from_first_database ? ++state_counters.after_v0 : ++state_counters.after_v1;
	}

	debug_assert (state_counters.are_equal ());
	logger.always_log ("Epoch merge upgrade: Finished state blocks, now doing pending blocks");

	state_blocks = state_blocks_new;

	// No longer need states v0/v1 databases
	mdb_drop (env.tx (transaction_a), state_blocks_v1, 1);
	mdb_drop (env.tx (transaction_a), state_blocks_v0, 1);

	state_blocks_v0 = state_blocks;

	upgrade_counters pending_counters (count (transaction_a, pending_v0), count (transaction_a, pending_v1));
	std::vector<std::pair<nano::pending_key, nano::pending_info>> pending_infos;
	pending_infos.reserve (pending_counters.before_v0 + pending_counters.before_v1);

	nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> i_pending (transaction_a, pending_v0, pending_v1);
	nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> n_pending{};
	for (; i_pending != n_pending; ++i_pending)
	{
		nano::pending_info_v14 info (i_pending->second);
		pending_infos.emplace_back (nano::pending_key (i_pending->first), nano::pending_info{ info.source, info.amount, i_pending.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
		i_pending.from_first_database ? ++pending_counters.after_v0 : ++pending_counters.after_v1;
	}

	debug_assert (pending_counters.are_equal ());

	// No longer need the pending v1 table
	mdb_drop (env.tx (transaction_a), pending_v1, 1);
	mdb_drop (env.tx (transaction_a), pending_v0, 0);

	for (auto const & pending_key_pending_info_pair : pending_infos)
	{
		mdb_put (env.tx (transaction_a), pending, nano::mdb_val (pending_key_pending_info_pair.first), nano::mdb_val (pending_key_pending_info_pair.second), MDB_APPEND);
	}

	version_put (transaction_a, 15);
	logger.always_log ("Finished epoch merge upgrade");
}

void nano::mdb_store::upgrade_v15_to_v16 (nano::write_transaction const & transaction_a)
{
	// Representation table is no longer used
	debug_assert (representation != 0);
	if (representation != 0)
	{
		auto status (mdb_drop (env.tx (transaction_a), representation, 1));
		release_assert (status == MDB_SUCCESS);
		representation = 0;
	}
	version_put (transaction_a, 16);
}

void nano::mdb_store::upgrade_v16_to_v17 (nano::write_transaction const & transaction_a)
{
	logger.always_log ("Preparing v16 to v17 database upgrade...");

	auto account_info_i = accounts_begin (transaction_a);
	auto account_info_n = accounts_end ();

	// Set the confirmed frontier for each account in the confirmation height table
	std::vector<std::pair<nano::account, nano::confirmation_height_info>> confirmation_height_infos;
	auto num = 0u;
	for (nano::mdb_iterator<nano::account, uint64_t> i (transaction_a, confirmation_height), n (nano::mdb_iterator<nano::account, uint64_t>{}); i != n; ++i, ++account_info_i, ++num)
	{
		nano::account account (i->first);
		uint64_t confirmation_height (i->second);

		// Check account hashes matches both the accounts table and confirmation height table
		debug_assert (account == account_info_i->first);

		auto const & account_info = account_info_i->second;

		if (confirmation_height == 0)
		{
			confirmation_height_infos.emplace_back (account, confirmation_height_info{ 0, nano::block_hash (0) });
		}
		else
		{
			if (account_info_i->second.block_count / 2 >= confirmation_height)
			{
				// The confirmation height of the account is closer to the bottom of the chain, so start there and work up
				auto block = block_get_v18 (transaction_a, account_info.open_block);
				debug_assert (block);
				auto height = 1;

				while (height != confirmation_height)
				{
					block = block_get_v18 (transaction_a, block->sideband ().successor);
					debug_assert (block);
					++height;
				}

				debug_assert (block->sideband ().height == confirmation_height);
				confirmation_height_infos.emplace_back (account, confirmation_height_info{ confirmation_height, block->hash () });
			}
			else
			{
				// The confirmation height of the account is closer to the top of the chain so start there and work down
				auto block = block_get_v18 (transaction_a, account_info.head);
				auto height = block->sideband ().height;
				while (height != confirmation_height)
				{
					block = block_get_v18 (transaction_a, block->previous ());
					debug_assert (block);
					--height;
				}
				confirmation_height_infos.emplace_back (account, confirmation_height_info{ confirmation_height, block->hash () });
			}
		}

		// Every so often output to the log to indicate progress (every 200k accounts)
		constexpr auto output_cutoff = 200000;
		if (num % output_cutoff == 0 && num != 0)
		{
			logger.always_log (boost::str (boost::format ("Confirmation height frontier set for %1%00k accounts") % ((num / output_cutoff) * 2)));
		}
	}

	// Clear it then append
	auto status (mdb_drop (env.tx (transaction_a), confirmation_height, 0));
	release_assert_success (*this, status);

	for (auto const & confirmation_height_info_pair : confirmation_height_infos)
	{
		mdb_put (env.tx (transaction_a), confirmation_height, nano::mdb_val (confirmation_height_info_pair.first), nano::mdb_val (confirmation_height_info_pair.second), MDB_APPEND);
	}

	version_put (transaction_a, 17);
	logger.always_log ("Finished upgrading confirmation height frontiers");
}

void nano::mdb_store::upgrade_v17_to_v18 (nano::write_transaction const & transaction_a)
{
	logger.always_log ("Preparing v17 to v18 database upgrade...");

	auto count_pre (count (transaction_a, state_blocks));

	auto num = 0u;
	for (nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::state_block>> state_i (transaction_a, state_blocks), state_n{}; state_i != state_n; ++state_i, ++num)
	{
		nano::block_w_sideband_v18<nano::state_block> block_w_sideband (state_i->second);
		auto & block (block_w_sideband.block);
		auto & sideband (block_w_sideband.sideband);

		bool is_send{ false };
		bool is_receive{ false };
		bool is_epoch{ false };

		nano::amount prev_balance (0);
		if (!block->hashables.previous.is_zero ())
		{
			prev_balance = block_balance_v18 (transaction_a, block->hashables.previous);
		}
		if (block->hashables.balance == prev_balance && network_params.ledger.epochs.is_epoch_link (block->hashables.link))
		{
			is_epoch = true;
		}
		else if (block->hashables.balance < prev_balance)
		{
			is_send = true;
		}
		else if (!block->hashables.link.is_zero ())
		{
			is_receive = true;
		}

		nano::block_sideband_v18 new_sideband (sideband.account, sideband.successor, sideband.balance, sideband.height, sideband.timestamp, sideband.details.epoch, is_send, is_receive, is_epoch);
		// Write these out
		std::vector<uint8_t> data;
		{
			nano::vectorstream stream (data);
			block->serialize (stream);
			new_sideband.serialize (stream, block->type ());
		}
		nano::mdb_val value{ data.size (), (void *)data.data () };
		auto s = mdb_cursor_put (state_i.cursor, state_i->first, value, MDB_CURRENT);
		release_assert_success (*this, s);

		// Every so often output to the log to indicate progress
		constexpr auto output_cutoff = 1000000;
		if (num > 0 && num % output_cutoff == 0)
		{
			logger.always_log (boost::str (boost::format ("Database sideband upgrade %1% million state blocks upgraded (out of %2%)") % (num / output_cutoff) % count_pre));
		}
	}

	auto count_post (count (transaction_a, state_blocks));
	release_assert (count_pre == count_post);

	version_put (transaction_a, 18);
	logger.always_log ("Finished upgrading the sideband");
}

void nano::mdb_store::upgrade_v18_to_v19 (nano::write_transaction const & transaction_a)
{
	logger.always_log ("Preparing v18 to v19 database upgrade...");
	auto count_pre (count (transaction_a, state_blocks) + count (transaction_a, send_blocks) + count (transaction_a, receive_blocks) + count (transaction_a, change_blocks) + count (transaction_a, open_blocks));

	// Combine in order of likeliness of counts
	std::map<nano::block_hash, nano::block_w_sideband> legacy_open_receive_change_blocks;

	for (auto i (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::change_block>> (std::make_unique<nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::change_block>>> (transaction_a, change_blocks))), n (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::change_block>> (nullptr)); i != n; ++i)
	{
		nano::block_sideband_v18 const & old_sideband (i->second.sideband);
		nano::block_sideband new_sideband (old_sideband.account, old_sideband.successor, old_sideband.balance, old_sideband.height, old_sideband.timestamp, nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0);
		legacy_open_receive_change_blocks[i->first] = { nano::block_w_sideband{ i->second.block, new_sideband } };
	}

	for (auto i (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::open_block>> (std::make_unique<nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::open_block>>> (transaction_a, open_blocks))), n (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::open_block>> (nullptr)); i != n; ++i)
	{
		nano::block_sideband_v18 const & old_sideband (i->second.sideband);
		nano::block_sideband new_sideband (old_sideband.account, old_sideband.successor, old_sideband.balance, old_sideband.height, old_sideband.timestamp, nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0);
		legacy_open_receive_change_blocks[i->first] = { nano::block_w_sideband{ i->second.block, new_sideband } };
	}

	for (auto i (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::receive_block>> (std::make_unique<nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::receive_block>>> (transaction_a, receive_blocks))), n (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::receive_block>> (nullptr)); i != n; ++i)
	{
		nano::block_sideband_v18 const & old_sideband (i->second.sideband);
		nano::block_sideband new_sideband (old_sideband.account, old_sideband.successor, old_sideband.balance, old_sideband.height, old_sideband.timestamp, nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0);
		legacy_open_receive_change_blocks[i->first] = { nano::block_w_sideband{ i->second.block, new_sideband } };
	}

	release_assert (!mdb_drop (env.tx (transaction_a), receive_blocks, 1));
	receive_blocks = 0;
	release_assert (!mdb_drop (env.tx (transaction_a), open_blocks, 1));
	open_blocks = 0;
	release_assert (!mdb_drop (env.tx (transaction_a), change_blocks, 1));
	change_blocks = 0;

	logger.always_log ("Write legacy open/receive/change to new format");

	MDB_dbi temp_legacy_open_receive_change_blocks;
	{
		mdb_dbi_open (env.tx (transaction_a), "temp_legacy_open_receive_change_blocks", MDB_CREATE, &temp_legacy_open_receive_change_blocks);

		for (auto const & legacy_block : legacy_open_receive_change_blocks)
		{
			std::vector<uint8_t> data;
			{
				nano::vectorstream stream (data);
				nano::serialize_block (stream, *legacy_block.second.block);
				legacy_block.second.sideband.serialize (stream, legacy_block.second.block->type ());
			}

			nano::mdb_val value{ data.size (), (void *)data.data () };
			auto s = mdb_put (env.tx (transaction_a), temp_legacy_open_receive_change_blocks, nano::mdb_val (legacy_block.first), value, MDB_APPEND);
			release_assert_success (*this, s);
		}
	}

	logger.always_log ("Write legacy send to new format");

	// Write send blocks to a new table (this was not done in memory as it would push us above memory requirements)
	MDB_dbi temp_legacy_send_blocks;
	{
		mdb_dbi_open (env.tx (transaction_a), "temp_legacy_send_blocks", MDB_CREATE, &temp_legacy_send_blocks);

		for (auto i (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::send_block>> (std::make_unique<nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::send_block>>> (transaction_a, send_blocks))), n (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::send_block>> (nullptr)); i != n; ++i)
		{
			auto const & block_w_sideband_v18 (i->second);

			std::vector<uint8_t> data;
			{
				nano::vectorstream stream (data);
				nano::serialize_block (stream, *block_w_sideband_v18.block);
				block_w_sideband_v18.sideband.serialize (stream, nano::block_type::send); // Equal to new version for legacy blocks
			}

			nano::mdb_val value{ data.size (), (void *)data.data () };
			auto s = mdb_put (env.tx (transaction_a), temp_legacy_send_blocks, nano::mdb_val (i->first), value, MDB_APPEND);
			release_assert_success (*this, s);
		}
	}

	release_assert (!mdb_drop (env.tx (transaction_a), send_blocks, 1));
	send_blocks = 0;

	logger.always_log ("Merge legacy open/receive/change with legacy send blocks");

	MDB_dbi temp_legacy_send_open_receive_change_blocks;
	{
		mdb_dbi_open (env.tx (transaction_a), "temp_legacy_send_open_receive_change_blocks", MDB_CREATE, &temp_legacy_send_open_receive_change_blocks);

		nano::mdb_merge_iterator<nano::block_hash, nano::block_w_sideband> i (transaction_a, temp_legacy_open_receive_change_blocks, temp_legacy_send_blocks);
		nano::mdb_merge_iterator<nano::block_hash, nano::block_w_sideband> n{};
		for (; i != n; ++i)
		{
			auto s = mdb_put (env.tx (transaction_a), temp_legacy_send_open_receive_change_blocks, nano::mdb_val (i->first), nano::mdb_val (i->second), MDB_APPEND);
			release_assert_success (*this, s);
		}

		// Delete tables
		mdb_drop (env.tx (transaction_a), temp_legacy_send_blocks, 1);
		mdb_drop (env.tx (transaction_a), temp_legacy_open_receive_change_blocks, 1);
	}

	logger.always_log ("Write state blocks to new format");

	// Write state blocks to a new table (this was not done in memory as it would push us above memory requirements)
	MDB_dbi temp_state_blocks;
	{
		auto type_state (nano::block_type::state);
		mdb_dbi_open (env.tx (transaction_a), "temp_state_blocks", MDB_CREATE, &temp_state_blocks);

		for (auto i (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::state_block>> (std::make_unique<nano::mdb_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::state_block>>> (transaction_a, state_blocks))), n (nano::store_iterator<nano::block_hash, nano::block_w_sideband_v18<nano::state_block>> (nullptr)); i != n; ++i)
		{
			auto const & block_w_sideband_v18 (i->second);
			nano::block_sideband_v18 const & old_sideband (block_w_sideband_v18.sideband);
			nano::epoch source_epoch (nano::epoch::epoch_0);
			// Source block v18 epoch
			if (old_sideband.details.is_receive)
			{
				auto db_val (block_raw_get_by_type_v18 (transaction_a, block_w_sideband_v18.block->link ().as_block_hash (), type_state));
				if (db_val.is_initialized ())
				{
					nano::bufferstream stream (reinterpret_cast<uint8_t const *> (db_val.get ().data ()), db_val.get ().size ());
					auto source_block (nano::deserialize_block (stream, type_state));
					release_assert (source_block != nullptr);
					nano::block_sideband_v18 source_sideband;
					auto error (source_sideband.deserialize (stream, type_state));
					release_assert (!error);
					source_epoch = source_sideband.details.epoch;
				}
			}
			nano::block_sideband new_sideband (old_sideband.account, old_sideband.successor, old_sideband.balance, old_sideband.height, old_sideband.timestamp, old_sideband.details.epoch, old_sideband.details.is_send, old_sideband.details.is_receive, old_sideband.details.is_epoch, source_epoch);

			std::vector<uint8_t> data;
			{
				nano::vectorstream stream (data);
				nano::serialize_block (stream, *block_w_sideband_v18.block);
				new_sideband.serialize (stream, nano::block_type::state);
			}

			nano::mdb_val value{ data.size (), (void *)data.data () };
			auto s = mdb_put (env.tx (transaction_a), temp_state_blocks, nano::mdb_val (i->first), value, MDB_APPEND);
			release_assert_success (*this, s);
		}
	}

	release_assert (!mdb_drop (env.tx (transaction_a), state_blocks, 1));
	state_blocks = 0;

	logger.always_log ("Merging all legacy blocks with state blocks");

	// Merge all legacy blocks with state blocks into the final table
	nano::mdb_merge_iterator<nano::block_hash, nano::block_w_sideband> i (transaction_a, temp_legacy_send_open_receive_change_blocks, temp_state_blocks);
	nano::mdb_merge_iterator<nano::block_hash, nano::block_w_sideband> n{};
	mdb_dbi_open (env.tx (transaction_a), "blocks", MDB_CREATE, &blocks);
	for (; i != n; ++i)
	{
		auto s = mdb_put (env.tx (transaction_a), blocks, nano::mdb_val (i->first), nano::mdb_val (i->second), MDB_APPEND);
		release_assert_success (*this, s);
	}

	// Delete tables
	mdb_drop (env.tx (transaction_a), temp_legacy_send_open_receive_change_blocks, 1);
	mdb_drop (env.tx (transaction_a), temp_state_blocks, 1);

	auto count_post (count (transaction_a, blocks));
	release_assert (count_pre == count_post);

	MDB_dbi vote{ 0 };
	release_assert (!mdb_dbi_open (env.tx (transaction_a), "vote", MDB_CREATE, &vote));
	release_assert (!mdb_drop (env.tx (transaction_a), vote, 1));

	version_put (transaction_a, 19);
	logger.always_log ("Finished upgrading all blocks to new blocks database");
}

void nano::mdb_store::upgrade_v19_to_v20 (nano::write_transaction const & transaction_a)
{
	logger.always_log ("Preparing v19 to v20 database upgrade...");
	mdb_dbi_open (env.tx (transaction_a), "pruned", MDB_CREATE, &pruned);
	version_put (transaction_a, 20);
	logger.always_log ("Finished creating new pruned table");
}

void nano::mdb_store::upgrade_v20_to_v21 (nano::write_transaction const & transaction_a)
{
	logger.always_log ("Preparing v20 to v21 database upgrade...");
	mdb_dbi_open (env.tx (transaction_a), "final_votes", MDB_CREATE, &final_votes);
	version_put (transaction_a, 21);
	logger.always_log ("Finished creating new final_vote table");
}

/** Takes a filepath, appends '_backup_<timestamp>' to the end (but before any extension) and saves that file in the same directory */
void nano::mdb_store::create_backup_file (nano::mdb_env & env_a, boost::filesystem::path const & filepath_a, nano::logger_mt & logger_a)
{
	auto extension = filepath_a.extension ();
	auto filename_without_extension = filepath_a.filename ().replace_extension ("");
	auto orig_filepath = filepath_a;
	auto & backup_path = orig_filepath.remove_filename ();
	auto backup_filename = filename_without_extension;
	backup_filename += "_backup_";
	backup_filename += std::to_string (std::chrono::system_clock::now ().time_since_epoch ().count ());
	backup_filename += extension;
	auto backup_filepath = backup_path / backup_filename;
	auto start_message (boost::str (boost::format ("Performing %1% backup before database upgrade...") % filepath_a.filename ()));
	logger_a.always_log (start_message);
	std::cout << start_message << std::endl;
	auto error (mdb_env_copy (env_a, backup_filepath.string ().c_str ()));
	if (error)
	{
		auto error_message (boost::str (boost::format ("%1% backup failed") % filepath_a.filename ()));
		logger_a.always_log (error_message);
		std::cerr << error_message << std::endl;
		std::exit (1);
	}
	else
	{
		auto success_message (boost::str (boost::format ("Backup created: %1%") % backup_filename));
		logger_a.always_log (success_message);
		std::cout << success_message << std::endl;
	}
}

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

void nano::mdb_store::version_put (nano::write_transaction const & transaction_a, int version_a)
{
	nano::uint256_union version_key (1);
	nano::uint256_union version_value (version_a);
	auto status (mdb_put (env.tx (transaction_a), meta, nano::mdb_val (version_key), nano::mdb_val (version_value), 0));
	release_assert_success (*this, status);
}

bool nano::mdb_store::exists (nano::transaction const & transaction_a, tables table_a, nano::mdb_val const & key_a) const
{
	nano::mdb_val junk;
	auto status = get (transaction_a, table_a, key_a, junk);
	release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND);
	return (status == MDB_SUCCESS);
}

int nano::mdb_store::get (nano::transaction const & transaction_a, tables table_a, nano::mdb_val const & key_a, nano::mdb_val & value_a) const
{
	return mdb_get (env.tx (transaction_a), table_to_dbi (table_a), key_a, value_a);
}

int nano::mdb_store::put (nano::write_transaction const & transaction_a, tables table_a, nano::mdb_val const & key_a, const nano::mdb_val & value_a) const
{
	return (mdb_put (env.tx (transaction_a), table_to_dbi (table_a), key_a, value_a, 0));
}

int nano::mdb_store::del (nano::write_transaction const & transaction_a, tables table_a, nano::mdb_val const & key_a) const
{
	return (mdb_del (env.tx (transaction_a), table_to_dbi (table_a), key_a, nullptr));
}

int nano::mdb_store::drop (nano::write_transaction const & transaction_a, tables table_a)
{
	return clear (transaction_a, table_to_dbi (table_a));
}

int nano::mdb_store::clear (nano::write_transaction const & transaction_a, MDB_dbi handle_a)
{
	return mdb_drop (env.tx (transaction_a), handle_a, 0);
}

uint64_t nano::mdb_store::count (nano::transaction const & transaction_a, tables table_a) const
{
	return count (transaction_a, table_to_dbi (table_a));
}

uint64_t nano::mdb_store::count (nano::transaction const & transaction_a, MDB_dbi db_a) const
{
	MDB_stat stats;
	auto status (mdb_stat (env.tx (transaction_a), db_a, &stats));
	release_assert_success (*this, status);
	return (stats.ms_entries);
}

MDB_dbi nano::mdb_store::table_to_dbi (tables table_a) const
{
	switch (table_a)
	{
		case tables::frontiers:
			return frontiers;
		case tables::accounts:
			return accounts;
		case tables::blocks:
			return blocks;
		case tables::pending:
			return pending;
		case tables::unchecked:
			return unchecked;
		case tables::online_weight:
			return online_weight;
		case tables::meta:
			return meta;
		case tables::peers:
			return peers;
		case tables::pruned:
			return pruned;
		case tables::confirmation_height:
			return confirmation_height;
		case tables::final_votes:
			return final_votes;
		default:
			release_assert (false);
			return peers;
	}
}

bool nano::mdb_store::not_found (int status) const
{
	return (status_code_not_found () == status);
}

bool nano::mdb_store::success (int status) const
{
	return (MDB_SUCCESS == status);
}

int nano::mdb_store::status_code_not_found () const
{
	return MDB_NOTFOUND;
}

std::string nano::mdb_store::error_string (int status) const
{
	return mdb_strerror (status);
}

bool nano::mdb_store::copy_db (boost::filesystem::path const & destination_file)
{
	return !mdb_env_copy2 (env.environment, destination_file.string ().c_str (), MDB_CP_COMPACT);
}

void nano::mdb_store::rebuild_db (nano::write_transaction const & transaction_a)
{
	// Tables with uint256_union key
	std::vector<MDB_dbi> tables = { accounts, blocks, pruned, confirmation_height };
	for (auto const & table : tables)
	{
		MDB_dbi temp;
		mdb_dbi_open (env.tx (transaction_a), "temp_table", MDB_CREATE, &temp);
		// Copy all values to temporary table
		for (auto i (nano::store_iterator<nano::uint256_union, nano::mdb_val> (std::make_unique<nano::mdb_iterator<nano::uint256_union, nano::mdb_val>> (transaction_a, table))), n (nano::store_iterator<nano::uint256_union, nano::mdb_val> (nullptr)); i != n; ++i)
		{
			auto s = mdb_put (env.tx (transaction_a), temp, nano::mdb_val (i->first), i->second, MDB_APPEND);
			release_assert_success (*this, s);
		}
		release_assert (count (transaction_a, table) == count (transaction_a, temp));
		// Clear existing table
		mdb_drop (env.tx (transaction_a), table, 0);
		// Put values from copy
		for (auto i (nano::store_iterator<nano::uint256_union, nano::mdb_val> (std::make_unique<nano::mdb_iterator<nano::uint256_union, nano::mdb_val>> (transaction_a, temp))), n (nano::store_iterator<nano::uint256_union, nano::mdb_val> (nullptr)); i != n; ++i)
		{
			auto s = mdb_put (env.tx (transaction_a), table, nano::mdb_val (i->first), i->second, MDB_APPEND);
			release_assert_success (*this, s);
		}
		release_assert (count (transaction_a, table) == count (transaction_a, temp));
		// Remove temporary table
		mdb_drop (env.tx (transaction_a), temp, 1);
	}
	// Pending table
	{
		MDB_dbi temp;
		mdb_dbi_open (env.tx (transaction_a), "temp_table", MDB_CREATE, &temp);
		// Copy all values to temporary table
		for (auto i (nano::store_iterator<nano::pending_key, nano::pending_info> (std::make_unique<nano::mdb_iterator<nano::pending_key, nano::pending_info>> (transaction_a, pending))), n (nano::store_iterator<nano::pending_key, nano::pending_info> (nullptr)); i != n; ++i)
		{
			auto s = mdb_put (env.tx (transaction_a), temp, nano::mdb_val (i->first), nano::mdb_val (i->second), MDB_APPEND);
			release_assert_success (*this, s);
		}
		release_assert (count (transaction_a, pending) == count (transaction_a, temp));
		mdb_drop (env.tx (transaction_a), pending, 0);
		// Put values from copy
		for (auto i (nano::store_iterator<nano::pending_key, nano::pending_info> (std::make_unique<nano::mdb_iterator<nano::pending_key, nano::pending_info>> (transaction_a, temp))), n (nano::store_iterator<nano::pending_key, nano::pending_info> (nullptr)); i != n; ++i)
		{
			auto s = mdb_put (env.tx (transaction_a), pending, nano::mdb_val (i->first), nano::mdb_val (i->second), MDB_APPEND);
			release_assert_success (*this, s);
		}
		release_assert (count (transaction_a, pending) == count (transaction_a, temp));
		mdb_drop (env.tx (transaction_a), temp, 1);
	}
}

bool nano::mdb_store::init_error () const
{
	return error;
}

std::shared_ptr<nano::block> nano::mdb_store::block_get_v18 (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
{
	nano::block_type type;
	auto value (block_raw_get_v18 (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);
		release_assert (result != nullptr);
		nano::block_sideband_v18 sideband;
		auto error = (sideband.deserialize (stream, type));
		release_assert (!error);
		result->sideband_set (nano::block_sideband (sideband.account, sideband.successor, sideband.balance, sideband.height, sideband.timestamp, sideband.details.epoch, sideband.details.is_send, sideband.details.is_receive, sideband.details.is_epoch, nano::epoch::epoch_0));
	}
	return result;
}

nano::mdb_val nano::mdb_store::block_raw_get_v18 (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) const
{
	nano::mdb_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_v18 (transaction_a, hash_a, current_type));
		if (db_val.is_initialized ())
		{
			type_a = current_type;
			result = db_val.get ();
			break;
		}
	}

	return result;
}

boost::optional<nano::mdb_val> nano::mdb_store::block_raw_get_by_type_v18 (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) const
{
	nano::mdb_val value;
	nano::mdb_val hash (hash_a);
	int status = status_code_not_found ();
	switch (type_a)
	{
		case nano::block_type::send:
		{
			status = mdb_get (env.tx (transaction_a), send_blocks, hash, value);
			break;
		}
		case nano::block_type::receive:
		{
			status = mdb_get (env.tx (transaction_a), receive_blocks, hash, value);
			break;
		}
		case nano::block_type::open:
		{
			status = mdb_get (env.tx (transaction_a), open_blocks, hash, value);
			break;
		}
		case nano::block_type::change:
		{
			status = mdb_get (env.tx (transaction_a), change_blocks, hash, value);
			break;
		}
		case nano::block_type::state:
		{
			status = mdb_get (env.tx (transaction_a), state_blocks, hash, value);
			break;
		}
		case nano::block_type::invalid:
		case nano::block_type::not_a_block:
		{
			break;
		}
	}

	release_assert (success (status) || not_found (status));
	boost::optional<nano::mdb_val> result;
	if (success (status))
	{
		result = value;
	}
	return result;
}

nano::uint128_t nano::mdb_store::block_balance_v18 (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
{
	auto block (block_get_v18 (transaction_a, hash_a));
	release_assert (block);
	nano::uint128_t result (block_balance_calculated (block));
	return result;
}

// All the v14 functions below are only needed during upgrades
size_t nano::mdb_store::block_successor_offset_v14 (nano::transaction const & transaction_a, size_t entry_size_a, nano::block_type type_a) const
{
	return entry_size_a - nano::block_sideband_v14::size (type_a);
}

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

nano::mdb_val nano::mdb_store::block_raw_get_v14 (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a, bool * is_state_v1) const
{
	nano::mdb_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_v14 (transaction_a, hash_a, current_type, is_state_v1));
		if (db_val.is_initialized ())
		{
			type_a = current_type;
			result = db_val.get ();
			break;
		}
	}

	return result;
}

boost::optional<nano::mdb_val> nano::mdb_store::block_raw_get_by_type_v14 (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a, bool * is_state_v1) const
{
	nano::mdb_val value;
	nano::mdb_val hash (hash_a);
	int status = status_code_not_found ();
	switch (type_a)
	{
		case nano::block_type::send:
		{
			status = mdb_get (env.tx (transaction_a), send_blocks, hash, value);
			break;
		}
		case nano::block_type::receive:
		{
			status = mdb_get (env.tx (transaction_a), receive_blocks, hash, value);
			break;
		}
		case nano::block_type::open:
		{
			status = mdb_get (env.tx (transaction_a), open_blocks, hash, value);
			break;
		}
		case nano::block_type::change:
		{
			status = mdb_get (env.tx (transaction_a), change_blocks, hash, value);
			break;
		}
		case nano::block_type::state:
		{
			status = mdb_get (env.tx (transaction_a), state_blocks_v1, hash, value);
			if (is_state_v1 != nullptr)
			{
				*is_state_v1 = success (status);
			}
			if (not_found (status))
			{
				status = mdb_get (env.tx (transaction_a), state_blocks_v0, hash, value);
			}
			break;
		}
		case nano::block_type::invalid:
		case nano::block_type::not_a_block:
		{
			break;
		}
	}

	release_assert (success (status) || not_found (status));
	boost::optional<nano::mdb_val> result;
	if (success (status))
	{
		result = value;
	}
	return result;
}

std::shared_ptr<nano::block> nano::mdb_store::block_get_v14 (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_sideband_v14 * sideband_a, bool * is_state_v1) const
{
	nano::block_type type;
	auto value (block_raw_get_v14 (transaction_a, hash_a, type, is_state_v1));
	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);
		debug_assert (result != nullptr);
		if (sideband_a)
		{
			sideband_a->type = type;
			bool error = sideband_a->deserialize (stream);
			(void)error;
			debug_assert (!error);
		}
	}
	return result;
}

nano::mdb_store::upgrade_counters::upgrade_counters (uint64_t count_before_v0, uint64_t count_before_v1) :
	before_v0 (count_before_v0),
	before_v1 (count_before_v1)
{
}

bool nano::mdb_store::upgrade_counters::are_equal () const
{
	return (before_v0 == after_v0) && (before_v1 == after_v1);
}

unsigned nano::mdb_store::max_block_write_batch_num () const
{
	return std::numeric_limits<unsigned>::max ();
}

// Explicitly instantiate
template class nano::block_store_partial<MDB_val, nano::mdb_store>;


text:
// Copyright (c) 2018 Steven Watanabe
//
// Distributed under the Boost Software License Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS2_DETAIL_META_HPP_INCLUDED
#define BOOST_UNITS2_DETAIL_META_HPP_INCLUDED

#include <boost/units2/def.hpp>
#include <boost/units2/unit.hpp>

namespace boost {
namespace units2 {
namespace detail {

// std::experimental
template<class T, class Enable, template<class...> class F, class... A>
struct detected_or_impl { using type = T; };
template<class T, template<class...> class F, class... A>
struct detected_or_impl<T, std::void_t<F<A...>>, F, A...>
{
    using type = F<A...>;
};
template<class T, template<class...> class F, class... A>
using detected_or = typename detected_or_impl<T, void, F, A...>::type type;

}
}
}

#endif

ext: hpp


text:
#ifndef BURST__ITERATOR__SYMMETRIC_DIFFERENCE_ITERATOR_HPP
#define BURST__ITERATOR__SYMMETRIC_DIFFERENCE_ITERATOR_HPP

#include <burst/algorithm/select_min.hpp>
#include <burst/container/access/front.hpp>
#include <burst/functional/each.hpp>
#include <burst/iterator/detail/prevent_writing.hpp>
#include <burst/iterator/detail/uniform_range_tuple_please.hpp>
#include <burst/iterator/end_tag.hpp>
#include <burst/range/make_range_vector.hpp>
#include <burst/range/own_as_range.hpp>
#include <burst/tuple/apply.hpp>
#include <burst/type_traits/iterator_value.hpp>
#include <burst/type_traits/range_reference.hpp>
#include <burst/type_traits/range_value.hpp>

#include <boost/algorithm/cxx11/is_sorted.hpp>
#include <boost/assert.hpp>
#include <boost/iterator/iterator_concepts.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/concepts.hpp>

#include <algorithm>
#include <functional>
#include <iterator>
#include <tuple>
#include <utility>

namespace burst
{
    //!     Итератор симметрической разности
    /*!
            Симметрическая разность нескольких множеств — это множество, состоящее из элементов,
        которые присутствуют в нечётном количестве этих множеств.

            Предназначен для нахождения симметрической разности нескольких диапазонов одного типа
        "на лету", то есть без использования дополнительной памяти для хранения результирующего
        диапазона.
            Принимает на вход набор упорядоченных диапазонов и перемещается по тем элементам,
        которые в нечётном количестве этих диапазонов.
            Входные диапазоны рассматриваются как мультимножества.
            Результирующий диапазон неизменяем. То есть из итератора можно только прочитать
        значения, но нельзя записать. Такое ограничение обусловлено тем, что каждый выдаваемый
        итератором элемент может присутствовать в нескольких из входных диапазонов, а значит,
        нельзя однозначно выбрать из них какой-то один для записи нового значения.
            Между элементами результирующего диапазона сохраняется заданное отношение порядка.
            Алгоритм деструктивен по отношению к внешнему и всем внутренним диапазонам. Поэтому
        итератор симметрической разности однопроходный.

        \tparam RandomAccessIterator
            Тип итератора принимаемого на вход внешнего диапазона. Он должен быть диапазоном
            произвольного доступа, то есть удовлетворять требованиям понятия "Random Access
            Iterator".
        \tparam Compare
            Двухместная операция, задающая отношение строгого порядка на элементах внутренних
            диапазонов. Если пользователем явно не указана операция, то, по-умолчанию, берётся
            отношение "меньше", задаваемое функциональным объектом "std::less<>".

            Алгоритм работы.

        1.  Инвариант
            Либо внешний диапазон пуст, либо на первых N местах (N — любое нечётное число) стоят
            диапазоны с наименьшими первыми элементами.
            Текущим элементом является первый элемент первого из внутренних диапазонов.
        2.  Поиск следующего элемента
            а.  Первые N диапазонов продвигаются на один элемент вперёд.
            б.  Каждый диапазон, который закончился, выбрасывается из рассмотрения. Когда все
                диапазоны закончились, элементов симметрической разности больше нет.
            в.  Восстановление инварианта.
     */
    template
    <
        typename RandomAccessIterator,
        typename Compare = std::less<>
    >
    class symmetric_difference_iterator:
        public boost::iterator_facade
        <
            symmetric_difference_iterator<RandomAccessIterator, Compare>,
            range_value_t<iterator_value_t<RandomAccessIterator>>,
            boost::single_pass_traversal_tag,
            detail::prevent_writing_t<range_reference_t<iterator_value_t<RandomAccessIterator>>>
        >
    {
    private:
        using outer_range_iterator = RandomAccessIterator;
        BOOST_CONCEPT_ASSERT((boost::RandomAccessIteratorConcept<outer_range_iterator>));

        using inner_range_type = iterator_value_t<outer_range_iterator>;
        BOOST_CONCEPT_ASSERT((boost::ForwardRangeConcept<inner_range_type>));

        using compare_type = Compare;

        using base_type =
            boost::iterator_facade
            <
                symmetric_difference_iterator,
                range_value_t<inner_range_type>,
                boost::single_pass_traversal_tag,
                detail::prevent_writing_t<range_reference_t<inner_range_type>>
            >;

    public:
        symmetric_difference_iterator
            (
                outer_range_iterator first, outer_range_iterator last,
                Compare compare = Compare()
            ):
            m_begin(std::move(first)),
            m_end(std::move(last)),
            m_min_end(m_begin),
            m_compare(compare)
        {
            BOOST_ASSERT(std::all_of(m_begin, m_end,
                [this] (const auto & range)
                {
                    return boost::algorithm::is_sorted(range, m_compare);
                }));

            settle();
        }

        symmetric_difference_iterator
        (
            iterator::end_tag_t,
            const symmetric_difference_iterator & begin
        ):
            m_begin(begin.m_begin),
            m_end(begin.m_begin),
            m_min_end(begin.m_begin),
            m_compare(begin.m_compare)
        {
        }

        symmetric_difference_iterator () = default;

    private:
        friend class boost::iterator_core_access;

        void remove_empty_ranges ()
        {
            m_end = std::remove_if(m_begin, m_end, [] (const auto & r) {return r.empty();});
        }

        //!     Поддержать инвариант
        /*!
                Подробно инвариант описан в п.1 алгоритма работы.
         */
        void maintain_invariant ()
        {
            m_min_end = select_min(m_begin, m_end, each(front) | m_compare);
            while (m_min_end != m_begin && std::distance(m_begin, m_min_end) % 2 == 0)
            {
                std::for_each(m_begin, m_min_end, [] (auto & r) {r.advance_begin(1);});
                remove_empty_ranges();
                m_min_end = select_min(m_begin, m_end, each(front) | m_compare);
            }
        }

        void increment ()
        {
            faze();
            settle();
        }

        //!     Вывести диапазоны из равновесия
        /*!
                Приводит диапазоны в такое состояние, что предыдущий элемент уже недостижим, а
            следующий в общем случае ещё не достигнут.
                Продвигает диапазоны, содержащие на первом месте текущий элемент, на один элемент
            вперёд.
         */
        void faze ()
        {
            std::for_each(m_begin, m_min_end, [] (auto & r) {r.advance_begin(1);});
        }

        //!     Устаканить диапазоны на ближайшем элементе симметрической разности
        /*!
                Выбрасывает из рассмотрения пустые диапазоны и восстанавливает инвариант.
         */
        void settle ()
        {
            remove_empty_ranges();
            maintain_invariant();
        }

    private:
        typename base_type::reference dereference () const
        {
            return m_begin->front();
        }

        bool equal (const symmetric_difference_iterator & that) const
        {
            assert(this->m_begin == that.m_begin);
            return std::equal(this->m_begin, this->m_end, that.m_begin, that.m_end);
        }

    private:
        outer_range_iterator m_begin;
        outer_range_iterator m_end;
        outer_range_iterator m_min_end; // Итератор на позицию за последним минимальным элементом.
        compare_type m_compare;
    };

    //!     Функция для создания итератора симметрической разности с предикатом.
    /*!
            Принимает на вход набор диапазонов, для которых нужно найти симметрическую разность,
        и операцию, задающую отношение строгого порядка на элементах этих диапазонов.
            Сами диапазоны должны быть упорядочены относительно этой операции.
            Возвращает итератор на первый элемент симметрической разности входных диапазонов.
     */
    template <typename RandomAccessIterator, typename Compare>
    auto
        make_symmetric_difference_iterator
        (
            RandomAccessIterator first, RandomAccessIterator last,
            Compare compare
        )
    {
        return
            symmetric_difference_iterator<RandomAccessIterator, Compare>
            (
                std::move(first), std::move(last),
                compare
            );
    }

    template <typename RandomAccessRange, typename Compare>
    auto make_symmetric_difference_iterator (RandomAccessRange && ranges, Compare compare)
    {
        using std::begin;
        using std::end;
        return
            make_symmetric_difference_iterator
            (
                begin(std::forward<RandomAccessRange>(ranges)),
                end(std::forward<RandomAccessRange>(ranges)),
                compare
            );
    }

    /*!
        \brief
            Функция для создания итератора симметрической разности с предикатом из кортежа ссылок

        \details
            Создаёт итератор ленивой симметрической разности нескольких диапазонов. Проход от
            созданного итератора до итератора-конца (см. перегрузку с `end_tag_t`) перечисляет
            те, и только те элементы исходных диапазонов `ranges`, которые присутствуют в нечётном
            числе этих диапазонов, причём в порядке их неубывания относительно операции `compare`.

        \param ranges
            Кортеж ссылок на диапазоны, для которых нужно вычислить симметрическую разность.
        \param compare
            Операция, задающая отношение строгого порядка на элементах результирующего диапазона.

        \pre
            Каждый диапазон в `ranges` упорядочен относительно операции `compare`.

        \returns
            Итератор на наименьший относительно заданного отношения порядка элемент ленивой
            симметрической разности входных диапазонов.

        \see symmetric_difference_iterator
     */
    template <typename ... Ranges, typename Compare>
    auto make_symmetric_difference_iterator (std::tuple<Ranges &...> ranges, Compare compare)
    {
        auto common_ranges = detail::uniform_range_tuple_please(ranges);
        return
            make_symmetric_difference_iterator
            (
                burst::own_as_range(burst::apply(burst::make_range_vector, common_ranges)),
                std::move(compare)
            );
    }

    //!     Функция для создания итератора симметрической разности
    /*!
            Принимает на вход набор диапазонов, для которых нужно найти симметрическую разность.
            Возвращает итератор на первый элемент симметрической разности входных диапазонов.
            Отношение порядка для элементов диапазона выбирается по-умолчанию.
     */
    template <typename RandomAccessIterator>
    auto make_symmetric_difference_iterator (RandomAccessIterator first, RandomAccessIterator last)
    {
        return
            symmetric_difference_iterator<RandomAccessIterator>(std::move(first), std::move(last));
    }

    template <typename RandomAccessRange>
    auto make_symmetric_difference_iterator (RandomAccessRange && ranges)
    {
        using std::begin;
        using std::end;
        return
            make_symmetric_difference_iterator
            (
                begin(std::forward<RandomAccessRange>(ranges)),
                end(std::forward<RandomAccessRange>(ranges))
            );
    }

    /*!
        \brief
            Функция для создания итератора симметрической разности из кортежа ссылок

        \returns
            Итератор ленивой симметрической разности с отношением порядка по умолчанию.

        \see make_symmetric_difference_iterator
        \see symmetric_difference_iterator
     */
    template <typename ... Ranges>
    auto make_symmetric_difference_iterator (std::tuple<Ranges &...> ranges)
    {
        auto common_ranges = detail::uniform_range_tuple_please(ranges);
        return
            make_symmetric_difference_iterator
            (
                burst::own_as_range(burst::apply(burst::make_range_vector, common_ranges))
            );
    }

    //!     Функция для создания итератора на конец симметрической разности
    /*!
            Принимает на вход итератор на начало симметрической разности и индикатор конца
        итератора.
            Возвращает итератор-конец, который, если до него дойти, покажет, что элементы
        симметрической разности закончились.
     */
    template <typename RandomAccessIterator, typename Compare>
    auto
        make_symmetric_difference_iterator
        (
            iterator::end_tag_t,
            const symmetric_difference_iterator<RandomAccessIterator, Compare> & begin
        )
    {
        return
            symmetric_difference_iterator<RandomAccessIterator, Compare>(iterator::end_tag, begin);
    }
} // namespace burst

#endif // BURST__ITERATOR__SYMMETRIC_DIFFERENCE_ITERATOR_HPP

max_stars_repo_name: izvolov/thrust

max_issues_repo_name: izvolov/burst

max_forks_repo_name: izvolov/thrust


text:
#include "tree_display.hpp"
#include "ui_tree_display.h"

#include <boost/numeric/conversion/cast.hpp>

#include <osg/ComputeBoundsVisitor>
#include <osg/Geode>
#include <osg/Material>
#include <osg/MatrixTransform>
#include <osg/PolygonMode>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/io_utils>

#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QTableWidgetItem>

#include <sstream>

static osg::Geode* make_box(const QColor& c) {
    auto box = new osg::Box();

    auto draw = new osg::ShapeDrawable(box);
    draw->setColor(osg::Vec4(c.redF(), c.greenF(), c.blueF(), c.alphaF()));

    auto geode = new osg::Geode();
    geode->addDrawable(draw);

    return geode;
}

static std::vector<QColor> get_colors(double seed, int count) {
    static constexpr double golden_ratio = 0.618033988749895;

    std::vector<QColor> result;
    double hue = seed * golden_ratio;
    for (int i = 0; i < count; i++) {
        hue = std::fmod(hue + golden_ratio, 1.0);
        result.push_back(QColor::fromHslF(hue, 1.0, 0.5));
    }
    return result;
}

template<typename T>
QString to_string(const T& t) {
    std::stringstream ss;
    ss << t;
    return QString::fromStdString(ss.str());
}

TreeDisplay::TreeDisplay(const QString& path, external_tree tree, QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::TreeDisplay)
    , m_path(path)
    , m_tree(std::move(tree))
{
    ui->setupUi(this);
    ui->treeTabs->setCurrentWidget(ui->childrenTab);
    ui->leafChildrenTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->internalChildrenTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->indexTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->summaryTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);

    ui->pathText->setText(m_path);
    ui->sizeText->setText(QString::number(m_tree.size()));
    ui->heightText->setText(QString::number(m_tree.height()));
    ui->fanoutText->setText(QString("%1 / %2")
                            .arg(m_tree.max_internal_entries())
                            .arg(m_tree.max_leaf_entries()));
    ui->nodesText->setText(QString("%1 / %2")
                            .arg(m_tree.internal_node_count())
                            .arg(m_tree.leaf_node_count()));

    connect(ui->internalChildrenTree, &QTreeWidget::itemActivated, this, &TreeDisplay::internalChildActivated);
    connect(ui->rootButton, &QPushButton::clicked, this, &TreeDisplay::rootActivated);
    connect(ui->parentButton, &QPushButton::clicked, this, &TreeDisplay::parentActivated);
    connect(ui->gotoButton, &QPushButton::clicked, this, &TreeDisplay::gotoActivated);

    connect(ui->sceneRenderer, &SceneRenderer::eyeChanged, this, &TreeDisplay::refreshDirection);
    connect(ui->sceneRenderer, &SceneRenderer::centerChanged, this, &TreeDisplay::refreshDirection);
    connect(ui->sceneRenderer, &SceneRenderer::upChanged, this, &TreeDisplay::refreshUp);
    connect(ui->resetButton, &QPushButton::clicked, ui->sceneRenderer, &SceneRenderer::reset);
    connect(ui->recurseBox, &QCheckBox::clicked, this, &TreeDisplay::recurseClicked);
    refreshDirection();
    refreshUp();

    // Initialize to root (if possible).
    if (!m_tree.empty()) {
        m_current = m_tree.root();
    }
    refreshNode();
}

TreeDisplay::~TreeDisplay()
{
    delete ui;
}

void TreeDisplay::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

static void visitNode(const external_tree::cursor& node, QTreeWidgetItem* parent, int depth, int maxdepth) {
    if (node.is_internal()) {
        for (size_t i = 0; i < node.size(); ++i) {
            QString label = QString::number(i) + " (id: " + QString::number(node.child_id(i)) + ")";
            QTreeWidgetItem* item = new QTreeWidgetItem(parent, QStringList(label));
            if (depth < maxdepth) {
                visitNode(node.child(i), item, depth + 1, maxdepth);
            }
        }
    } else {
        for (size_t i = 0; i < node.size(); ++i) {
            external_tree::value_type value = node.value(i);
            QString label = QString("%1 (trajectory: %2, unit: %3)")
                    .arg(i)
                    .arg(value.trajectory_id)
                    .arg(value.unit_index);
            new QTreeWidgetItem(parent, QStringList(label));
        }
    }
}

static QString get_path(external_tree::cursor node) {
    QString path;
    while (1) {
        QString id = QString::number(node.id());
        if (!path.isEmpty())
            id += "/";
        path = id + path;

        if (!node.has_parent())
            break;

        node.move_parent();
    }
    return path;
}

static QString get_type(const external_tree::cursor& node) {
    if (node.is_leaf()) {
        return "Leaf";
    }
    if (node.is_root()) {
        return "Internal (root)";
    }
    return "Internal";
}

static QString get_mbb(const external_tree::cursor& node) {
    std::stringstream ss;
    ss << node.mbb();
    return QString::fromStdString(ss.str());
}

void TreeDisplay::refreshNode() {
    if (m_current) {
        ui->nodeIdText->setText(QString::number(m_current->id()));
        ui->nodeTypeText->setText(get_type(*m_current));
        ui->nodePathText->setText(get_path(*m_current));
        ui->nodeMbbText->setText(get_mbb(*m_current));
        ui->nodeEntriesText->setText(QString("%1/%2")
                                     .arg(m_current->size())
                                     .arg(m_current->max_size()));

        ui->parentButton->setEnabled(m_current->has_parent());
        ui->rootButton->setEnabled(!m_current->is_root());
        ui->gotoButton->setEnabled(true);
    } else {
        ui->nodeIdText->setText("N/A");
        ui->nodeTypeText->setText("N/A");
        ui->nodePathText->setText("N/A");
        ui->nodeMbbText->setText("N/A");
        ui->nodeEntriesText->setText("N/A");

        ui->parentButton->setEnabled(false);
        ui->rootButton->setEnabled(false);
        ui->gotoButton->setEnabled(false);
    }

    refreshChildren();
    refreshIndex();
    refreshSummary();
    refreshScene();
}

void TreeDisplay::refreshChildren() {
    QTreeWidget* internal = ui->internalChildrenTree;
    QTreeWidget* leaf = ui->leafChildrenTree;
    internal->clear();
    leaf->clear();

    if (!m_current) {
        ui->stackedWidget->setCurrentWidget(ui->internalPage);
        return;
    }

    if (m_current->is_internal()) {
        for (size_t i = 0; i < m_current->size(); ++i) {
            QString index = QString::number(i);
            QString box = to_string(m_current->mbb(i));
            QString ptr = QString::number(m_current->child_id(i));

            QTreeWidgetItem* item = new QTreeWidgetItem(internal->invisibleRootItem(), QStringList{index, box, ptr});
            item->setData(0, Qt::UserRole, qint64(i));
        }
        ui->stackedWidget->setCurrentWidget(ui->internalPage);
    } else {
        for (size_t i = 0; i < m_current->size(); ++i) {
            auto data = m_current->value(i);

            QString index = QString::number(i);
            QString id = QString("%1, %2").arg(data.trajectory_id).arg(data.unit_index);
            QString start = to_string(data.unit.start);
            QString end = to_string(data.unit.end);
            QString label = QString::number(data.unit.label);
            new QTreeWidgetItem(leaf->invisibleRootItem(), QStringList{index, id, start, end, label});
        }
        ui->stackedWidget->setCurrentWidget(ui->leafPage);
    }
}

void TreeDisplay::refreshIndex() {
    QTreeWidget* tree = ui->indexTree;
    tree->clear();

    if (!m_current || m_current->is_leaf()) {
        return;
    }

    auto append = [&](const QString& title, auto list) {
        QTreeWidgetItem* parent = new QTreeWidgetItem(QStringList(title));

        auto items = list->all();
        std::sort(items.begin(), items.end(), [&](auto&& a, auto&& b) { return a.node() < b.node(); });
        for (const external_tree::posting_type&  p : items) {
            QString index = QString::number(p.node());
            QString count = QString::number(p.count());
            QString trajectories = to_string(p.id_set());
            QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{index, count, trajectories});
            parent->addChild(item);
        }

        tree->addTopLevelItem(parent);
    };

    auto index = m_current->inverted_index();
    append("Total", index->total());
    for (auto item : *index) {
        auto list = item.postings_list();
        if (!list->empty()) {
            append(QString("Label %1").arg(item.label()), list);
        }
    }
}

void TreeDisplay::refreshSummary() {
    QTreeWidget* tree = ui->summaryTree;
    tree->clear();
    tree->setSortingEnabled(false);

    if (!m_current) {
        return;
    }

    auto insert_summary = [&](auto&& label, auto&& summary) {
        auto* item = new QTreeWidgetItem;
        item->setData(0, Qt::DisplayRole, label);
        item->setData(1, Qt::DisplayRole, qint64(summary.count));
        item->setData(2, Qt::DisplayRole, to_string(summary.trajectories));
        tree->addTopLevelItem(item);
    };

    if (m_current->is_internal()) {
        auto index = m_current->inverted_index();

        insert_summary(QString("Total"), index->total()->summarize());
        for (const auto& entry : *index) {
            const auto label = entry.label();
            const auto summary = entry.postings_list()->summarize();
            insert_summary(qint64(label), summary);
        }
    } else {
        struct item {
            std::set<geodb::trajectory_id_type> trajectories;
            u64 count = 0;
        };

        u64 total_count = 0;
        std::set<geodb::trajectory_id_type> total_trajectories;
        std::map<geodb::label_type, item> items;
        for (size_t i = 0; i < m_current->size(); ++i) {
            const geodb::tree_entry entry = m_current->value(i);

            auto& item = items[entry.unit.label];
            ++item.count;
            item.trajectories.insert(entry.trajectory_id);

            ++total_count;
            total_trajectories.insert(entry.trajectory_id);
        }

        geodb::postings_list_summary<external_tree::lambda()> total_summary;
        total_summary.count = total_count;
        total_summary.trajectories.assign(total_trajectories.begin(), total_trajectories.end());
        insert_summary(QString("Total"), total_summary);
        for (const auto& pair : items) {
            geodb::postings_list_summary<external_tree::lambda()> summary;
            summary.count = pair.second.count;
            summary.trajectories.assign(pair.second.trajectories.begin(),
                                        pair.second.trajectories.end());

            insert_summary(pair.first, summary);
        }
    }

    tree->sortByColumn(1, Qt::DescendingOrder);
    tree->setSortingEnabled(true);
}

void TreeDisplay::refreshScene() {
    if (!m_current) {
        ui->sceneRenderer->setSceneData(nullptr);
        return;
    }

    osg::ref_ptr<osg::Node> scene = m_recurse ? createRecursiveScene(*m_current)
                                              : createScene(*m_current);
    osg::StateSet* set = scene->getOrCreateStateSet();

    set->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE));
    set->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);

    osg::Material* material = new osg::Material;
    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
    set->setAttributeAndModes(material, osg::StateAttribute::ON);

    ui->sceneRenderer->setSceneData(scene);
}

void TreeDisplay::internalChildActivated(const QTreeWidgetItem* item, int column) {
    if (!item || column < 0) {
        return;
    }

    // Tree item stores child index at [0, UserRole]
    QVariant data = item->data(0, Qt::UserRole);
    assert(data.isValid());
    assert(data.canConvert<qint64>());

    size_t child = data.value<qint64>();
    assert(m_current);
    assert(child < m_current->size());
    m_current->move_child(child);
    refreshNode();
}

void TreeDisplay::rootActivated() {
    if (!m_current) {
        return;
    }
    m_current->move_root();
    refreshNode();
}

void TreeDisplay::parentActivated() {
    if (!m_current || !m_current->has_parent()) {
        return;
    }
    m_current->move_parent();
    refreshNode();
}

void TreeDisplay::gotoActivated() {
    if (!m_current) {
        return;
    }

    QString pathText = QInputDialog::getText(this, "Go to node",
                                             "Enter the node path (node IDs separated by \"/\"):");
    if (pathText.isNull()) {
        return;
    }

    QStringList path = pathText.split("/", QString::SkipEmptyParts);
    if (path.empty()) {
        m_current->move_root();
        refreshNode();
        return;
    }

    using node_id = external_tree::cursor::node_id;

    try {
        // Parses the node id from the text or throws QString.
        auto get_id = [](const QString& idText) {
            bool ok = false;
            node_id result = idText.toULongLong(&ok);
            if (!ok) {
                throw QString("Invalid node id: %1.").arg(idText);
            }
            return result;
        };

        // Returns the index of `child` in `c` or throws a QString.
        auto index_of = [](const external_tree::cursor& c, node_id child) {
            geodb_assert(c.is_internal(), "Must be an internal node");
            const size_t size = c.size();
            for (size_t i = 0; i < size; ++i) {
                if (c.child_id(i) == child)
                    return i;
            }
            throw QString("%1 is not a child of %2.").arg(child).arg(c.id());
        };

        external_tree::cursor c = m_current->root();
        if (get_id(path.front()) != c.id()) {
            throw QString("First node id must point to the root.");
        }
        path.pop_front();

        for (const QString& idText : path) {
            node_id childId = get_id(idText);
            if (!c.is_internal()) {
                throw QString("Cannot navigate to %1 because %2 is already a leaf.")
                        .arg(childId)
                        .arg(c.id());
            }
            size_t index = index_of(c, childId);
            c.move_child(index);
        }

        m_current = c;
        refreshNode();
    } catch (QString error) {
        QMessageBox::critical(this, "Error", error);
    }
}

void TreeDisplay::recurseClicked(bool checked) {
    if (checked == m_recurse) {
        return;
    }

    m_recurse = checked;
    refreshScene();
}

void TreeDisplay::refreshDirection() {
    auto dir = ui->sceneRenderer->center() - ui->sceneRenderer->eye();
    ui->directionText->setText(to_string(dir));
}

void TreeDisplay::refreshUp() {
    ui->upText->setText(to_string(ui->sceneRenderer->up()));
}

static osg::Node* make_box(const geodb::bounding_box& mbb, const osg::Matrix& global, const QColor& c) {
    geodb::vector3 center = mbb.center();
    geodb::vector3 widths = mbb.max() - mbb.min();

    osg::Matrix m1 = osg::Matrix::scale(widths.x(), widths.y(), widths.t());
    osg::Matrix m2 = osg::Matrix::translate(center.x(), center.y(), center.t());
    osg::MatrixTransform* tx = new osg::MatrixTransform(m1 * m2 * global);
    tx->addChild(make_box(c));
    return tx;
}

static osg::Matrix global_transform(const geodb::bounding_box& root) {
    // Rescale everything to [0, 1000].
    geodb::vector3 width = root.max() - root.min();
    return osg::Matrix::scale(1000.0 / double(width.x()),
                              1000.0 / double(width.y()),
                              1000.0 / double(width.t()));
}

osg::Node* TreeDisplay::createScene(const external_tree::cursor& node) {
    osg::ref_ptr<osg::Group> group = new osg::Group;
    std::vector<QColor> colors = get_colors(node.id(), node.size());

    osg::Matrix global = global_transform(node.mbb());
    for (size_t i = 0; i < node.size(); ++i) {
        group->addChild(make_box(node.mbb(i), global, colors[i]));
    }
    return group.release();
}

// Counts the direct children of `c` and their direct children.
static size_t count_nodes(const external_tree::cursor& c) {
    external_tree::cursor node = c;
    size_t count = node.size();
    if (node.is_internal()) {
        for (size_t i = 0; i < node.size(); ++i) {
            node.move_child(i);
            count += node.size();
            node.move_parent();
        }
    }
    return count;
}

static osg::Node* make_tree(external_tree::cursor& node,
                            const osg::Matrix& global)
{
    std::vector<QColor> colors = get_colors(node.id(), node.size() + 1);
    size_t index = 0;
    QColor color = colors[index++];
    osg::ref_ptr<osg::Group> group = new osg::Group;

    if (node.is_internal()) {
        for (size_t i = 0; i < node.size(); ++i) {
            group->addChild(make_box(node.mbb(i), global, color));

            node.move_child(i);
            {
                osg::ref_ptr<osg::Group> child_group = new osg::Group;
                QColor child_color = colors[index++];
                for (size_t j = 0; j < node.size(); ++j) {
                    child_group->addChild(make_box(node.mbb(j), global, child_color));
                }
                group->addChild(child_group);
            }
            node.move_parent();
        }
    } else {
        for (size_t i = 0; i < node.size(); ++i) {
            group->addChild(make_box(node.mbb(i), global, color));
        }
    }
    return group.release();
}

osg::Node* TreeDisplay::createRecursiveScene(const external_tree::cursor& node) {
    std::vector<QColor> colors = get_colors(node.id(), count_nodes(node));
    external_tree::cursor copy = node;
    return make_tree(copy, global_transform(node.mbb()));
}

ext: cpp

max_stars_repo_name: mbeckem/msc

max_issues_repo_name: mbeckem/msc

max_forks_repo_name: mbeckem/msc


text:
/**
 * @file   EigenWriter.hpp
 * @author  <hungptit@gmail.com>
 * @date   Tue Oct  2 23:05:04 2012
 *
 * @brief
 *
 *
 */
#ifndef EIGENWRITER_HPP_
#define EIGENWRITER_HPP_

#include <algorithm>
#include <Eigen/Dense>
#include "Writer.hpp"

namespace H5Interface {
    class EigenWriter : public Writer {
      public:
        EigenWriter (const std::string & fileName);

        template<class _Matrix>
        void write(const std::string & dataSetName,
                   const _Matrix & mat,
                   const H5Traits::dimension_type & chunkDimensions,
                   const int compressionLevel = BEST_COMPRESSION);

        template<class _Matrix>
        void write(const std::string & dataSetName,
                   const _Matrix & mat,
                   const int compressionLevel = BEST_COMPRESSION);
    };

#include "private/EigenWriter.cpp"
}

#endif


text:
#include "grade_school.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

namespace boost
{

// teach Boost.Test how to print std::vector
template <typename T>
inline wrap_stringstream&
operator<<(wrap_stringstream& wrapped, std::vector<T> const& item)
{
    wrapped << '[';
    bool first = true;
    for (auto const& element : item) {
        wrapped << (!first ? "," : "") << element;
        first = false;
    }
    return wrapped << ']';
}

// teach Boost.Test how to print std::pair
template <typename K, typename V>
inline wrap_stringstream&
operator<<(wrap_stringstream& wrapped, std::pair<const K, V> const& item)
{
    return wrapped << '<' << item.first << ',' << item.second << '>';
}

}

#define REQUIRE_EQUAL_CONTAINERS(left_, right_) \
    BOOST_REQUIRE_EQUAL_COLLECTIONS(left_.begin(), left_.end(), right_.begin(), right_.end())

using namespace std;

struct school_fixture
{
    grade_school::school school_;
};

BOOST_FIXTURE_TEST_SUITE(school_suite, school_fixture);

BOOST_AUTO_TEST_CASE(a_new_school_has_an_empty_roster)
{
    BOOST_REQUIRE(school_.roster().empty());
}

#if defined(EXERCISM_RUN_ALL_TESTS)
BOOST_AUTO_TEST_CASE(adding_a_student_adds_them_to_the_roster_for_the_given_grade)
{
    school_.add("Aimee", 2);

    const auto actual = school_.roster();

    const map<int, vector<string>> expected{{2, {"Aimee"}}};
    REQUIRE_EQUAL_CONTAINERS(expected, actual);
}

BOOST_AUTO_TEST_CASE(adding_more_students_to_the_same_grade_adds_them_to_the_roster)
{
    school_.add("Blair", 2);
    school_.add("James", 2);
    school_.add("Paul", 2);

    const auto actual = school_.roster();

    const map<int, vector<string>> expected{{2, {"Blair", "James", "Paul"}}};
    REQUIRE_EQUAL_CONTAINERS(expected, actual);
}

BOOST_AUTO_TEST_CASE(adding_students_to_different_grades_adds_them_to_the_roster)
{
    school_.add("Chelsea", 3);
    school_.add("Logan", 7);

    const auto actual = school_.roster();

    const map<int, vector<string>> expected{{3, {"Chelsea"}}, {7, {"Logan"}}};
    REQUIRE_EQUAL_CONTAINERS(expected, actual);
}

BOOST_AUTO_TEST_CASE(grade_returns_the_students_in_that_grade_in_alphabetical_order)
{
    school_.add("Franklin", 5);
    school_.add("Bradley", 5);
    school_.add("Jeff", 1);

    const auto actual = school_.grade(5);

    const vector<string> expected{"Bradley", "Franklin"};
    REQUIRE_EQUAL_CONTAINERS(expected, actual);
}

BOOST_AUTO_TEST_CASE(grade_returns_an_empty_array_if_there_are_no_students_in_that_grade)
{
    const auto actual = school_.grade(1);

    BOOST_REQUIRE(actual.empty());
}

BOOST_AUTO_TEST_CASE(the_student_names_in_each_grade_in_the_roster_are_sorted)
{
    school_.add("Jennifer", 4);
    school_.add("Kareem", 6);
    school_.add("Christopher", 4);
    school_.add("Kyle", 3);

    const auto actual = school_.roster();

    const map<int, vector<string>> expected{
            {3, {"Kyle"}},
            {4, {"Christopher", "Jennifer"}},
            {6, {"Kareem"}}
        };
    REQUIRE_EQUAL_CONTAINERS(expected, actual);
}
#endif

BOOST_AUTO_TEST_SUITE_END();

max_stars_repo_name: LegalizeAdulthood/exercism-xcpp

max_issues_repo_name: LegalizeAdulthood/exercism-xcpp

max_forks_repo_name: LegalizeAdulthood/exercism-xcpp


text:
#include <boost/math/special_functions/spherical_harmonic.hpp>
#include <iostream>

#include "../src/spherical_harmonics.hpp"

using namespace fsph;
using namespace boost::math;

int main(int argc, char **argv)
{
    unsigned int lmax(5);
    float phi(0.25), theta(1.5*M_PI);

    PointSPHEvaluator<float> eval(lmax);
    eval.compute(phi, theta);
    PointSPHEvaluator<float>::iterator iter(eval.begin(true));

    double error(0);
    unsigned int N(0);

    for(unsigned int l(0); l <= lmax; ++l)
    {
        for(unsigned int m(0); m <= l; ++m)
        {
            std::complex<float> from_fsph(*iter);
            // std::cout << iter.grad_phi(phi, theta) << ' ' << iter.grad_theta() << std::endl;
            // boost names phi and theta using the opposite convention
            std::complex<float> from_boost(spherical_harmonic(l, m, phi, theta));
            from_boost *= pow(-1, m);
            error += abs(from_fsph - from_boost);
            if(abs(from_fsph - from_boost) > 1e-5)
                std::cout << l << ' ' << m << ' ' << from_fsph << ' ' << from_boost << std::endl;
            ++N;
            ++iter;
        }
        for(unsigned int m(1); m <= l; ++m)
        {
            std::complex<float> from_fsph(*iter);
            // std::cout << iter.grad_phi(phi, theta) << ' ' << iter.grad_theta() << std::endl;
            // boost names phi and theta using the opposite convention
            std::complex<float> from_boost(spherical_harmonic(l, -(int)m, phi, theta));
            error += abs(from_fsph - from_boost);
            if(abs(from_fsph - from_boost) > 1e-5)
                std::cout << l << ' ' << m << ' ' << from_fsph << ' ' << from_boost << std::endl;
            ++N;
            ++iter;
        }
    }

    const unsigned int start_l(2);
    const unsigned int start_m(1);
    PointSPHEvaluator<float>::iterator iter_l(eval.begin_l(start_l, start_m, false));

    for(unsigned int l(start_l); l <= lmax; ++l)
    {
        for(unsigned int m(l == start_l? start_m: 0); m <= l; ++m)
        {
            std::complex<float> from_fsph(*iter_l);
            // std::cout << iter_l.grad_phi(phi, theta) << ' ' << iter_l.grad_theta() << std::endl;
            // boost names phi and theta using the opposite convention
            std::complex<float> from_boost(spherical_harmonic(l, m, phi, theta));
            from_boost *= pow(-1, m);
            error += abs(from_fsph - from_boost);
            if(abs(from_fsph - from_boost) > 1e-5)
                std::cout << l << ' ' << m << ' ' << from_fsph << ' ' << from_boost << std::endl;
            ++N;
            ++iter_l;
        }
    }

    return error/N > 1e-7;
}

max_stars_repo_name: glotzerlab/fsph

max_issues_repo_name: glotzerlab/fsph

max_forks_repo_name: glotzerlab/fsph


text:
#include <iostream>

#include <array>
#include <cassert>

#include <boost/hana.hpp>
#include <boost/hana/at_key.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/type.hpp>

#include <cmbml/cdr/serialize_anything.hpp>
#include <cmbml/cdr/deserialize_anything.hpp>

#include <cmbml/message/data.hpp>
#include <cmbml/message/submessage.hpp>
#include <cmbml/message/message.hpp>

namespace hana = boost::hana;

int main(int argc, char** argv) {

  // Nominal place_integral_type test (widening destination)
  {
    uint32_t dst = 0;
    uint8_t src = 1;
    size_t index = 0;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == (1 << 8) + 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == (1 << 16) + (1 << 8) + 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == (1 << 24) + (1 << 16) + (1 << 8) + 1);
  }

  // Nominal place_integral_type test (shrinking destination)
  {
    uint32_t src = 0x01010101;
    uint8_t dst = 0;
    size_t index = 0;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == 1);
    index += 8;
    cmbml::place_integral_type(src, dst, index);
    assert(dst == 1);
  }

  // Serialization test
  // TODO compile-time inference of the serialized array for fixed sizes
  //
  std::array<uint32_t, 1024> serialized_data;
  {

    uint16_t test_int = 3;
    cmbml::serialize(test_int, serialized_data);
    assert(serialized_data[0] == 3);

    // How to know when to call this callback?
    auto confirm_int_callback = [test_int](auto deserialized_value) {
      assert(deserialized_value == test_int);
      return cmbml::StatusCode::ok;
    };
    size_t index = 0;
    cmbml::deserialize<uint32_t>(serialized_data, index, confirm_int_callback);
  }

  {
    std::array<uint8_t, 4> example_src;
    std::array<uint32_t, 2> example_dst;

    for (uint8_t i = 0; i < example_src.size(); ++i) {
      example_src[i] = i;
    }
    auto confirm_array_callback = [example_src](auto && deserialized_array) {
      for (size_t i = 0; i < example_src.size(); ++i) {
        assert(example_src[i] == deserialized_array[i]);
      }
      return cmbml::StatusCode::ok;
    };
    cmbml::serialize(example_src, example_dst);
    assert(example_dst[0] == example_src.size());
    assert(example_dst[1] == 0x3020100);
    size_t index = 0;
    cmbml::deserialize<decltype(example_src)>(example_dst, index, confirm_array_callback);
  }

  {
    // Validate serialize/deserialize via the callback passed the deserialize
    cmbml::SubmessageHeader sub_header;
    cmbml::serialize(sub_header, serialized_data);
    // TODO validate values
    auto callback = [&sub_header](auto && x) {
      return cmbml::StatusCode::ok;
     };

    size_t index = 0;
    cmbml::deserialize<cmbml::SubmessageHeader>(serialized_data, index, callback);
  }

  {
    size_t index = 0;
    cmbml::Submessage<cmbml::AckNack> submsg;
    cmbml::serialize(submsg, serialized_data);
    auto callback = [](auto && msg) {
      return cmbml::StatusCode::ok;
    };
    cmbml::deserialize<cmbml::AckNack>(serialized_data, index, callback);
  }

  hana::tuple<cmbml::AckNack, cmbml::Data, cmbml::Gap,
    cmbml::Heartbeat, cmbml::InfoDestination,
    cmbml::InfoReply, cmbml::InfoSource, cmbml::InfoTimestamp, cmbml::NackFrag> types;

  hana::for_each(types, [&serialized_data](const auto & x) {
    cmbml::serialize(x, serialized_data);
    using MsgT = std::decay<decltype(x)>;
    size_t index = 0;
    cmbml::deserialize(x, serialized_data, index);
  });

  printf("All tests passed.\n");
  return 0;
}

max_stars_repo_name: osrf/cmbml

max_issues_repo_name: osrf/cmbml

max_forks_repo_name: osrf/cmbml


text:
// @file oid.cpp

/*    Copyright 2009 10gen Inc.
 *
 *    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 "mongo/platform/basic.h"

#include "mongo/bson/oid.h"

#include <boost/functional/hash.hpp>
#include <boost/scoped_ptr.hpp>

#include "mongo/base/init.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/random.h"
#include "mongo/util/hex.h"

namespace mongo {

namespace {
    boost::scoped_ptr<AtomicUInt32> counter;

    const std::size_t kTimestampOffset = 0;
    const std::size_t kInstanceUniqueOffset = kTimestampOffset +
                                              OID::kTimestampSize;
    const std::size_t kIncrementOffset = kInstanceUniqueOffset +
                                         OID::kInstanceUniqueSize;
    OID::InstanceUnique _instanceUnique;
}  // namespace

    MONGO_INITIALIZER_GENERAL(OIDGeneration, MONGO_NO_PREREQUISITES, ("default"))
        (InitializerContext* context) {
        boost::scoped_ptr<SecureRandom> entropy(SecureRandom::create());
        counter.reset(new AtomicUInt32(uint32_t(entropy->nextInt64())));
        _instanceUnique = OID::InstanceUnique::generate(*entropy);
        return Status::OK();
    }

    OID::Increment OID::Increment::next() {
        uint64_t nextCtr = counter->fetchAndAdd(1);
        OID::Increment incr;

        incr.bytes[0] = uint8_t(nextCtr >> 16);
        incr.bytes[1] = uint8_t(nextCtr >> 8);
        incr.bytes[2] = uint8_t(nextCtr);

        return incr;
    }

    OID::InstanceUnique OID::InstanceUnique::generate(SecureRandom& entropy) {
        int64_t rand = entropy.nextInt64();
        OID::InstanceUnique u;
        std::memcpy(u.bytes, &rand, kInstanceUniqueSize);
        return u;
    }

    void OID::setTimestamp(const OID::Timestamp timestamp) {
        _view().writeBE<Timestamp>(timestamp, kTimestampOffset);
    }

    void OID::setInstanceUnique(const OID::InstanceUnique unique) {
        // Byte order doesn't matter here
        _view().writeNative<InstanceUnique>(unique, kInstanceUniqueOffset);
    }

    void OID::setIncrement(const OID::Increment inc) {
        _view().writeNative<Increment>(inc, kIncrementOffset);
    }

    OID::Timestamp OID::getTimestamp() const {
        return view().readBE<Timestamp>(kTimestampOffset);
    }

    OID::InstanceUnique OID::getInstanceUnique() const {
        // Byte order doesn't matter here
        return view().readNative<InstanceUnique>(kInstanceUniqueOffset);
    }

    OID::Increment OID::getIncrement() const {
        return view().readNative<Increment>(kIncrementOffset);
    }

    void OID::hash_combine(size_t &seed) const {
        uint32_t v;
        for (int i = 0; i != kOIDSize; i += sizeof(uint32_t)) {
            memcpy(&v, _data + i, sizeof(uint32_t));
            boost::hash_combine(seed, v);
        }
    }

    size_t OID::Hasher::operator() (const OID& oid) const {
        size_t seed = 0;
        oid.hash_combine(seed);
        return seed;
    }

    void OID::regenMachineId() {
        boost::scoped_ptr<SecureRandom> entropy(SecureRandom::create());
        _instanceUnique = InstanceUnique::generate(*entropy);
    }

    unsigned OID::getMachineId() {
        uint32_t ret = 0;
        std::memcpy(&ret, _instanceUnique.bytes, sizeof(uint32_t));
        return ret;
    }

    void OID::justForked() {
        regenMachineId();
    }

    void OID::init() {
        // each set* method handles endianness
        setTimestamp(time(0));
        setInstanceUnique(_instanceUnique);
        setIncrement(Increment::next());
    }

    void OID::init( const std::string& s ) {
        verify( s.size() == 24 );
        const char *p = s.c_str();
        for (std::size_t i = 0; i < kOIDSize; i++) {
            _data[i] = fromHex(p);
            p += 2;
        }
    }

    void OID::init(Date_t date, bool max) {
        setTimestamp(uint32_t(date / 1000));
        uint64_t rest = max ? std::numeric_limits<uint64_t>::max() : 0u;
        std::memcpy(_view().view(kInstanceUniqueOffset), &rest,
                    kInstanceUniqueSize + kIncrementSize);
    }

    time_t OID::asTimeT() const {
        return getTimestamp();
    }

    std::string OID::toString() const {
        return toHexLower(_data, kOIDSize);
    }

    std::string OID::toIncString() const {
        return toHexLower(getIncrement().bytes, kIncrementSize);
    }

}  // namespace mongo

MONGO_INITIALIZER_FUNCTION_ASSURE_FILE(bson_oid)

max_stars_repo_name: engineer-legion/mongo-cxx-driver

max_issues_repo_name: samantharitter/mongo-cxx-driver

max_forks_repo_name: samantharitter/mongo-cxx-driver


text:
/*
 * Copyright (c) 2010-2012 frankee zhou (frankee.zhou at gmail dot com)
 *
 * Distributed under 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 <cetty/shuttle/protobuf/ProtobufProxyCodec.h>

#include <boost/assert.hpp>
#include <google/protobuf/message.h>

#include <cetty/buffer/Unpooled.h>
#include <cetty/buffer/ChannelBuffer.h>
#include <cetty/buffer/ChannelBufferUtil.h>
#include <cetty/protobuf/service/service.pb.h>
#include <cetty/protobuf/service/handler/MessageCodec.h>
#include <cetty/shuttle/protobuf/ProtobufProxyMessage.h>

namespace cetty {
namespace shuttle {
namespace protobuf {

using namespace cetty::buffer;
using namespace cetty::channel;
using namespace cetty::protobuf::service;
using namespace cetty::protobuf::service::handler;

ProtobufProxyMessagePtr ProtobufProxyCodec::decode(ChannelHandlerContext& ctx,
        const ChannelBufferPtr& msg) {
    if (msg) {
        ProtobufProxyMessagePtr message(new ProtobufProxyMessage);

        int size = msg->readableBytes();
        ChannelBufferPtr buffer = Unpooled::buffer(size + 8, 4);
        msg->getBytes(msg->readerIndex(), buffer, size);

        if (!MessageCodec::decodeMessageHead(msg, &message->message())) {
            message->setBuffer(buffer);
            return message;
        }
        else {
            LOG_WARN << "decode Message failed.";
        }
    }
    else {
        LOG_WARN << "input channel buffer empty, decode failed.";
    }

    return ProtobufProxyMessagePtr();
}

ChannelBufferPtr ProtobufProxyCodec::encode(ChannelHandlerContext& ctx,
        const ProtobufProxyMessagePtr& msg) {
    if (msg) {
        return msg->buffer();
    }
    else {
        return ChannelBufferPtr();
    }
}

}
}
}


text:
#pragma once

#include <opencv2/opencv.hpp>
#include <Eigen/Dense>

class HomographyModel {
 public:
  HomographyModel(const Eigen::Matrix3d& matrix);
  HomographyModel();
  cv::Point2f operator()(const cv::Point2f& p) const;
  void print() const;
  void operator=(const HomographyModel& o);
  HomographyModel inverse();
  cv::Mat toMat();
 private:
  Eigen::Matrix3d _matrix;
};