<commit_before>/**
 *  @file
 *  @copyright defined in eos/LICENSE
 */
#include <sstream>

#include <eosio/chain/snapshot.hpp>
#include <eosio/testing/tester.hpp>

#include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>

#include <contracts.hpp>
#include <snapshots.hpp>

using namespace eosio;
using namespace testing;
using namespace chain;

class snapshotted_tester : public base_tester {
public:
   snapshotted_tester(controller::config config, const snapshot_reader_ptr& snapshot, int ordinal) {
      FC_ASSERT(config.blocks_dir.filename().generic_string() != "."
         && config.state_dir.filename().generic_string() != ".", "invalid path names in controller::config");

      controller::config copied_config = config;
      copied_config.blocks_dir =
              config.blocks_dir.parent_path() / std::to_string(ordinal).append(config.blocks_dir.filename().generic_string());
      copied_config.state_dir =
              config.state_dir.parent_path() / std::to_string(ordinal).append(config.state_dir.filename().generic_string());

      init(copied_config, snapshot);
   }

   snapshotted_tester(controller::config config, const snapshot_reader_ptr& snapshot, int ordinal, int copy_block_log_from_ordinal) {
      FC_ASSERT(config.blocks_dir.filename().generic_string() != "."
         && config.state_dir.filename().generic_string() != ".", "invalid path names in controller::config");

      controller::config copied_config = config;
      copied_config.blocks_dir =
              config.blocks_dir.parent_path() / std::to_string(ordinal).append(config.blocks_dir.filename().generic_string());
      copied_config.state_dir =
              config.state_dir.parent_path() / std::to_string(ordinal).append(config.state_dir.filename().generic_string());

      // create a copy of the desired block log and reversible
      auto block_log_path = config.blocks_dir.parent_path() / std::to_string(copy_block_log_from_ordinal).append(config.blocks_dir.filename().generic_string());
      fc::create_directories(copied_config.blocks_dir);
      fc::copy(block_log_path / "blocks.log", copied_config.blocks_dir / "blocks.log");
      fc::copy(block_log_path / config::reversible_blocks_dir_name, copied_config.blocks_dir / config::reversible_blocks_dir_name );

      init(copied_config, snapshot);
   }
   signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
      return _produce_block(skip_time, false);
   }

   signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
      control->abort_block();
      return _produce_block(skip_time, true);
   }

   signed_block_ptr finish_block()override {
      return _finish_block();
   }

   bool validate() { return true; }
};

struct variant_snapshot_suite {
   using writer_t = variant_snapshot_writer;
   using reader_t = variant_snapshot_reader;
   using write_storage_t = fc::mutable_variant_object;
   using snapshot_t = fc::variant;

   struct writer : public writer_t {
      writer( const std::shared_ptr<write_storage_t>& storage )
      :writer_t(*storage)
      ,storage(storage)
      {

      }

      std::shared_ptr<write_storage_t> storage;
   };

   struct reader : public reader_t {
      explicit reader(const snapshot_t& storage)
      :reader_t(storage)
      {}
   };

   static auto get_writer() {
      return std::make_shared<writer>(std::make_shared<write_storage_t>());
   }

   static auto finalize(const std::shared_ptr<writer>& w) {
      w->finalize();
      return snapshot_t(*w->storage);
   }

   static auto get_reader( const snapshot_t& buffer) {
      return std::make_shared<reader>(buffer);
   }

   template<typename Snapshot>
   static snapshot_t load_from_file() {
      return Snapshot::json();
   }
};

struct buffered_snapshot_suite {
   using writer_t = ostream_snapshot_writer;
   using reader_t = istream_snapshot_reader;
   using write_storage_t = std::ostringstream;
   using snapshot_t = std::string;
   using read_storage_t = std::istringstream;

   struct writer : public writer_t {
      writer( const std::shared_ptr<write_storage_t>& storage )
      :writer_t(*storage)
      ,storage(storage)
      {

      }

      std::shared_ptr<write_storage_t> storage;
   };

   struct reader : public reader_t {
      explicit reader(const std::shared_ptr<read_storage_t>& storage)
      :reader_t(*storage)
      ,storage(storage)
      {}

      std::shared_ptr<read_storage_t> storage;
   };

   static auto get_writer() {
      return std::make_shared<writer>(std::make_shared<write_storage_t>());
   }

   static auto finalize(const std::shared_ptr<writer>& w) {
      w->finalize();
      return w->storage->str();
   }

   static auto get_reader( const snapshot_t& buffer) {
      return std::make_shared<reader>(std::make_shared<read_storage_t>(buffer));
   }

   template<typename Snapshot>
   static snapshot_t load_from_file() {
      return Snapshot::bin();
   }
};

BOOST_AUTO_TEST_SUITE(snapshot_tests)

using snapshot_suites = boost::mpl::list<variant_snapshot_suite, buffered_snapshot_suite>;

BOOST_AUTO_TEST_CASE_TEMPLATE(test_exhaustive_snapshot, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain;

   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();

   static const int generation_count = 8;
   std::list<snapshotted_tester> sub_testers;

   for (int generation = 0; generation < generation_count; generation++) {
      // create a new snapshot child
      auto writer = SNAPSHOT_SUITE::get_writer();
      chain.control->write_snapshot(writer);
      auto snapshot = SNAPSHOT_SUITE::finalize(writer);

      // create a new child at this snapshot
      sub_testers.emplace_back(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), generation);

      // increment the test contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce block
      auto new_block = chain.produce_block();

      // undo the auto-pending from tester
      chain.control->abort_block();

      auto integrity_value = chain.control->calculate_integrity_hash();

      // push that block to all sub testers and validate the integrity of the database after it.
      for (auto& other: sub_testers) {
         other.push_block(new_block);
         BOOST_REQUIRE_EQUAL(integrity_value.str(), other.control->calculate_integrity_hash().str());
      }
   }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_replay_over_snapshot, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain;

   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();

   static const int pre_snapshot_block_count = 12;
   static const int post_snapshot_block_count = 12;

   for (int itr = 0; itr < pre_snapshot_block_count; itr++) {
      // increment the contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce block
      chain.produce_block();
   }

   chain.control->abort_block();
   auto expected_pre_integrity_hash = chain.control->calculate_integrity_hash();

   // create a new snapshot child
   auto writer = SNAPSHOT_SUITE::get_writer();
   chain.control->write_snapshot(writer);
   auto snapshot = SNAPSHOT_SUITE::finalize(writer);

   // create a new child at this snapshot
   snapshotted_tester snap_chain(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), 1);
   BOOST_REQUIRE_EQUAL(expected_pre_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());

   // push more blocks to build up a block log
   for (int itr = 0; itr < post_snapshot_block_count; itr++) {
      // increment the contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce & push block
      snap_chain.push_block(chain.produce_block());
   }

   // verify the hash at the end
   chain.control->abort_block();
   auto expected_post_integrity_hash = chain.control->calculate_integrity_hash();
   BOOST_REQUIRE_EQUAL(expected_post_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());

   // replay the block log from the snapshot child, from the snapshot
   snapshotted_tester replay_chain(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), 2, 1);
   BOOST_REQUIRE_EQUAL(expected_post_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_compatible_versions, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain(setup_policy::preactivate_feature_and_new_bios);

   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();
   auto base_integrity_value = chain.control->calculate_integrity_hash();

   {
      auto v2 = SNAPSHOT_SUITE::template load_from_file<snapshots::snap_v2>();
      snapshotted_tester v2_tester(chain.get_config(), SNAPSHOT_SUITE::get_reader(v2), 0);
      auto v2_integrity_value = v2_tester.control->calculate_integrity_hash();

      BOOST_CHECK_EQUAL(v2_integrity_value.str(), base_integrity_value.str());

      // create a latest snapshot
      auto latest_writer = SNAPSHOT_SUITE::get_writer();
      v2_tester.control->write_snapshot(latest_writer);
      auto latest = SNAPSHOT_SUITE::finalize(latest_writer);

      // load the latest snapshot
      snapshotted_tester latest_tester(chain.get_config(), SNAPSHOT_SUITE::get_reader(latest), 1);
      auto latest_integrity_value = latest_tester.control->calculate_integrity_hash();

      BOOST_REQUIRE_EQUAL(v2_integrity_value.str(), latest_integrity_value.str());
   }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>add a sigil that will trip once the unit test for version 2 is no longer necessary to maintain<commit_after>/**
 *  @file
 *  @copyright defined in eos/LICENSE
 */
#include <sstream>

#include <eosio/chain/snapshot.hpp>
#include <eosio/testing/tester.hpp>

#include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>

#include <contracts.hpp>
#include <snapshots.hpp>

using namespace eosio;
using namespace testing;
using namespace chain;

class snapshotted_tester : public base_tester {
public:
   snapshotted_tester(controller::config config, const snapshot_reader_ptr& snapshot, int ordinal) {
      FC_ASSERT(config.blocks_dir.filename().generic_string() != "."
         && config.state_dir.filename().generic_string() != ".", "invalid path names in controller::config");

      controller::config copied_config = config;
      copied_config.blocks_dir =
              config.blocks_dir.parent_path() / std::to_string(ordinal).append(config.blocks_dir.filename().generic_string());
      copied_config.state_dir =
              config.state_dir.parent_path() / std::to_string(ordinal).append(config.state_dir.filename().generic_string());

      init(copied_config, snapshot);
   }

   snapshotted_tester(controller::config config, const snapshot_reader_ptr& snapshot, int ordinal, int copy_block_log_from_ordinal) {
      FC_ASSERT(config.blocks_dir.filename().generic_string() != "."
         && config.state_dir.filename().generic_string() != ".", "invalid path names in controller::config");

      controller::config copied_config = config;
      copied_config.blocks_dir =
              config.blocks_dir.parent_path() / std::to_string(ordinal).append(config.blocks_dir.filename().generic_string());
      copied_config.state_dir =
              config.state_dir.parent_path() / std::to_string(ordinal).append(config.state_dir.filename().generic_string());

      // create a copy of the desired block log and reversible
      auto block_log_path = config.blocks_dir.parent_path() / std::to_string(copy_block_log_from_ordinal).append(config.blocks_dir.filename().generic_string());
      fc::create_directories(copied_config.blocks_dir);
      fc::copy(block_log_path / "blocks.log", copied_config.blocks_dir / "blocks.log");
      fc::copy(block_log_path / config::reversible_blocks_dir_name, copied_config.blocks_dir / config::reversible_blocks_dir_name );

      init(copied_config, snapshot);
   }
   signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
      return _produce_block(skip_time, false);
   }

   signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
      control->abort_block();
      return _produce_block(skip_time, true);
   }

   signed_block_ptr finish_block()override {
      return _finish_block();
   }

   bool validate() { return true; }
};

struct variant_snapshot_suite {
   using writer_t = variant_snapshot_writer;
   using reader_t = variant_snapshot_reader;
   using write_storage_t = fc::mutable_variant_object;
   using snapshot_t = fc::variant;

   struct writer : public writer_t {
      writer( const std::shared_ptr<write_storage_t>& storage )
      :writer_t(*storage)
      ,storage(storage)
      {

      }

      std::shared_ptr<write_storage_t> storage;
   };

   struct reader : public reader_t {
      explicit reader(const snapshot_t& storage)
      :reader_t(storage)
      {}
   };

   static auto get_writer() {
      return std::make_shared<writer>(std::make_shared<write_storage_t>());
   }

   static auto finalize(const std::shared_ptr<writer>& w) {
      w->finalize();
      return snapshot_t(*w->storage);
   }

   static auto get_reader( const snapshot_t& buffer) {
      return std::make_shared<reader>(buffer);
   }

   template<typename Snapshot>
   static snapshot_t load_from_file() {
      return Snapshot::json();
   }
};

struct buffered_snapshot_suite {
   using writer_t = ostream_snapshot_writer;
   using reader_t = istream_snapshot_reader;
   using write_storage_t = std::ostringstream;
   using snapshot_t = std::string;
   using read_storage_t = std::istringstream;

   struct writer : public writer_t {
      writer( const std::shared_ptr<write_storage_t>& storage )
      :writer_t(*storage)
      ,storage(storage)
      {

      }

      std::shared_ptr<write_storage_t> storage;
   };

   struct reader : public reader_t {
      explicit reader(const std::shared_ptr<read_storage_t>& storage)
      :reader_t(*storage)
      ,storage(storage)
      {}

      std::shared_ptr<read_storage_t> storage;
   };

   static auto get_writer() {
      return std::make_shared<writer>(std::make_shared<write_storage_t>());
   }

   static auto finalize(const std::shared_ptr<writer>& w) {
      w->finalize();
      return w->storage->str();
   }

   static auto get_reader( const snapshot_t& buffer) {
      return std::make_shared<reader>(std::make_shared<read_storage_t>(buffer));
   }

   template<typename Snapshot>
   static snapshot_t load_from_file() {
      return Snapshot::bin();
   }
};

BOOST_AUTO_TEST_SUITE(snapshot_tests)

using snapshot_suites = boost::mpl::list<variant_snapshot_suite, buffered_snapshot_suite>;

BOOST_AUTO_TEST_CASE_TEMPLATE(test_exhaustive_snapshot, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain;

   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();

   static const int generation_count = 8;
   std::list<snapshotted_tester> sub_testers;

   for (int generation = 0; generation < generation_count; generation++) {
      // create a new snapshot child
      auto writer = SNAPSHOT_SUITE::get_writer();
      chain.control->write_snapshot(writer);
      auto snapshot = SNAPSHOT_SUITE::finalize(writer);

      // create a new child at this snapshot
      sub_testers.emplace_back(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), generation);

      // increment the test contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce block
      auto new_block = chain.produce_block();

      // undo the auto-pending from tester
      chain.control->abort_block();

      auto integrity_value = chain.control->calculate_integrity_hash();

      // push that block to all sub testers and validate the integrity of the database after it.
      for (auto& other: sub_testers) {
         other.push_block(new_block);
         BOOST_REQUIRE_EQUAL(integrity_value.str(), other.control->calculate_integrity_hash().str());
      }
   }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_replay_over_snapshot, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain;

   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();

   static const int pre_snapshot_block_count = 12;
   static const int post_snapshot_block_count = 12;

   for (int itr = 0; itr < pre_snapshot_block_count; itr++) {
      // increment the contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce block
      chain.produce_block();
   }

   chain.control->abort_block();
   auto expected_pre_integrity_hash = chain.control->calculate_integrity_hash();

   // create a new snapshot child
   auto writer = SNAPSHOT_SUITE::get_writer();
   chain.control->write_snapshot(writer);
   auto snapshot = SNAPSHOT_SUITE::finalize(writer);

   // create a new child at this snapshot
   snapshotted_tester snap_chain(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), 1);
   BOOST_REQUIRE_EQUAL(expected_pre_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());

   // push more blocks to build up a block log
   for (int itr = 0; itr < post_snapshot_block_count; itr++) {
      // increment the contract
      chain.push_action(N(snapshot), N(increment), N(snapshot), mutable_variant_object()
         ( "value", 1 )
      );

      // produce & push block
      snap_chain.push_block(chain.produce_block());
   }

   // verify the hash at the end
   chain.control->abort_block();
   auto expected_post_integrity_hash = chain.control->calculate_integrity_hash();
   BOOST_REQUIRE_EQUAL(expected_post_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());

   // replay the block log from the snapshot child, from the snapshot
   snapshotted_tester replay_chain(chain.get_config(), SNAPSHOT_SUITE::get_reader(snapshot), 2, 1);
   BOOST_REQUIRE_EQUAL(expected_post_integrity_hash.str(), snap_chain.control->calculate_integrity_hash().str());
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_compatible_versions, SNAPSHOT_SUITE, snapshot_suites)
{
   tester chain(setup_policy::preactivate_feature_and_new_bios);

   ///< Begin deterministic code to generate blockchain for comparison
   // TODO: create a utility that will write new bin/json gzipped files based on this
   chain.create_account(N(snapshot));
   chain.produce_blocks(1);
   chain.set_code(N(snapshot), contracts::snapshot_test_wasm());
   chain.set_abi(N(snapshot), contracts::snapshot_test_abi().data());
   chain.produce_blocks(1);
   chain.control->abort_block();
   ///< End deterministic code to generate blockchain for comparison
   auto base_integrity_value = chain.control->calculate_integrity_hash();

   {
      static_assert(chain_snapshot_header::minimum_compatible_version <= 2, "version 2 unit test is no longer needed.  Please clean up data files");
      auto v2 = SNAPSHOT_SUITE::template load_from_file<snapshots::snap_v2>();
      snapshotted_tester v2_tester(chain.get_config(), SNAPSHOT_SUITE::get_reader(v2), 0);
      auto v2_integrity_value = v2_tester.control->calculate_integrity_hash();

      BOOST_CHECK_EQUAL(v2_integrity_value.str(), base_integrity_value.str());

      // create a latest snapshot
      auto latest_writer = SNAPSHOT_SUITE::get_writer();
      v2_tester.control->write_snapshot(latest_writer);
      auto latest = SNAPSHOT_SUITE::finalize(latest_writer);

      // load the latest snapshot
      snapshotted_tester latest_tester(chain.get_config(), SNAPSHOT_SUITE::get_reader(latest), 1);
      auto latest_integrity_value = latest_tester.control->calculate_integrity_hash();

      BOOST_REQUIRE_EQUAL(v2_integrity_value.str(), latest_integrity_value.str());
   }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before><commit_msg>Marking one more as flaky test. This failed 3 times out of 20 invocations.<commit_after><|endoftext|>


<commit_before>#pragma once
/**
	@file
	@brief succinct vector
	@author MITSUNARI Shigeo(@herumi)
	@license modified new BSD license
	http://opensource.org/licenses/BSD-3-Clause
*/
#include <assert.h>
#include <vector>
#include <cybozu/exception.hpp>
#include <cybozu/bit_operation.hpp>
#include <cybozu/select8.hpp>

#ifdef _MSC_VER
	#pragma warning(push)
	#pragma warning(disable : 4127)
#endif

namespace cybozu {

const uint64_t NotFound = ~uint64_t(0);

namespace sucvector_util {

inline uint32_t rank64(uint64_t v, size_t i)
{
    return cybozu::popcnt<uint64_t>(v & cybozu::makeBitMask64(i));
}

inline uint64_t select64(uint64_t v, size_t r)
{
	assert(r <= 64);
	if (r > popcnt(v)) return 64;
	uint32_t pos = 0;
	uint32_t c = popcnt(uint32_t(v));
	if (r > c) {
		r -= c;
		pos = 32;
		v >>= 32;
	}
	c = popcnt<uint32_t>(uint16_t(v));
	if (r > c) {
		r -= c;
		pos += 16;
		v >>= 16;
	}
	c = popcnt<uint32_t>(uint8_t(v));
	if (r > c) {
		r -= c;
		pos += 8;
		v >>= 8;
	}
	if (r == 8 && uint8_t(v) == 0xff) return pos + 7;
	assert(r <= 8);
	c = cybozu::select8_util::select8(uint8_t(v), r);
	return pos + c;
}

} // cybozu::sucvector_util

/*
	extra memory
	(32 + 8 * 4) / 256 = 1/4 bit per bit
*/
struct SucVector {
	static const uint64_t maxBitSize = uint64_t(1) << 40;
	struct B {
		uint64_t org[4];
		union {
			uint64_t a64;
			struct {
				uint32_t a;
				uint8_t b[4]; // b[0] is used for (b[0] << 32) | a
			} ab;
		};
	};
	std::vector<B> blk_;
	uint64_t bitSize_;
	uint64_t num_[2];

	template<int b>
	uint64_t rank_a(uint64_t i) const
	{
		assert(i < blk_.size());
		uint64_t ret = blk_[i].a64 % maxBitSize;
		if (!b) ret = i * 256 - ret;
		return ret;
	}
	template<bool b>
	size_t get_b(size_t L, size_t i) const
	{
		assert(i > 0);
		size_t r = blk_[L].ab.b[i];
		if (!b) r = 64 * i - r;
		return r;
	}
public:
	SucVector() : bitSize_(0) { num_[0] = num_[1] = 0; }
	/*
		@param blk [in] bit pattern block
		@param bitSize [in] bitSize ; blk size = (bitSize + 63) / 64
		@note max bitSize is 1<<40
	*/
	SucVector(const uint64_t *blk, size_t bitSize)
	{
		if (bitSize > maxBitSize) throw cybozu::Exception("SucVector:too large") << bitSize;
		init(blk, bitSize);
	}
	void init(const uint64_t *blk, size_t bitSize)
	{
		bitSize_ = bitSize;
		const size_t blkNum = (bitSize + 63) / 64;
		size_t tblNum = (blkNum + 3) / 4;
		blk_.resize(tblNum + 1);

		uint64_t av = 0;
		size_t pos = 0;
		for (size_t i = 0; i < tblNum; i++) {
			B& b = blk_[i];
			b.a64 = av % maxBitSize;
			uint32_t bv = 0;
			for (size_t j = 0; j < 4; j++) {
				uint64_t v = pos < blkNum ? blk[pos++] : 0;
				b.org[j] = v;
				uint32_t c = cybozu::popcnt(v);
				av += c;
				if (j > 0) {
					b.ab.b[j] = (uint8_t)bv;
				}
				bv += c;
			}
		}
		blk_[tblNum].a64 = av;
		num_[0] = blkNum * 64 - av;
		num_[1] = av;
	}
	uint64_t getNum(bool b) const { return num_[b ? 1 : 0]; }
	uint64_t rank1(size_t i) const
	{
		size_t q = i / 256;
		size_t r = (i / 64) & 3;
		assert(q < blk_.size());
		const B& b = blk_[q];
		uint64_t ret = b.a64 % maxBitSize;
		if (r > 0) {
			ret += b.ab.b[r];
		}
		ret += cybozu::popcnt<uint64_t>(b.org[r] & cybozu::makeBitMask64(i & 63));
		return ret;
	}
	size_t size() const { return bitSize_; }
	uint64_t rank0(size_t i) const
	{
		return i - rank1(i);
	}
	uint64_t rank(bool b, size_t i) const
	{
		if (b) return rank1(i);
		return rank0(i);
	}
	bool get(size_t i) const
	{
		size_t q = i / 256;
		size_t r = (i / 64) & 3;
		assert(q < blk_.size());
		const B& b = blk_[q];
		return (b.org[r] & (1ULL << (i & 63))) != 0;
	}
	size_t select0(uint64_t rank) const { return selectSub<false>(rank); }
	size_t select1(uint64_t rank) const { return selectSub<true>(rank); }
	size_t select(bool b, uint64_t rank) const
	{
		if (b) return select1(rank);
		return select0(rank);
	}

	/*
		0123456789
		0100101101
		 ^  ^ ^^
		 0  1 23
		select(v, r) = min { i - 1 | rank(v, i) = r + 1 }
		select(3) = 7
	*/
	template<bool b>
	size_t selectSub(uint64_t rank) const
	{
		rank++;
		if (rank > num_[b]) return NotFound;
		size_t L = 0;
		size_t R = blk_.size();
		while (L < R) {
			size_t M = (L + R) / 2; // (R - L) / 2 + L;
			if (rank_a<b>(M) < rank) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		if (L > 0) L--;
		rank -= rank_a<b>(L);
		size_t i = 0;
		while (i < 3) {
			size_t r = get_b<b>(L, i + 1);
			if (r >= rank) {
				break;
			}
			i++;
		}
		if (i > 0) {
			size_t r = get_b<b>(L, i);
			rank -= r;
		}
		uint64_t v = blk_[L].org[i];
		if (!b) v = ~v;
		uint64_t ret = cybozu::sucvector_util::select64(v, rank);
		ret += L * 256 + i * 64;
		return ret;
   	}
};

} // cybozu

#ifdef _WIN32
	#pragma warning(pop)
#endif
<commit_msg>avoid load memory<commit_after>#pragma once
/**
	@file
	@brief succinct vector
	@author MITSUNARI Shigeo(@herumi)
	@license modified new BSD license
	http://opensource.org/licenses/BSD-3-Clause
*/
#include <assert.h>
#include <vector>
#include <cybozu/exception.hpp>
#include <cybozu/bit_operation.hpp>
#include <cybozu/select8.hpp>

#ifdef _MSC_VER
	#pragma warning(push)
	#pragma warning(disable : 4127)
#endif

namespace cybozu {

const uint64_t NotFound = ~uint64_t(0);

namespace sucvector_util {

inline uint32_t rank64(uint64_t v, size_t i)
{
    return cybozu::popcnt<uint64_t>(v & cybozu::makeBitMask64(i));
}

inline uint64_t select64(uint64_t v, size_t r)
{
	assert(r <= 64);
	if (r > popcnt(v)) return 64;
	uint32_t pos = 0;
	uint32_t c = popcnt(uint32_t(v));
	if (r > c) {
		r -= c;
		pos = 32;
		v >>= 32;
	}
	c = popcnt<uint32_t>(uint16_t(v));
	if (r > c) {
		r -= c;
		pos += 16;
		v >>= 16;
	}
	c = popcnt<uint32_t>(uint8_t(v));
	if (r > c) {
		r -= c;
		pos += 8;
		v >>= 8;
	}
	if (r == 8 && uint8_t(v) == 0xff) return pos + 7;
	assert(r <= 8);
	c = cybozu::select8_util::select8(uint8_t(v), r);
	return pos + c;
}

} // cybozu::sucvector_util

/*
	extra memory
	(32 + 8 * 4) / 256 = 1/4 bit per bit
*/
struct SucVector {
	static const uint64_t maxBitSize = uint64_t(1) << 40;
	struct B {
		uint64_t org[4];
		union {
			uint64_t a64;
			struct {
				uint32_t a;
				uint8_t b[4]; // b[0] is used for (b[0] << 32) | a
			} ab;
		};
	};
	std::vector<B> blk_;
	uint64_t bitSize_;
	uint64_t num_[2];

	template<int b>
	uint64_t rank_a(uint64_t i) const
	{
		assert(i < blk_.size());
		uint64_t ret = blk_[i].a64 % maxBitSize;
		if (!b) ret = i * 256 - ret;
		return ret;
	}
	template<bool b>
	size_t get_b(size_t L, size_t i) const
	{
		assert(i > 0);
		size_t r = blk_[L].ab.b[i];
		if (!b) r = 64 * i - r;
		return r;
	}
public:
	SucVector() : bitSize_(0) { num_[0] = num_[1] = 0; }
	/*
		@param blk [in] bit pattern block
		@param bitSize [in] bitSize ; blk size = (bitSize + 63) / 64
		@note max bitSize is 1<<40
	*/
	SucVector(const uint64_t *blk, size_t bitSize)
	{
		if (bitSize > maxBitSize) throw cybozu::Exception("SucVector:too large") << bitSize;
		init(blk, bitSize);
	}
	void init(const uint64_t *blk, size_t bitSize)
	{
		bitSize_ = bitSize;
		const size_t blkNum = (bitSize + 63) / 64;
		size_t tblNum = (blkNum + 3) / 4;
		blk_.resize(tblNum + 1);

		uint64_t av = 0;
		size_t pos = 0;
		for (size_t i = 0; i < tblNum; i++) {
			B& b = blk_[i];
			b.a64 = av % maxBitSize;
			uint32_t bv = 0;
			for (size_t j = 0; j < 4; j++) {
				uint64_t v = pos < blkNum ? blk[pos++] : 0;
				b.org[j] = v;
				uint32_t c = cybozu::popcnt(v);
				av += c;
				if (j > 0) {
					b.ab.b[j] = (uint8_t)bv;
				}
				bv += c;
			}
		}
		blk_[tblNum].a64 = av;
		num_[0] = blkNum * 64 - av;
		num_[1] = av;
	}
	uint64_t getNum(bool b) const { return num_[b ? 1 : 0]; }
	uint64_t rank1(size_t i) const
	{
		size_t q = i / 256;
		size_t r = (i / 64) & 3;
		assert(q < blk_.size());
		const B& b = blk_[q];
		uint64_t ret = b.a64 % maxBitSize;
		if (r > 0) {
//			ret += b.ab.b[r];
			ret += uint8_t(b.a64 >> (32 + r * 8));
		}
		ret += cybozu::popcnt<uint64_t>(b.org[r] & cybozu::makeBitMask64(i & 63));
		return ret;
	}
	size_t size() const { return bitSize_; }
	uint64_t rank0(size_t i) const
	{
		return i - rank1(i);
	}
	uint64_t rank(bool b, size_t i) const
	{
		if (b) return rank1(i);
		return rank0(i);
	}
	bool get(size_t i) const
	{
		size_t q = i / 256;
		size_t r = (i / 64) & 3;
		assert(q < blk_.size());
		const B& b = blk_[q];
		return (b.org[r] & (1ULL << (i & 63))) != 0;
	}
	size_t select0(uint64_t rank) const { return selectSub<false>(rank); }
	size_t select1(uint64_t rank) const { return selectSub<true>(rank); }
	size_t select(bool b, uint64_t rank) const
	{
		if (b) return select1(rank);
		return select0(rank);
	}

	/*
		0123456789
		0100101101
		 ^  ^ ^^
		 0  1 23
		select(v, r) = min { i - 1 | rank(v, i) = r + 1 }
		select(3) = 7
	*/
	template<bool b>
	size_t selectSub(uint64_t rank) const
	{
		rank++;
		if (rank > num_[b]) return NotFound;
		size_t L = 0;
		size_t R = blk_.size();
		while (L < R) {
			size_t M = (L + R) / 2; // (R - L) / 2 + L;
			if (rank_a<b>(M) < rank) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		if (L > 0) L--;
		rank -= rank_a<b>(L);
		size_t i = 0;
		while (i < 3) {
			size_t r = get_b<b>(L, i + 1);
			if (r >= rank) {
				break;
			}
			i++;
		}
		if (i > 0) {
			size_t r = get_b<b>(L, i);
			rank -= r;
		}
		uint64_t v = blk_[L].org[i];
		if (!b) v = ~v;
		uint64_t ret = cybozu::sucvector_util::select64(v, rank);
		ret += L * 256 + i * 64;
		return ret;
   	}
};

} // cybozu

#ifdef _WIN32
	#pragma warning(pop)
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#pragma once

#include "bytes.hh"
#include "timestamp.hh"
#include "gc_clock.hh"
#include <cstdint>

template<typename T>
static inline
void set_field(bytes& v, unsigned offset, T val) {
    reinterpret_cast<net::packed<T>*>(v.begin() + offset)->raw = net::hton(val);
}

template<typename T>
static inline
T get_field(const bytes_view& v, unsigned offset) {
    return net::ntoh(*reinterpret_cast<const net::packed<T>*>(v.begin() + offset));
}

class atomic_cell_or_collection;

/*
 * Represents atomic cell layout. Works on serialized form.
 *
 * Layout:
 *
 *  <live>  := <int8_t:flags><int64_t:timestamp><int32_t:ttl>?<value>
 *  <dead>  := <int8_t:    0><int64_t:timestamp><int32_t:ttl>
 */
class atomic_cell_type final {
private:
    static constexpr int8_t DEAD_FLAGS = 0;
    static constexpr int8_t LIVE_FLAG = 0x01;
    static constexpr int8_t TTL_FLAG  = 0x02; // When present, TTL field is present. Set only for live cells
    static constexpr unsigned flags_size = 1;
    static constexpr unsigned timestamp_offset = flags_size;
    static constexpr unsigned timestamp_size = 8;
    static constexpr unsigned ttl_offset = timestamp_offset + timestamp_size;
    static constexpr unsigned ttl_size = 4;
private:
    static bool is_live(const bytes_view& cell) {
        return cell[0] != DEAD_FLAGS;
    }
    static bool is_live_and_has_ttl(const bytes_view& cell) {
        return cell[0] & TTL_FLAG;
    }
    static bool is_dead(const bytes_view& cell) {
        return cell[0] == DEAD_FLAGS;
    }
    // Can be called on live and dead cells
    static api::timestamp_type timestamp(const bytes_view& cell) {
        return get_field<api::timestamp_type>(cell, timestamp_offset);
    }
    // Can be called on live cells only
    static bytes_view value(bytes_view cell) {
        auto ttl_field_size = bool(cell[0] & TTL_FLAG) * ttl_size;
        auto value_offset = flags_size + timestamp_size + ttl_field_size;
        cell.remove_prefix(value_offset);
        return cell;
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    static ttl_opt ttl(const bytes_view& cell) {
        auto flags = cell[0];
        if (flags == DEAD_FLAGS || (flags & TTL_FLAG)) {
            auto ttl = get_field<int32_t>(cell, ttl_offset);
            return {gc_clock::time_point(gc_clock::duration(ttl))};
        }
        return {};
    }
    static bytes make_dead(api::timestamp_type timestamp, gc_clock::time_point ttl) {
        bytes b(bytes::initialized_later(), flags_size + timestamp_size + ttl_size);
        b[0] = DEAD_FLAGS;
        set_field(b, timestamp_offset, timestamp);
        set_field(b, ttl_offset, ttl.time_since_epoch().count());
        return b;
    }
    static bytes make_live(api::timestamp_type timestamp, ttl_opt ttl, bytes_view value) {
        auto value_offset = flags_size + timestamp_size + bool(ttl) * ttl_size;
        bytes b(bytes::initialized_later(), value_offset + value.size());
        b[0] = (ttl ? TTL_FLAG : 0) | LIVE_FLAG;
        set_field(b, timestamp_offset, timestamp);
        if (ttl) {
            set_field(b, ttl_offset, ttl->time_since_epoch().count());
        }
        std::copy_n(value.begin(), value.size(), b.begin() + value_offset);
        return b;
    }
    friend class atomic_cell_view;
    friend class atomic_cell;
};

class atomic_cell_view final {
    bytes_view _data;
private:
    atomic_cell_view(bytes_view data) : _data(data) {}
public:
    static atomic_cell_view from_bytes(bytes_view data) { return atomic_cell_view(data); }
    bool is_live() const {
        return atomic_cell_type::is_live(_data);
    }
    bool is_live_and_has_ttl() const {
        return atomic_cell_type::is_live_and_has_ttl(_data);
    }
    bool is_dead() const {
        return atomic_cell_type::is_dead(_data);
    }
    // Can be called on live and dead cells
    api::timestamp_type timestamp() const {
        return atomic_cell_type::timestamp(_data);
    }
    // Can be called on live cells only
    bytes_view value() const {
        return atomic_cell_type::value(_data);
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    ttl_opt ttl() const {
        return atomic_cell_type::ttl(_data);
    }
    bytes_view serialize() const {
        return _data;
    }
    friend class atomic_cell;
};

class atomic_cell final {
    bytes _data;
private:
    atomic_cell(bytes b) : _data(std::move(b)) {}
public:
    atomic_cell(const atomic_cell&) = default;
    atomic_cell(atomic_cell&&) = default;
    atomic_cell& operator=(const atomic_cell&) = default;
    atomic_cell& operator=(atomic_cell&&) = default;
    static atomic_cell from_bytes(bytes b) {
        return atomic_cell(std::move(b));
    }
    atomic_cell(atomic_cell_view other) : _data(other._data.begin(), other._data.end()) {}
    bool is_live() const {
        return atomic_cell_type::is_live(_data);
    }
    bool is_live_and_has_ttl() const {
        return atomic_cell_type::is_live_and_has_ttl(_data);
    }
    bool is_dead(const bytes_view& cell) const {
        return atomic_cell_type::is_dead(_data);
    }
    // Can be called on live and dead cells
    api::timestamp_type timestamp() const {
        return atomic_cell_type::timestamp(_data);
    }
    // Can be called on live cells only
    bytes_view value() const {
        return atomic_cell_type::value(_data);
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    ttl_opt ttl() const {
        return atomic_cell_type::ttl(_data);
    }
    bytes_view serialize() const {
        return _data;
    }
    operator atomic_cell_view() const {
        return atomic_cell_view(_data);
    }
    static atomic_cell make_dead(api::timestamp_type timestamp, gc_clock::time_point ttl) {
        return atomic_cell_type::make_dead(timestamp, ttl);
    }
    static atomic_cell make_live(api::timestamp_type timestamp, ttl_opt ttl, bytes_view value) {
        return atomic_cell_type::make_live(timestamp, ttl, value);
    }
    friend class atomic_cell_or_collection;
};

// Represents a mutation of a collection.  Actual format is determined by collection type,
// and is:
//   set:  list of atomic_cell
//   map:  list of pair<atomic_cell, bytes> (for key/value)
//   list: tbd, probably ugly
class collection_mutation {
public:
    struct view {
        bytes_view data;
    };
    struct one {
        bytes data;
        operator view() const { return { data }; }
    };
};

// A variant type that can hold either an atomic_cell, or a serialized collection.
// Which type is stored is determinied by the schema.
class atomic_cell_or_collection final {
    bytes _data;
private:
    atomic_cell_or_collection(bytes&& data) : _data(std::move(data)) {}
public:
    atomic_cell_or_collection(atomic_cell ac) : _data(std::move(ac._data)) {}
    static atomic_cell_or_collection from_atomic_cell(atomic_cell data) { return { std::move(data._data) }; }
    atomic_cell_view as_atomic_cell() const { return atomic_cell_view::from_bytes(_data); }
    atomic_cell_or_collection(collection_mutation::one cm) : _data(std::move(cm.data)) {}
    static atomic_cell_or_collection from_collection_mutation(collection_mutation::one data) {
        return std::move(data.data);
    }
    collection_mutation::view as_collection_mutation() const {
        return collection_mutation::view{_data};
    }
};

class column_definition;

int compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right);
void merge_column(const column_definition& def,
        atomic_cell_or_collection& old,
        const atomic_cell_or_collection& neww);
<commit_msg>db: drop unused atomic_cell::is_dead() parameter<commit_after>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#pragma once

#include "bytes.hh"
#include "timestamp.hh"
#include "gc_clock.hh"
#include <cstdint>

template<typename T>
static inline
void set_field(bytes& v, unsigned offset, T val) {
    reinterpret_cast<net::packed<T>*>(v.begin() + offset)->raw = net::hton(val);
}

template<typename T>
static inline
T get_field(const bytes_view& v, unsigned offset) {
    return net::ntoh(*reinterpret_cast<const net::packed<T>*>(v.begin() + offset));
}

class atomic_cell_or_collection;

/*
 * Represents atomic cell layout. Works on serialized form.
 *
 * Layout:
 *
 *  <live>  := <int8_t:flags><int64_t:timestamp><int32_t:ttl>?<value>
 *  <dead>  := <int8_t:    0><int64_t:timestamp><int32_t:ttl>
 */
class atomic_cell_type final {
private:
    static constexpr int8_t DEAD_FLAGS = 0;
    static constexpr int8_t LIVE_FLAG = 0x01;
    static constexpr int8_t TTL_FLAG  = 0x02; // When present, TTL field is present. Set only for live cells
    static constexpr unsigned flags_size = 1;
    static constexpr unsigned timestamp_offset = flags_size;
    static constexpr unsigned timestamp_size = 8;
    static constexpr unsigned ttl_offset = timestamp_offset + timestamp_size;
    static constexpr unsigned ttl_size = 4;
private:
    static bool is_live(const bytes_view& cell) {
        return cell[0] != DEAD_FLAGS;
    }
    static bool is_live_and_has_ttl(const bytes_view& cell) {
        return cell[0] & TTL_FLAG;
    }
    static bool is_dead(const bytes_view& cell) {
        return cell[0] == DEAD_FLAGS;
    }
    // Can be called on live and dead cells
    static api::timestamp_type timestamp(const bytes_view& cell) {
        return get_field<api::timestamp_type>(cell, timestamp_offset);
    }
    // Can be called on live cells only
    static bytes_view value(bytes_view cell) {
        auto ttl_field_size = bool(cell[0] & TTL_FLAG) * ttl_size;
        auto value_offset = flags_size + timestamp_size + ttl_field_size;
        cell.remove_prefix(value_offset);
        return cell;
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    static ttl_opt ttl(const bytes_view& cell) {
        auto flags = cell[0];
        if (flags == DEAD_FLAGS || (flags & TTL_FLAG)) {
            auto ttl = get_field<int32_t>(cell, ttl_offset);
            return {gc_clock::time_point(gc_clock::duration(ttl))};
        }
        return {};
    }
    static bytes make_dead(api::timestamp_type timestamp, gc_clock::time_point ttl) {
        bytes b(bytes::initialized_later(), flags_size + timestamp_size + ttl_size);
        b[0] = DEAD_FLAGS;
        set_field(b, timestamp_offset, timestamp);
        set_field(b, ttl_offset, ttl.time_since_epoch().count());
        return b;
    }
    static bytes make_live(api::timestamp_type timestamp, ttl_opt ttl, bytes_view value) {
        auto value_offset = flags_size + timestamp_size + bool(ttl) * ttl_size;
        bytes b(bytes::initialized_later(), value_offset + value.size());
        b[0] = (ttl ? TTL_FLAG : 0) | LIVE_FLAG;
        set_field(b, timestamp_offset, timestamp);
        if (ttl) {
            set_field(b, ttl_offset, ttl->time_since_epoch().count());
        }
        std::copy_n(value.begin(), value.size(), b.begin() + value_offset);
        return b;
    }
    friend class atomic_cell_view;
    friend class atomic_cell;
};

class atomic_cell_view final {
    bytes_view _data;
private:
    atomic_cell_view(bytes_view data) : _data(data) {}
public:
    static atomic_cell_view from_bytes(bytes_view data) { return atomic_cell_view(data); }
    bool is_live() const {
        return atomic_cell_type::is_live(_data);
    }
    bool is_live_and_has_ttl() const {
        return atomic_cell_type::is_live_and_has_ttl(_data);
    }
    bool is_dead() const {
        return atomic_cell_type::is_dead(_data);
    }
    // Can be called on live and dead cells
    api::timestamp_type timestamp() const {
        return atomic_cell_type::timestamp(_data);
    }
    // Can be called on live cells only
    bytes_view value() const {
        return atomic_cell_type::value(_data);
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    ttl_opt ttl() const {
        return atomic_cell_type::ttl(_data);
    }
    bytes_view serialize() const {
        return _data;
    }
    friend class atomic_cell;
};

class atomic_cell final {
    bytes _data;
private:
    atomic_cell(bytes b) : _data(std::move(b)) {}
public:
    atomic_cell(const atomic_cell&) = default;
    atomic_cell(atomic_cell&&) = default;
    atomic_cell& operator=(const atomic_cell&) = default;
    atomic_cell& operator=(atomic_cell&&) = default;
    static atomic_cell from_bytes(bytes b) {
        return atomic_cell(std::move(b));
    }
    atomic_cell(atomic_cell_view other) : _data(other._data.begin(), other._data.end()) {}
    bool is_live() const {
        return atomic_cell_type::is_live(_data);
    }
    bool is_live_and_has_ttl() const {
        return atomic_cell_type::is_live_and_has_ttl(_data);
    }
    bool is_dead() const {
        return atomic_cell_type::is_dead(_data);
    }
    // Can be called on live and dead cells
    api::timestamp_type timestamp() const {
        return atomic_cell_type::timestamp(_data);
    }
    // Can be called on live cells only
    bytes_view value() const {
        return atomic_cell_type::value(_data);
    }
    // Can be called on live and dead cells. For dead cells, the result is never empty.
    ttl_opt ttl() const {
        return atomic_cell_type::ttl(_data);
    }
    bytes_view serialize() const {
        return _data;
    }
    operator atomic_cell_view() const {
        return atomic_cell_view(_data);
    }
    static atomic_cell make_dead(api::timestamp_type timestamp, gc_clock::time_point ttl) {
        return atomic_cell_type::make_dead(timestamp, ttl);
    }
    static atomic_cell make_live(api::timestamp_type timestamp, ttl_opt ttl, bytes_view value) {
        return atomic_cell_type::make_live(timestamp, ttl, value);
    }
    friend class atomic_cell_or_collection;
};

// Represents a mutation of a collection.  Actual format is determined by collection type,
// and is:
//   set:  list of atomic_cell
//   map:  list of pair<atomic_cell, bytes> (for key/value)
//   list: tbd, probably ugly
class collection_mutation {
public:
    struct view {
        bytes_view data;
    };
    struct one {
        bytes data;
        operator view() const { return { data }; }
    };
};

// A variant type that can hold either an atomic_cell, or a serialized collection.
// Which type is stored is determinied by the schema.
class atomic_cell_or_collection final {
    bytes _data;
private:
    atomic_cell_or_collection(bytes&& data) : _data(std::move(data)) {}
public:
    atomic_cell_or_collection(atomic_cell ac) : _data(std::move(ac._data)) {}
    static atomic_cell_or_collection from_atomic_cell(atomic_cell data) { return { std::move(data._data) }; }
    atomic_cell_view as_atomic_cell() const { return atomic_cell_view::from_bytes(_data); }
    atomic_cell_or_collection(collection_mutation::one cm) : _data(std::move(cm.data)) {}
    static atomic_cell_or_collection from_collection_mutation(collection_mutation::one data) {
        return std::move(data.data);
    }
    collection_mutation::view as_collection_mutation() const {
        return collection_mutation::view{_data};
    }
};

class column_definition;

int compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right);
void merge_column(const column_definition& def,
        atomic_cell_or_collection& old,
        const atomic_cell_or_collection& neww);
<|endoftext|>


<commit_before>#ifndef KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP

#include "container.hpp"
#include "detail/lazy_base.hpp"

namespace kgr {
namespace detail {

/*
 * Base class for invokers. Implements the call operator that invoke the function.
 */
template<typename Base, typename Map>
struct invoker_base : protected Base {
	using Base::Base;

	template<typename F, typename... Args, enable_if_t<is_invoke_valid<Map, decay_t<F>, Args...>::value, int> = 0>
	invoke_function_result_t<Map, decay_t<F>, Args...> operator()(F&& f, Args&&... args) {
		kgr::container& c = this->container();
		return c.invoke<Map>(std::forward<F>(f), std::forward<Args>(args)...);
	}

	sink operator()(not_invokable_error = {}, ...) = delete;
};

/*
 * Base class for generators. Implements the call operator that create services.
 */
template<typename Base, typename T>
struct generator_base : protected Base {
	static_assert(!is_single<T>::value, "Generator only work with non-single services.");
	using Base::Base;

	template<typename... Args, enable_if_t<is_service_valid<T, Args...>::value, int> = 0>
	service_type<T> operator()(Args&&... args) {
		kgr::container& c = Base::container();
		return c.service<T>(std::forward<Args>(args)...);
	}

	template<typename U = T, enable_if_t<std::is_default_constructible<service_error<U>>::value, int> = 0>
	sink operator()(service_error<U> = {}) = delete;

	template<typename... Args>
	sink operator()(service_error<T, identity_t<Args>...>, Args&&...) = delete;
};

/*
 * Base class for any non forking operators.
 *
 * Hold a non-owning reference to the container as a pointer.
 */
struct operator_base {
	explicit operator_base(kgr::container& c) noexcept : _container{&c} {}

	inline auto container() noexcept -> kgr::container& {
		return *_container;
	}

	inline auto container() const noexcept -> kgr::container const& {
		return *_container;
	}

	kgr::container* _container;
};

/*
 * Base class for any forking operators.
 *
 * Hold a fork of the container as a member value.
 */
struct forked_operator_base {
	explicit forked_operator_base(kgr::container&& c) noexcept : _container{std::move(c)} {}

	inline auto container() noexcept -> kgr::container& {
		return _container;
	}

	inline auto container() const noexcept -> kgr::container const& {
		return _container;
	}

	kgr::container _container;
};

} // namespace detail

/**
 * Function object that calls container::invoke.
 *
 * Useful when dealing with higer order functions or to convey intent.
 */
template<typename Map>
struct mapped_invoker : detail::invoker_base<detail::operator_base, Map> {
	using detail::invoker_base<detail::operator_base, Map>::invoker_base;

	template<typename M>
	mapped_invoker(const mapped_invoker<M>& other) :
		detail::invoker_base<detail::operator_base, Map>{other.container()} {}
};

/**
 * Function object that calls container::invoke.
 *
 * This version forks the container.
 *
 * Useful when dealing with higer order functions or to convey intent.
 */
template<typename Map>
struct forked_mapped_invoker : detail::invoker_base<detail::forked_operator_base, Map> {
	using detail::invoker_base<detail::forked_operator_base, Map>::invoker_base;

	template<typename M>
	forked_mapped_invoker(forked_mapped_invoker<M>&& other) :
		detail::invoker_base<detail::forked_operator_base, Map>{std::move(other.container())} {}
};

/**
 * Function object that calls creates a service.
 * Basically a factory function for a particular service.
 *
 * Useful to convey intent or contraining usage of the container.
 */
template<typename T>
using generator = detail::generator_base<detail::operator_base, T>;

/**
 * Function object that calls creates a service.
 * Basically a factory function for a particular service.
 *
 * This version forks the container.
 *
 * Useful to convey intent or contraining usage of the container.
 */
template<typename T>
using forked_generator = detail::generator_base<detail::forked_operator_base, T>;

/**
 * A proxy class that delays the construction of a service until usage.
 */
template<typename T>
using lazy = detail::lazy_base<detail::operator_base, T>;

/**
 * A proxy class that delays the construction of a service until usage.
 *
 * This will fork the container when the proxy is created.
 */
template<typename T>
using forked_lazy = detail::lazy_base<detail::forked_operator_base, T>;

/**
 * Alias to the default invoker
 */
using invoker = mapped_invoker<map<>>;

/**
 * Alias to the default forked invoker
 */
using forked_invoker = forked_mapped_invoker<map<>>;

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
<commit_msg>Invoker now supports custom maps when no maps are specified<commit_after>#ifndef KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP

#include "container.hpp"
#include "detail/lazy_base.hpp"

namespace kgr {
namespace detail {

/*
 * Base class for invokers. Implements the call operator that invoke the function.
 */
template<typename Base, typename Map>
struct invoker_base : protected Base {
	using Base::Base;

	template<typename F, typename... Args, enable_if_t<is_invoke_valid<Map, decay_t<F>, Args...>::value, int> = 0>
	invoke_function_result_t<Map, decay_t<F>, Args...> operator()(F&& f, Args&&... args) {
		kgr::container& c = this->container();
		return c.invoke<Map>(std::forward<F>(f), std::forward<Args>(args)...);
	}

	sink operator()(not_invokable_error = {}, ...) = delete;
};

template<typename Base>
struct invoker_base<Base, map<>> : protected Base {
	using Base::Base;
	using map_t = map<>;

	template<typename F, typename... Args, enable_if_t<is_invoke_valid<map_t, decay_t<F>, Args...>::value, int> = 0>
	invoke_function_result_t<map_t, decay_t<F>, Args...> operator()(F&& f, Args&&... args) {
		kgr::container& c = this->container();
		return c.invoke<map_t>(std::forward<F>(f), std::forward<Args>(args)...);
	}

	template<typename Map, typename F, typename... Args, enable_if_t<is_map<Map>::value && is_invoke_valid<Map, decay_t<F>, Args...>::value, int> = 0>
	invoke_function_result_t<Map, decay_t<F>, Args...> operator()(Map map, F&& f, Args&&... args) {
		kgr::container& c = this->container();
		return c.invoke(map, std::forward<F>(f), std::forward<Args>(args)...);
	}

	sink operator()(not_invokable_error = {}, ...) = delete;
};

/*
 * Base class for generators. Implements the call operator that create services.
 */
template<typename Base, typename T>
struct generator_base : protected Base {
	static_assert(!is_single<T>::value, "Generator only work with non-single services.");
	using Base::Base;

	template<typename... Args, enable_if_t<is_service_valid<T, Args...>::value, int> = 0>
	service_type<T> operator()(Args&&... args) {
		kgr::container& c = Base::container();
		return c.service<T>(std::forward<Args>(args)...);
	}

	template<typename U = T, enable_if_t<std::is_default_constructible<service_error<U>>::value, int> = 0>
	sink operator()(service_error<U> = {}) = delete;

	template<typename... Args>
	sink operator()(service_error<T, identity_t<Args>...>, Args&&...) = delete;
};

/*
 * Base class for any non forking operators.
 *
 * Hold a non-owning reference to the container as a pointer.
 */
struct operator_base {
	explicit operator_base(kgr::container& c) noexcept : _container{&c} {}

	inline auto container() noexcept -> kgr::container& {
		return *_container;
	}

	inline auto container() const noexcept -> kgr::container const& {
		return *_container;
	}

	kgr::container* _container;
};

/*
 * Base class for any forking operators.
 *
 * Hold a fork of the container as a member value.
 */
struct forked_operator_base {
	explicit forked_operator_base(kgr::container&& c) noexcept : _container{std::move(c)} {}

	inline auto container() noexcept -> kgr::container& {
		return _container;
	}

	inline auto container() const noexcept -> kgr::container const& {
		return _container;
	}

	kgr::container _container;
};

} // namespace detail

/**
 * Function object that calls container::invoke.
 *
 * Useful when dealing with higer order functions or to convey intent.
 */
template<typename Map>
struct mapped_invoker : detail::invoker_base<detail::operator_base, Map> {
	using detail::invoker_base<detail::operator_base, Map>::invoker_base;

	template<typename M>
	mapped_invoker(const mapped_invoker<M>& other) :
		detail::invoker_base<detail::operator_base, Map>{other.container()} {}
};

/**
 * Function object that calls container::invoke.
 *
 * This version forks the container.
 *
 * Useful when dealing with higer order functions or to convey intent.
 */
template<typename Map>
struct forked_mapped_invoker : detail::invoker_base<detail::forked_operator_base, Map> {
	using detail::invoker_base<detail::forked_operator_base, Map>::invoker_base;

	template<typename M>
	forked_mapped_invoker(forked_mapped_invoker<M>&& other) :
		detail::invoker_base<detail::forked_operator_base, Map>{std::move(other.container())} {}
};

/**
 * Function object that calls creates a service.
 * Basically a factory function for a particular service.
 *
 * Useful to convey intent or contraining usage of the container.
 */
template<typename T>
using generator = detail::generator_base<detail::operator_base, T>;

/**
 * Function object that calls creates a service.
 * Basically a factory function for a particular service.
 *
 * This version forks the container.
 *
 * Useful to convey intent or contraining usage of the container.
 */
template<typename T>
using forked_generator = detail::generator_base<detail::forked_operator_base, T>;

/**
 * A proxy class that delays the construction of a service until usage.
 */
template<typename T>
using lazy = detail::lazy_base<detail::operator_base, T>;

/**
 * A proxy class that delays the construction of a service until usage.
 *
 * This will fork the container when the proxy is created.
 */
template<typename T>
using forked_lazy = detail::lazy_base<detail::forked_operator_base, T>;

/**
 * Alias to the default invoker
 */
using invoker = mapped_invoker<map<>>;

/**
 * Alias to the default forked invoker
 */
using forked_invoker = forked_mapped_invoker<map<>>;

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ENTRY_HPP_INCLUDED
#define TORRENT_ENTRY_HPP_INCLUDED

/*
 *
 * This file declares the entry class. It is a
 * variant-type that can be an integer, list,
 * dictionary (map) or a string. This type is
 * used to hold bdecoded data (which is the
 * encoding BitTorrent messages uses).
 *
 * it has 4 accessors to access the actual
 * type of the object. They are:
 * integer()
 * string()
 * list()
 * dict()
 * The actual type has to match the type you
 * are asking for, otherwise you will get an
 * assertion failure.
 * When you default construct an entry, it is
 * uninitialized. You can initialize it through the
 * assignment operator, copy-constructor or
 * the constructor that takes a data_type enum.
 *
 *
 */

#include <map>
#include <list>
#include <string>
#include <stdexcept>

#include "libtorrent/size_type.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/error_code.hpp"

#if TORRENT_USE_IOSTREAM
#include <iosfwd>
#endif

namespace libtorrent
{

	struct TORRENT_EXPORT type_error: std::runtime_error
	{
		type_error(const char* error): std::runtime_error(error) {}
	};

	namespace detail
	{
		template<int v1, int v2>
		struct max2 { enum { value = v1>v2?v1:v2 }; };

		template<int v1, int v2, int v3>
		struct max3
		{
			enum
			{
				temp = max2<v1,v2>::value,
				value = temp>v3?temp:v3
			};
		};

		template<int v1, int v2, int v3, int v4>
		struct max4
		{
			enum
			{
				temp = max3<v1,v2, v3>::value,
				value = temp>v4?temp:v4
			};
		};
	}

	class entry;

	class TORRENT_EXPORT entry
	{
	public:

		// the key is always a string. If a generic entry would be allowed
		// as a key, sorting would become a problem (e.g. to compare a string
		// to a list). The definition doesn't mention such a limit though.
		typedef std::map<std::string, entry> dictionary_type;
		typedef std::string string_type;
		typedef std::list<entry> list_type;
		typedef size_type integer_type;

		enum data_type
		{
			int_t,
			string_t,
			list_t,
			dictionary_t,
			undefined_t
		};

		data_type type() const;

		entry(dictionary_type const&);
		entry(string_type const&);
		entry(list_type const&);
		entry(integer_type const&);

		entry();
		entry(data_type t);
		entry(entry const& e);
		~entry();

		bool operator==(entry const& e) const;

		void operator=(entry const&);
		void operator=(dictionary_type const&);
		void operator=(string_type const&);
		void operator=(list_type const&);
		void operator=(integer_type const&);

		integer_type& integer();
		const integer_type& integer() const;
		string_type& string();
		const string_type& string() const;
		list_type& list();
		const list_type& list() const;
		dictionary_type& dict();
		const dictionary_type& dict() const;

		void swap(entry& e);

		// these functions requires that the entry
		// is a dictionary, otherwise they will throw
		entry& operator[](char const* key);
		entry& operator[](std::string const& key);
#ifndef BOOST_NO_EXCEPTIONS
		const entry& operator[](char const* key) const;
		const entry& operator[](std::string const& key) const;
#endif
		entry* find_key(char const* key);
		entry const* find_key(char const* key) const;
		entry* find_key(std::string const& key);
		entry const* find_key(std::string const& key) const;

#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
		void print(std::ostream& os, int indent = 0) const;
#endif

	protected:

		void construct(data_type t);
		void copy(const entry& e);
		void destruct();

	private:

		data_type m_type;

#if defined(_MSC_VER) && _MSC_VER < 1310
		// workaround for msvc-bug.
		// assumes sizeof(map<string, char>) == sizeof(map<string, entry>)
		// and sizeof(list<char>) == sizeof(list<entry>)
		union
		{
			char data[
				detail::max4<sizeof(std::list<char>)
				, sizeof(std::map<std::string, char>)
				, sizeof(string_type)
				, sizeof(integer_type)>::value];
			integer_type dummy_aligner;
		};
#else
		union
		{
			char data[detail::max4<sizeof(list_type)
				, sizeof(dictionary_type)
				, sizeof(string_type)
				, sizeof(integer_type)>::value];
			integer_type dummy_aligner;
		};
#endif

#ifdef TORRENT_DEBUG
	public:
		// in debug mode this is set to false by bdecode
		// to indicate that the program has not yet queried
		// the type of this entry, and sould not assume
		// that it has a certain type. This is asserted in
		// the accessor functions. This does not apply if
		// exceptions are used.
		mutable bool m_type_queried;
#endif
	};

#if TORRENT_USE_IOSTREAM
	inline std::ostream& operator<<(std::ostream& os, const entry& e)
	{
		e.print(os, 0);
		return os;
	}
#endif

#ifndef BOOST_NO_EXCEPTIONS
	inline void throw_type_error()
	{
		throw libtorrent_exception(error_code(errors::invalid_entry_type
			, libtorrent_category));
	}
#endif

	inline entry::data_type entry::type() const
	{
#ifdef TORRENT_DEBUG
		m_type_queried = true;
#endif
		return m_type;
	}

	inline entry::~entry() { destruct(); }

	inline void entry::operator=(const entry& e)
	{
		destruct();
		copy(e);
	}

	inline entry::integer_type& entry::integer()
	{
		if (m_type == undefined_t) construct(int_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != int_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == int_t);
		return *reinterpret_cast<integer_type*>(data);
	}

	inline entry::integer_type const& entry::integer() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != int_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == int_t);
		return *reinterpret_cast<const integer_type*>(data);
	}

	inline entry::string_type& entry::string()
	{
		if (m_type == undefined_t) construct(string_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != string_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == string_t);
		return *reinterpret_cast<string_type*>(data);
	}

	inline entry::string_type const& entry::string() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != string_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == string_t);
		return *reinterpret_cast<const string_type*>(data);
	}

	inline entry::list_type& entry::list()
	{
		if (m_type == undefined_t) construct(list_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != list_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == list_t);
		return *reinterpret_cast<list_type*>(data);
	}

	inline entry::list_type const& entry::list() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != list_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == list_t);
		return *reinterpret_cast<const list_type*>(data);
	}

	inline entry::dictionary_type& entry::dict()
	{
		if (m_type == undefined_t) construct(dictionary_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != dictionary_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == dictionary_t);
		return *reinterpret_cast<dictionary_type*>(data);
	}

	inline entry::dictionary_type const& entry::dict() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != dictionary_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == dictionary_t);
		return *reinterpret_cast<const dictionary_type*>(data);
	}

}

#endif // TORRENT_ENTRY_HPP_INCLUDED

<commit_msg>fixed inconsistent use of preprocessor macro for entry::print<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ENTRY_HPP_INCLUDED
#define TORRENT_ENTRY_HPP_INCLUDED

/*
 *
 * This file declares the entry class. It is a
 * variant-type that can be an integer, list,
 * dictionary (map) or a string. This type is
 * used to hold bdecoded data (which is the
 * encoding BitTorrent messages uses).
 *
 * it has 4 accessors to access the actual
 * type of the object. They are:
 * integer()
 * string()
 * list()
 * dict()
 * The actual type has to match the type you
 * are asking for, otherwise you will get an
 * assertion failure.
 * When you default construct an entry, it is
 * uninitialized. You can initialize it through the
 * assignment operator, copy-constructor or
 * the constructor that takes a data_type enum.
 *
 *
 */

#include <map>
#include <list>
#include <string>
#include <stdexcept>

#include "libtorrent/size_type.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/error_code.hpp"

#if TORRENT_USE_IOSTREAM
#include <iosfwd>
#endif

namespace libtorrent
{

	struct TORRENT_EXPORT type_error: std::runtime_error
	{
		type_error(const char* error): std::runtime_error(error) {}
	};

	namespace detail
	{
		template<int v1, int v2>
		struct max2 { enum { value = v1>v2?v1:v2 }; };

		template<int v1, int v2, int v3>
		struct max3
		{
			enum
			{
				temp = max2<v1,v2>::value,
				value = temp>v3?temp:v3
			};
		};

		template<int v1, int v2, int v3, int v4>
		struct max4
		{
			enum
			{
				temp = max3<v1,v2, v3>::value,
				value = temp>v4?temp:v4
			};
		};
	}

	class entry;

	class TORRENT_EXPORT entry
	{
	public:

		// the key is always a string. If a generic entry would be allowed
		// as a key, sorting would become a problem (e.g. to compare a string
		// to a list). The definition doesn't mention such a limit though.
		typedef std::map<std::string, entry> dictionary_type;
		typedef std::string string_type;
		typedef std::list<entry> list_type;
		typedef size_type integer_type;

		enum data_type
		{
			int_t,
			string_t,
			list_t,
			dictionary_t,
			undefined_t
		};

		data_type type() const;

		entry(dictionary_type const&);
		entry(string_type const&);
		entry(list_type const&);
		entry(integer_type const&);

		entry();
		entry(data_type t);
		entry(entry const& e);
		~entry();

		bool operator==(entry const& e) const;

		void operator=(entry const&);
		void operator=(dictionary_type const&);
		void operator=(string_type const&);
		void operator=(list_type const&);
		void operator=(integer_type const&);

		integer_type& integer();
		const integer_type& integer() const;
		string_type& string();
		const string_type& string() const;
		list_type& list();
		const list_type& list() const;
		dictionary_type& dict();
		const dictionary_type& dict() const;

		void swap(entry& e);

		// these functions requires that the entry
		// is a dictionary, otherwise they will throw
		entry& operator[](char const* key);
		entry& operator[](std::string const& key);
#ifndef BOOST_NO_EXCEPTIONS
		const entry& operator[](char const* key) const;
		const entry& operator[](std::string const& key) const;
#endif
		entry* find_key(char const* key);
		entry const* find_key(char const* key) const;
		entry* find_key(std::string const& key);
		entry const* find_key(std::string const& key) const;

#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
		void print(std::ostream& os, int indent = 0) const;
#endif

	protected:

		void construct(data_type t);
		void copy(const entry& e);
		void destruct();

	private:

		data_type m_type;

#if defined(_MSC_VER) && _MSC_VER < 1310
		// workaround for msvc-bug.
		// assumes sizeof(map<string, char>) == sizeof(map<string, entry>)
		// and sizeof(list<char>) == sizeof(list<entry>)
		union
		{
			char data[
				detail::max4<sizeof(std::list<char>)
				, sizeof(std::map<std::string, char>)
				, sizeof(string_type)
				, sizeof(integer_type)>::value];
			integer_type dummy_aligner;
		};
#else
		union
		{
			char data[detail::max4<sizeof(list_type)
				, sizeof(dictionary_type)
				, sizeof(string_type)
				, sizeof(integer_type)>::value];
			integer_type dummy_aligner;
		};
#endif

#ifdef TORRENT_DEBUG
	public:
		// in debug mode this is set to false by bdecode
		// to indicate that the program has not yet queried
		// the type of this entry, and sould not assume
		// that it has a certain type. This is asserted in
		// the accessor functions. This does not apply if
		// exceptions are used.
		mutable bool m_type_queried;
#endif
	};

#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
	inline std::ostream& operator<<(std::ostream& os, const entry& e)
	{
		e.print(os, 0);
		return os;
	}
#endif

#ifndef BOOST_NO_EXCEPTIONS
	inline void throw_type_error()
	{
		throw libtorrent_exception(error_code(errors::invalid_entry_type
			, libtorrent_category));
	}
#endif

	inline entry::data_type entry::type() const
	{
#ifdef TORRENT_DEBUG
		m_type_queried = true;
#endif
		return m_type;
	}

	inline entry::~entry() { destruct(); }

	inline void entry::operator=(const entry& e)
	{
		destruct();
		copy(e);
	}

	inline entry::integer_type& entry::integer()
	{
		if (m_type == undefined_t) construct(int_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != int_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == int_t);
		return *reinterpret_cast<integer_type*>(data);
	}

	inline entry::integer_type const& entry::integer() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != int_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == int_t);
		return *reinterpret_cast<const integer_type*>(data);
	}

	inline entry::string_type& entry::string()
	{
		if (m_type == undefined_t) construct(string_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != string_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == string_t);
		return *reinterpret_cast<string_type*>(data);
	}

	inline entry::string_type const& entry::string() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != string_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == string_t);
		return *reinterpret_cast<const string_type*>(data);
	}

	inline entry::list_type& entry::list()
	{
		if (m_type == undefined_t) construct(list_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != list_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == list_t);
		return *reinterpret_cast<list_type*>(data);
	}

	inline entry::list_type const& entry::list() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != list_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == list_t);
		return *reinterpret_cast<const list_type*>(data);
	}

	inline entry::dictionary_type& entry::dict()
	{
		if (m_type == undefined_t) construct(dictionary_t);
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != dictionary_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == dictionary_t);
		return *reinterpret_cast<dictionary_type*>(data);
	}

	inline entry::dictionary_type const& entry::dict() const
	{
#ifndef BOOST_NO_EXCEPTIONS
		if (m_type != dictionary_t) throw_type_error();
#elif defined TORRENT_DEBUG
		TORRENT_ASSERT(m_type_queried);
#endif
		TORRENT_ASSERT(m_type == dictionary_t);
		return *reinterpret_cast<const dictionary_type*>(data);
	}

}

#endif // TORRENT_ENTRY_HPP_INCLUDED

<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_QUAD_TREE_HPP
#define MAPNIK_QUAD_TREE_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/make_unique.hpp>
// stl
#include <algorithm>
#include <vector>
#include <type_traits>

#include <cstring>

namespace mapnik
{
template <typename T>
class quad_tree : util::noncopyable
{
    using value_type = T;
    struct node
    {
        using cont_type = std::vector<T>;
        using iterator = typename cont_type::iterator;
        using const_iterator = typename cont_type::const_iterator;
        box2d<double> extent_;
        cont_type cont_;
        node * children_[4];

        explicit node(box2d<double> const& ext)
            : extent_(ext)
        {
            std::fill(children_, children_ + 4, nullptr);
        }

        box2d<double> const& extent() const
        {
            return extent_;
        }

        iterator begin()
        {
            return cont_.begin();
        }

        const_iterator begin() const
        {
            return cont_.begin();
        }

        iterator end()
        {
            return cont_.end();
        }

        const_iterator end() const
        {
            return cont_.end();
        }

        int num_subnodes() const
        {
            int count = 0;
            for (int i = 0; i < 4; ++i)
            {
                if (children_[i]) ++count;
            }
            return count;
        }
        ~node () {}
    };

    using nodes_type = std::vector<std::unique_ptr<node> >;
    using cont_type = typename node::cont_type;
    using node_data_iterator = typename cont_type::iterator;

public:
    using iterator = typename nodes_type::iterator;
    using const_iterator = typename nodes_type::const_iterator;
    using result_type = typename std::vector<std::reference_wrapper<T> >;
    using query_iterator = typename result_type::iterator;

    explicit quad_tree(box2d<double> const& ext,
                       unsigned int max_depth = 8,
                       double ratio = 0.55)
        : max_depth_(max_depth),
          ratio_(ratio),
          query_result_(),
          nodes_()
    {
        nodes_.push_back(std::make_unique<node>(ext));
        root_ = nodes_[0].get();
    }

    void insert(T data, box2d<double> const& box)
    {
        unsigned int depth=0;
        do_insert_data(data,box,root_,depth);
    }

    query_iterator query_in_box(box2d<double> const& box)
    {
        query_result_.clear();
        query_node(box, query_result_, root_);
        return query_result_.begin();
    }

    query_iterator query_end()
    {
        return query_result_.end();
    }

    const_iterator begin() const
    {
        return nodes_.begin();
    }

    const_iterator end() const
    {
        return  nodes_.end();
    }

    void clear ()
    {
        box2d<double> ext = root_->extent_;
        nodes_.clear();
        nodes_.push_back(std::make_unique<node>(ext));
        root_ = nodes_[0].get();
    }

    box2d<double> const& extent() const
    {
        return root_->extent_;
    }

    int count() const
    {
        return count_nodes(root_);
    }

    int count_items() const
    {
        int count = 0;
        count_items(root_, count);
        return count;
    }
    void trim()
    {
        trim_tree(root_);
    }

    template <typename OutputStream>
    void write(OutputStream & out)
    {
        static_assert(std::is_standard_layout<value_type>::value,
                      "Values stored in quad-tree must be standard layout types to allow serialisation");
        char header[16];
        std::memset(header,0,16);
        header[0]='m';
        header[1]='a';
        header[2]='p';
        header[3]='n';
        header[4]='i';
        header[5]='k';
        out.write(header,16);
        write_node(out,root_);
    }
private:

    void query_node(box2d<double> const& box, result_type & result, node * node_) const
    {
        if (node_)
        {
            box2d<double> const& node_extent = node_->extent();
            if (box.intersects(node_extent))
            {
                for (auto & n : *node_)
                {
                    result.push_back(std::ref(n));
                }
                for (int k = 0; k < 4; ++k)
                {
                    query_node(box,result,node_->children_[k]);
                }
            }
        }
    }

    void do_insert_data(T data, box2d<double> const& box, node * n, unsigned int& depth)
    {
        if (++depth >= max_depth_)
        {
            n->cont_.push_back(data);
        }
        else
        {
            box2d<double> const& node_extent = n->extent();
            box2d<double> ext[4];
            split_box(node_extent,ext);
            for (int i = 0; i < 4; ++i)
            {
                if (ext[i].contains(box))
                {
                    if (!n->children_[i])
                    {
                        nodes_.push_back(std::make_unique<node>(ext[i]));
                        n->children_[i]=nodes_.back().get();
                    }
                    do_insert_data(data,box,n->children_[i],depth);
                    return;
                }
            }
            n->cont_.push_back(data);
        }
    }

    void split_box(box2d<double> const& node_extent,box2d<double> * ext)
    {
        double width=node_extent.width();
        double height=node_extent.height();

        double lox=node_extent.minx();
        double loy=node_extent.miny();
        double hix=node_extent.maxx();
        double hiy=node_extent.maxy();

        ext[0]=box2d<double>(lox,loy,lox + width * ratio_,loy + height * ratio_);
        ext[1]=box2d<double>(hix - width * ratio_,loy,hix,loy + height * ratio_);
        ext[2]=box2d<double>(lox,hiy - height*ratio_,lox + width * ratio_,hiy);
        ext[3]=box2d<double>(hix - width * ratio_,hiy - height*ratio_,hix,hiy);
    }

    void trim_tree(node * n)
    {
        if (n)
        {
            for (int i = 0; i < 4; ++i)
            {
                trim_tree(n->children_[i]);
            }
            if (n->num_subnodes() == 1 && n->cont_.size() == 0)
            {
                for (int i = 0; i < 4; ++i)
                {
                    if (n->children_[i])
                    {
                        n = n->children_[i];
                        break;
                    }
                }
            }
        }

    }

    int count_nodes(node const* n) const
    {
        if (!n) return 0;
        else
        {
            int count = 1;
            for (int i = 0; i < 4; ++i)
            {
                count += count_nodes(n->children_[i]);
            }
            return count;
        }
    }

    void count_items(node const* n,int& count) const
    {
        if (n)
        {
            count += n->cont_.size();
            for (int i = 0; i < 4; ++i)
            {
                count_items(n->children_[i],count);
            }
        }
    }

    int subnode_offset(node const* n) const
    {
        int offset = 0;
        for (int i = 0; i < 4; i++)
        {
            if (n->children_[i])
            {
                offset +=sizeof(box2d<double>) + (n->children_[i]->cont_.size() * sizeof(value_type)) + 3 * sizeof(int);
                offset +=subnode_offset(n->children_[i]);
            }
        }
        return offset;
    }

    template <typename OutputStream>
    void write_node(OutputStream & out, node const* n) const
    {
        if (n)
        {
            int offset=subnode_offset(n);
            int shape_count=n->cont_.size();
            int recsize=sizeof(box2d<double>) + 3 * sizeof(int) + shape_count * sizeof(value_type);
            std::unique_ptr<char[]> node_record(new char[recsize]);
            std::memset(node_record.get(), 0, recsize);
            std::memcpy(node_record.get(), &offset, 4);
            std::memcpy(node_record.get() + 4, &n->extent_, sizeof(box2d<double>));
            std::memcpy(node_record.get() + 36, &shape_count, 4);
            for (int i=0; i < shape_count; ++i)
            {
                memcpy(node_record.get() + 40 + i * sizeof(value_type), &(n->cont_[i]),sizeof(value_type));
            }
            int num_subnodes=0;
            for (int i = 0; i < 4; ++i)
            {
                if (n->children_[i])
                {
                    ++num_subnodes;
                }
            }
            std::memcpy(node_record.get() + 40 + shape_count * sizeof(value_type),&num_subnodes,4);
            out.write(node_record.get(),recsize);
            for (int i = 0; i < 4; ++i)
            {
                write_node(out, n->children_[i]);
            }
        }
    }

    const unsigned int max_depth_;
    const double ratio_;
    result_type query_result_;
    nodes_type nodes_;
    node * root_;

};
}

#endif // MAPNIK_QUAD_TREE_HPP
<commit_msg>quad_tree - pass node by *&<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_QUAD_TREE_HPP
#define MAPNIK_QUAD_TREE_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/make_unique.hpp>
// stl
#include <algorithm>
#include <vector>
#include <type_traits>

#include <cstring>

namespace mapnik
{
template <typename T>
class quad_tree : util::noncopyable
{
    using value_type = T;
    struct node
    {
        using cont_type = std::vector<T>;
        using iterator = typename cont_type::iterator;
        using const_iterator = typename cont_type::const_iterator;
        box2d<double> extent_;
        cont_type cont_;
        node * children_[4];

        explicit node(box2d<double> const& ext)
            : extent_(ext)
        {
            std::fill(children_, children_ + 4, nullptr);
        }

        box2d<double> const& extent() const
        {
            return extent_;
        }

        iterator begin()
        {
            return cont_.begin();
        }

        const_iterator begin() const
        {
            return cont_.begin();
        }

        iterator end()
        {
            return cont_.end();
        }

        const_iterator end() const
        {
            return cont_.end();
        }

        int num_subnodes() const
        {
            int count = 0;
            for (int i = 0; i < 4; ++i)
            {
                if (children_[i]) ++count;
            }
            return count;
        }
        ~node () {}
    };

    using nodes_type = std::vector<std::unique_ptr<node> >;
    using cont_type = typename node::cont_type;
    using node_data_iterator = typename cont_type::iterator;

public:
    using iterator = typename nodes_type::iterator;
    using const_iterator = typename nodes_type::const_iterator;
    using result_type = typename std::vector<std::reference_wrapper<T> >;
    using query_iterator = typename result_type::iterator;

    explicit quad_tree(box2d<double> const& ext,
                       unsigned int max_depth = 8,
                       double ratio = 0.55)
        : max_depth_(max_depth),
          ratio_(ratio),
          query_result_(),
          nodes_()
    {
        nodes_.push_back(std::make_unique<node>(ext));
        root_ = nodes_[0].get();
    }

    void insert(T data, box2d<double> const& box)
    {
        unsigned int depth=0;
        do_insert_data(data,box,root_,depth);
    }

    query_iterator query_in_box(box2d<double> const& box)
    {
        query_result_.clear();
        query_node(box, query_result_, root_);
        return query_result_.begin();
    }

    query_iterator query_end()
    {
        return query_result_.end();
    }

    const_iterator begin() const
    {
        return nodes_.begin();
    }

    const_iterator end() const
    {
        return  nodes_.end();
    }

    void clear ()
    {
        box2d<double> ext = root_->extent_;
        nodes_.clear();
        nodes_.push_back(std::make_unique<node>(ext));
        root_ = nodes_[0].get();
    }

    box2d<double> const& extent() const
    {
        return root_->extent_;
    }

    int count() const
    {
        return count_nodes(root_);
    }

    int count_items() const
    {
        int count = 0;
        count_items(root_, count);
        return count;
    }
    void trim()
    {
        trim_tree(root_);
    }

    template <typename OutputStream>
    void write(OutputStream & out)
    {
        static_assert(std::is_standard_layout<value_type>::value,
                      "Values stored in quad-tree must be standard layout types to allow serialisation");
        char header[16];
        std::memset(header,0,16);
        header[0]='m';
        header[1]='a';
        header[2]='p';
        header[3]='n';
        header[4]='i';
        header[5]='k';
        out.write(header,16);
        write_node(out,root_);
    }
private:

    void query_node(box2d<double> const& box, result_type & result, node * node_) const
    {
        if (node_)
        {
            box2d<double> const& node_extent = node_->extent();
            if (box.intersects(node_extent))
            {
                for (auto & n : *node_)
                {
                    result.push_back(std::ref(n));
                }
                for (int k = 0; k < 4; ++k)
                {
                    query_node(box,result,node_->children_[k]);
                }
            }
        }
    }

    void do_insert_data(T data, box2d<double> const& box, node * n, unsigned int& depth)
    {
        if (++depth >= max_depth_)
        {
            n->cont_.push_back(data);
        }
        else
        {
            box2d<double> const& node_extent = n->extent();
            box2d<double> ext[4];
            split_box(node_extent,ext);
            for (int i = 0; i < 4; ++i)
            {
                if (ext[i].contains(box))
                {
                    if (!n->children_[i])
                    {
                        nodes_.push_back(std::make_unique<node>(ext[i]));
                        n->children_[i]=nodes_.back().get();
                    }
                    do_insert_data(data,box,n->children_[i],depth);
                    return;
                }
            }
            n->cont_.push_back(data);
        }
    }

    void split_box(box2d<double> const& node_extent,box2d<double> * ext)
    {
        double width=node_extent.width();
        double height=node_extent.height();

        double lox=node_extent.minx();
        double loy=node_extent.miny();
        double hix=node_extent.maxx();
        double hiy=node_extent.maxy();

        ext[0]=box2d<double>(lox,loy,lox + width * ratio_,loy + height * ratio_);
        ext[1]=box2d<double>(hix - width * ratio_,loy,hix,loy + height * ratio_);
        ext[2]=box2d<double>(lox,hiy - height*ratio_,lox + width * ratio_,hiy);
        ext[3]=box2d<double>(hix - width * ratio_,hiy - height*ratio_,hix,hiy);
    }

    void trim_tree(node *& n)
    {
        if (n)
        {
            for (int i = 0; i < 4; ++i)
            {
                trim_tree(n->children_[i]);
            }
            if (n->num_subnodes() == 1 && n->cont_.size() == 0)
            {
                for (int i = 0; i < 4; ++i)
                {
                    if (n->children_[i])
                    {
                        n = n->children_[i];
                        break;
                    }
                }
            }
        }

    }

    int count_nodes(node const* n) const
    {
        if (!n) return 0;
        else
        {
            int count = 1;
            for (int i = 0; i < 4; ++i)
            {
                count += count_nodes(n->children_[i]);
            }
            return count;
        }
    }

    void count_items(node const* n,int& count) const
    {
        if (n)
        {
            count += n->cont_.size();
            for (int i = 0; i < 4; ++i)
            {
                count_items(n->children_[i],count);
            }
        }
    }

    int subnode_offset(node const* n) const
    {
        int offset = 0;
        for (int i = 0; i < 4; i++)
        {
            if (n->children_[i])
            {
                offset +=sizeof(box2d<double>) + (n->children_[i]->cont_.size() * sizeof(value_type)) + 3 * sizeof(int);
                offset +=subnode_offset(n->children_[i]);
            }
        }
        return offset;
    }

    template <typename OutputStream>
    void write_node(OutputStream & out, node const* n) const
    {
        if (n)
        {
            int offset=subnode_offset(n);
            int shape_count=n->cont_.size();
            int recsize=sizeof(box2d<double>) + 3 * sizeof(int) + shape_count * sizeof(value_type);
            std::unique_ptr<char[]> node_record(new char[recsize]);
            std::memset(node_record.get(), 0, recsize);
            std::memcpy(node_record.get(), &offset, 4);
            std::memcpy(node_record.get() + 4, &n->extent_, sizeof(box2d<double>));
            std::memcpy(node_record.get() + 36, &shape_count, 4);
            for (int i=0; i < shape_count; ++i)
            {
                memcpy(node_record.get() + 40 + i * sizeof(value_type), &(n->cont_[i]),sizeof(value_type));
            }
            int num_subnodes=0;
            for (int i = 0; i < 4; ++i)
            {
                if (n->children_[i])
                {
                    ++num_subnodes;
                }
            }
            std::memcpy(node_record.get() + 40 + shape_count * sizeof(value_type),&num_subnodes,4);
            out.write(node_record.get(),recsize);
            for (int i = 0; i < 4; ++i)
            {
                write_node(out, n->children_[i]);
            }
        }
    }

    const unsigned int max_depth_;
    const double ratio_;
    result_type query_result_;
    nodes_type nodes_;
    node * root_;

};
}

#endif // MAPNIK_QUAD_TREE_HPP
<|endoftext|>