<commit_before>#ifndef CONFIG_ARGS_HPP_
#define CONFIG_ARGS_HPP_

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.4"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                64

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     64

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// The io batch factor ensures a minimum number of i/o operations
// which are picked from any specific i/o account consecutively.
// A higher value might be advantageous for throughput if seek times
// matter on the underlying i/o system. A low value improves latency.
// The advantage only holds as long as each account has a tendentially
// sequential set of i/o operations though. If access patterns are random
// for all accounts, a low io batch factor does just as well (as bad) when it comes
// to the number of random seeks, but might still provide a lower latency
// than a high batch factor. Our serializer writes usually generate a sequential
// access pattern and therefore take considerable advantage from a high io batch
// factor.
#define DEFAULT_IO_BATCH_FACTOR                   8

// Currently, each cache uses two IO accounts:
// one account for writes, and one account for reads.
// By adjusting the priorities of these accounts, reads
// can be prioritized over writes or the other way around.
//
// This is a one-per-serializer/file priority.
// The per-cache priorities are dynamically derived by dividing these priorities
// by the number of slices on a specific file.
#define CACHE_READS_IO_PRIORITY                   512
#define CACHE_WRITES_IO_PRIORITY                  64

// Garbage Colletion uses its own two IO accounts.
// There is one low-priority account that is meant to guarantee
// (performance-wise) unintrusive garbage collection.
// If the garbage ratio keeps growing,
// GC starts using the high priority account instead, which
// might have a negative influence on database performance
// under i/o heavy workloads but guarantees that the database
// doesn't grow indefinitely.
//
// This is a one-per-serializer/file priority.
#define GC_IO_PRIORITY_NICE                       8
#define GC_IO_PRIORITY_HIGH                       (2 * CACHE_WRITES_IO_PRIORITY)

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (512 * KILOBYTE)

// Max number of blocks which can be read ahead in one i/o transaction (if enabled)
#define MAX_READ_AHEAD_BLOCKS 32

// Ratio of free ram to use for the cache by default
// TODO: DEFAULT_MAX_CACHE_RATIO is unused. Should it be deleted?
#define DEFAULT_MAX_CACHE_RATIO                   0.5

// Maximum number of threads we support
// TODO: make this dynamic where possible
#define MAX_THREADS                               128

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    1000

// flush_waiting_threshold is the maximal number of transactions which can wait
// for a sync before a flush gets triggered on any single slice. As transactions only wait for
// sync with wait_for_flush enabled, this option plays a role only then.
#define DEFAULT_FLUSH_WAITING_THRESHOLD           8

// If wait_for_flush is true, concurrent flushing can be used to reduce the latency
// of each single flush. max_concurrent_flushes controls how many flushes can be active
// on a specific slice at any given time.
#define DEFAULT_MAX_CONCURRENT_FLUSHES            1

// If the size of the data affected by the current set of patches in a block is larger than
// block size / MAX_PATCHES_SIZE_RATIO, we flush the block instead of waiting for
// more patches to come. Flushing the block means that we rewrite the actual data
// block (including all patches). In this case, we don't have to store any of the
// patches for that block, because to block will be re-written anyway. This results
// in improved CPU efficiency, because we save the overhead of storing and managing
// patches.
// In summary: larger max patches size ratio -> less usage of patches, more i/o
//     smaller max patches size ratio -> more usage of patches, less i/o, more CPU usage
//
// Note: An average write transaction under canonical workload leads to patches of about 75
// bytes of affected data.
// The actual value is constantly adjusted between MAX_PATCHES_SIZE_RATIO_MIN and
// MAX_PATCHES_SIZE_RATIO_MAX depending on whether the system is i/o bound or not.
// An exception from this is operating in durability mode, where any write
// operation is ultimately i/o bound anyway. In this case, the patches size ratio
// does not get dynamically adjusted but stays constant at MAX_PATCHES_SIZE_RATIO_DURABILITY.
#define MAX_PATCHES_SIZE_RATIO_MIN                100
#define MAX_PATCHES_SIZE_RATIO_MAX                2
#define MAX_PATCHES_SIZE_RATIO_DURABILITY         5
#define RAISE_PATCHES_RATIO_AT_FRACTION_OF_UNSAVED_DATA_LIMIT 0.6

// If more than this many bytes of dirty data accumulate in the cache, then write
// transactions will be throttled.
// A value of 0 means that it will automatically be set to MAX_UNSAVED_DATA_LIMIT_FRACTION
// times the max cache size
#define DEFAULT_UNSAVED_DATA_LIMIT                4096 * MEGABYTE

// The unsaved data limit cannot exceed this fraction of the max cache size
#define MAX_UNSAVED_DATA_LIMIT_FRACTION           0.9

// We start flushing dirty pages as soon as we hit this fraction of the unsaved data limit
#define FLUSH_AT_FRACTION_OF_UNSAVED_DATA_LIMIT   0.2

// How many times the page replacement algorithm tries to find an eligible page before giving up.
// Note that (MAX_UNSAVED_DATA_LIMIT_FRACTION ** PAGE_REPL_NUM_TRIES) is the probability that the
// page replacement algorithm will succeed on a given try, and if that probability is less than 1/2
// then the page replacement algorithm will on average be unable to evict pages from the cache.
#define PAGE_REPL_NUM_TRIES                       10

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// memcached specifies the maximum value size to be 1MB, but customers asked this to be much higher
#define MAX_VALUE_SIZE                            10 * MEGABYTE

// Values larger than this will be streamed in a get operation
#define MAX_BUFFERED_GET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// If a single connection sends this many 'noreply' commands, the next command will
// have to wait until the first one finishes
#define MAX_CONCURRENT_QUERIES_PER_CONNECTION     500

// The number of concurrent queries when loading memcached operations from a file.
#define MAX_CONCURRENT_QUEURIES_ON_IMPORT         1000

// How many timestamps we store in a leaf node.  We store the
// NUM_LEAF_NODE_EARLIER_TIMES+1 most-recent timestamps.
#define NUM_LEAF_NODE_EARLIER_TIMES               4

// We assume there will never be more than this many blocks. The value
// is computed by dividing 1 TB by the smallest reasonable block size.
// This value currently fits in 32 bits, and so block_id_t is a uint32_t.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// Special block IDs.  These don't really belong here because they're
// more magic constants than tunable parameters.

// The btree superblock, which has a reference to the root node block
// id.
#define SUPERBLOCK_ID                             0
// HEY: This is kind of fragile because some patch disk storage code
// expects this value to be 1 (since the free list returns 1 the first
// time a block id is generated, or something).
#define MC_CONFIGBLOCK_ID                         (SUPERBLOCK_ID + 1)

// The ratio at which we should start GCing.  (HEY: What's the extra
// 0.000001 in MAX_GC_HIGH_RATIO for?  Is it because we told the user
// that 0.99 was too high?)
#define DEFAULT_GC_HIGH_RATIO                     0.65
// TODO: MAX_GC_HIGH_RATIO is unused.  Use it!
//  - Keeping this around because if it becomes configurable again, we
//    should have these limits.  Before then at least rassert it.
#define MAX_GC_HIGH_RATIO                         0.990001

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.5
// TODO: MIN_GC_LOW_RATIO is unused.  Use it!
//  - Keeping this around because if it becomes configurable again, we
//    should have these limits.  Before then at least rassert it.
#define MIN_GC_LOW_RATIO                          0.099999

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// If the size of the LBA on a given disk exceeds LBA_MIN_SIZE_FOR_GC, then the fraction of the
// entries that are live and not garbage should be at least LBA_MIN_UNGARBAGE_FRACTION.
#define LBA_MIN_SIZE_FOR_GC                       (MEGABYTE * 20)
#define LBA_MIN_UNGARBAGE_FRACTION                0.15

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many bytes of buffering space we can use per disk when reading the LBA. If it's set
// too high, then RethinkDB will eat a lot of memory at startup. This is bad because tcmalloc
// doesn't return memory to the OS. If it's set too low, startup will take a longer time.
#define LBA_READ_BUFFER_SIZE                      GIGABYTE

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               1

// The size of zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE

#define COROUTINE_STACK_SIZE                      131072

#define MAX_COROS_PER_THREAD                      10000

// Size of a cache line (used in cache_line_padded_t).
#define CACHE_LINE_SIZE                           64

#endif  // CONFIG_ARGS_HPP_

<commit_msg>Shrunk the default gc ratio to 0.15-0.25.<commit_after>#ifndef CONFIG_ARGS_HPP_
#define CONFIG_ARGS_HPP_

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.4"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                64

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     64

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// The io batch factor ensures a minimum number of i/o operations
// which are picked from any specific i/o account consecutively.
// A higher value might be advantageous for throughput if seek times
// matter on the underlying i/o system. A low value improves latency.
// The advantage only holds as long as each account has a tendentially
// sequential set of i/o operations though. If access patterns are random
// for all accounts, a low io batch factor does just as well (as bad) when it comes
// to the number of random seeks, but might still provide a lower latency
// than a high batch factor. Our serializer writes usually generate a sequential
// access pattern and therefore take considerable advantage from a high io batch
// factor.
#define DEFAULT_IO_BATCH_FACTOR                   8

// Currently, each cache uses two IO accounts:
// one account for writes, and one account for reads.
// By adjusting the priorities of these accounts, reads
// can be prioritized over writes or the other way around.
//
// This is a one-per-serializer/file priority.
// The per-cache priorities are dynamically derived by dividing these priorities
// by the number of slices on a specific file.
#define CACHE_READS_IO_PRIORITY                   512
#define CACHE_WRITES_IO_PRIORITY                  64

// Garbage Colletion uses its own two IO accounts.
// There is one low-priority account that is meant to guarantee
// (performance-wise) unintrusive garbage collection.
// If the garbage ratio keeps growing,
// GC starts using the high priority account instead, which
// might have a negative influence on database performance
// under i/o heavy workloads but guarantees that the database
// doesn't grow indefinitely.
//
// This is a one-per-serializer/file priority.
#define GC_IO_PRIORITY_NICE                       8
#define GC_IO_PRIORITY_HIGH                       (2 * CACHE_WRITES_IO_PRIORITY)

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (512 * KILOBYTE)

// Max number of blocks which can be read ahead in one i/o transaction (if enabled)
#define MAX_READ_AHEAD_BLOCKS 32

// Ratio of free ram to use for the cache by default
// TODO: DEFAULT_MAX_CACHE_RATIO is unused. Should it be deleted?
#define DEFAULT_MAX_CACHE_RATIO                   0.5

// Maximum number of threads we support
// TODO: make this dynamic where possible
#define MAX_THREADS                               128

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    1000

// flush_waiting_threshold is the maximal number of transactions which can wait
// for a sync before a flush gets triggered on any single slice. As transactions only wait for
// sync with wait_for_flush enabled, this option plays a role only then.
#define DEFAULT_FLUSH_WAITING_THRESHOLD           8

// If wait_for_flush is true, concurrent flushing can be used to reduce the latency
// of each single flush. max_concurrent_flushes controls how many flushes can be active
// on a specific slice at any given time.
#define DEFAULT_MAX_CONCURRENT_FLUSHES            1

// If the size of the data affected by the current set of patches in a block is larger than
// block size / MAX_PATCHES_SIZE_RATIO, we flush the block instead of waiting for
// more patches to come. Flushing the block means that we rewrite the actual data
// block (including all patches). In this case, we don't have to store any of the
// patches for that block, because to block will be re-written anyway. This results
// in improved CPU efficiency, because we save the overhead of storing and managing
// patches.
// In summary: larger max patches size ratio -> less usage of patches, more i/o
//     smaller max patches size ratio -> more usage of patches, less i/o, more CPU usage
//
// Note: An average write transaction under canonical workload leads to patches of about 75
// bytes of affected data.
// The actual value is constantly adjusted between MAX_PATCHES_SIZE_RATIO_MIN and
// MAX_PATCHES_SIZE_RATIO_MAX depending on whether the system is i/o bound or not.
// An exception from this is operating in durability mode, where any write
// operation is ultimately i/o bound anyway. In this case, the patches size ratio
// does not get dynamically adjusted but stays constant at MAX_PATCHES_SIZE_RATIO_DURABILITY.
#define MAX_PATCHES_SIZE_RATIO_MIN                100
#define MAX_PATCHES_SIZE_RATIO_MAX                2
#define MAX_PATCHES_SIZE_RATIO_DURABILITY         5
#define RAISE_PATCHES_RATIO_AT_FRACTION_OF_UNSAVED_DATA_LIMIT 0.6

// If more than this many bytes of dirty data accumulate in the cache, then write
// transactions will be throttled.
// A value of 0 means that it will automatically be set to MAX_UNSAVED_DATA_LIMIT_FRACTION
// times the max cache size
#define DEFAULT_UNSAVED_DATA_LIMIT                4096 * MEGABYTE

// The unsaved data limit cannot exceed this fraction of the max cache size
#define MAX_UNSAVED_DATA_LIMIT_FRACTION           0.9

// We start flushing dirty pages as soon as we hit this fraction of the unsaved data limit
#define FLUSH_AT_FRACTION_OF_UNSAVED_DATA_LIMIT   0.2

// How many times the page replacement algorithm tries to find an eligible page before giving up.
// Note that (MAX_UNSAVED_DATA_LIMIT_FRACTION ** PAGE_REPL_NUM_TRIES) is the probability that the
// page replacement algorithm will succeed on a given try, and if that probability is less than 1/2
// then the page replacement algorithm will on average be unable to evict pages from the cache.
#define PAGE_REPL_NUM_TRIES                       10

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// memcached specifies the maximum value size to be 1MB, but customers asked this to be much higher
#define MAX_VALUE_SIZE                            10 * MEGABYTE

// Values larger than this will be streamed in a get operation
#define MAX_BUFFERED_GET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// If a single connection sends this many 'noreply' commands, the next command will
// have to wait until the first one finishes
#define MAX_CONCURRENT_QUERIES_PER_CONNECTION     500

// The number of concurrent queries when loading memcached operations from a file.
#define MAX_CONCURRENT_QUEURIES_ON_IMPORT         1000

// How many timestamps we store in a leaf node.  We store the
// NUM_LEAF_NODE_EARLIER_TIMES+1 most-recent timestamps.
#define NUM_LEAF_NODE_EARLIER_TIMES               4

// We assume there will never be more than this many blocks. The value
// is computed by dividing 1 TB by the smallest reasonable block size.
// This value currently fits in 32 bits, and so block_id_t is a uint32_t.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// Special block IDs.  These don't really belong here because they're
// more magic constants than tunable parameters.

// The btree superblock, which has a reference to the root node block
// id.
#define SUPERBLOCK_ID                             0
// HEY: This is kind of fragile because some patch disk storage code
// expects this value to be 1 (since the free list returns 1 the first
// time a block id is generated, or something).
#define MC_CONFIGBLOCK_ID                         (SUPERBLOCK_ID + 1)

// The ratio at which we should start GCing.  (HEY: What's the extra
// 0.000001 in MAX_GC_HIGH_RATIO for?  Is it because we told the user
// that 0.99 was too high?)
#define DEFAULT_GC_HIGH_RATIO                     0.25
// TODO: MAX_GC_HIGH_RATIO is unused.  Use it!
// TODO: Probably this value is way too high.
//  - Keeping this around because if it becomes configurable again, we
//    should have these limits.  Before then at least rassert it.
#define MAX_GC_HIGH_RATIO                         0.990001

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.15
// TODO: MIN_GC_LOW_RATIO is unused.  Use it!
//  - Keeping this around because if it becomes configurable again, we
//    should have these limits.  Before then at least rassert it.
#define MIN_GC_LOW_RATIO                          0.099999

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// If the size of the LBA on a given disk exceeds LBA_MIN_SIZE_FOR_GC, then the fraction of the
// entries that are live and not garbage should be at least LBA_MIN_UNGARBAGE_FRACTION.
#define LBA_MIN_SIZE_FOR_GC                       (MEGABYTE * 20)
#define LBA_MIN_UNGARBAGE_FRACTION                0.15

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many bytes of buffering space we can use per disk when reading the LBA. If it's set
// too high, then RethinkDB will eat a lot of memory at startup. This is bad because tcmalloc
// doesn't return memory to the OS. If it's set too low, startup will take a longer time.
#define LBA_READ_BUFFER_SIZE                      GIGABYTE

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               1

// The size of zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE

#define COROUTINE_STACK_SIZE                      131072

#define MAX_COROS_PER_THREAD                      10000

// Size of a cache line (used in cache_line_padded_t).
#define CACHE_LINE_SIZE                           64

#endif  // CONFIG_ARGS_HPP_

<|endoftext|>


<commit_before>#include "Makefile.hpp"

#include <configure/BuildGraph.hpp>
#include <configure/Build.hpp>
#include <configure/Command.hpp>
#include <configure/Filesystem.hpp>
#include <configure/Graph.hpp>
#include <configure/quote.hpp>

#include <boost/algorithm/string/join.hpp>
#include <boost/graph/topological_sort.hpp>

#include <fstream>
#include <unordered_set>

namespace configure { namespace generators {

	std::string Makefile::name() const
	{ return "Makefile"; }

	void Makefile::generate(Build& build) const
	{
		std::ofstream out((build.directory() / "Makefile").string());
		out << "# Generated makefile" << std::endl;
		BuildGraph const& bg = build.build_graph();
		Graph const& g = bg.graph();

		// Fill the "all" rule
		{
			std::set<Node*> all;
			std::set<Node*> phony;

			// Search leaf nodes in the graph which are not virtual nodes
			for (auto vertex_range = boost::vertices(g);
			     vertex_range.first != vertex_range.second;
			     ++vertex_range.first)
			{
				auto vertex = *vertex_range.first;
				auto& node = bg.node(vertex);
				if (node->is_virtual())
				{
					if (!node->name().empty())
						phony.insert(node.get());
					continue;
				}
				// Check if a node is not a dependency of something else
				// then it should be in the "all" rule.
				auto out_edge_range = boost::out_edges(vertex, g);
				if (out_edge_range.first == out_edge_range.second)
					all.insert(node.get());
			}

			// Add outputs of the all virtual node
			for (auto in_edge_range = boost::in_edges(build.virtual_node("all")->index, g);
			     in_edge_range.first != in_edge_range.second;
			     ++in_edge_range.first)
			{
				all.insert(
					bg.node(boost::source(*in_edge_range.first, g)).get()
				);
			}

			out << ".PHONY:" << std::endl;
			out << ".PHONY: all";
			for (auto node: phony)
				out << ' ' << node->name();
			out << "\n\n";

			if (!all.empty())
			{
				out << "all:";
				for (auto& node: all)
				{
					if (node->is_file())
						out << ' ' << this->node_path(build, *node);
					else if (node->is_virtual() && !node->name().empty())
						out << ' ' << node->name();
				}
				out << "\n\n";
			}
		}

		for (auto vertex_range = boost::vertices(g);
		     vertex_range.first != vertex_range.second;
		     ++vertex_range.first)
		{
			auto vertex = *vertex_range.first;
			auto node = bg.node(vertex).get();

			auto in_edge_range = boost::in_edges(vertex, g);

			if (in_edge_range.first == in_edge_range.second)
				continue;

			if (node->is_virtual())
				out << node->name() << ':';
			else
				out << this->node_path(build, *node) << ':';

			for (GraphTraits::in_edge_iterator i = in_edge_range.first;
			     i != in_edge_range.second; ++i)
			{
				auto node = bg.node(boost::source(*i, g)).get();
				if (node->is_file())
					out << ' ' << this->node_path(build, *node);
				else if (node->is_virtual())
					out << ' ' << node->name();
			}
			out << std::endl;

			std::unordered_set<Command const*> seen_commands;
			for (; in_edge_range.first != in_edge_range.second;
			     ++in_edge_range.first)
			{
				Command const* cmd_ptr = &bg.link(*in_edge_range.first).command();
				if (seen_commands.count(cmd_ptr) != 0)
					continue;
				seen_commands.insert(cmd_ptr);
				for (auto const& shell_command: cmd_ptr->shell_commands())
				{
					out << '\t';
					this->dump_command(out, shell_command.dump());
					out << std::endl;
				}
			}
			out << std::endl;
		}

	}

	std::string Makefile::node_path(Build&, Node& node) const
	{
		return node.path().string();
	}

	void Makefile::dump_command(
		    std::ostream& out,
		    std::vector<std::string> const& cmd) const
	{
		out << quote<CommandParser::make>(cmd);
	}

	bool Makefile::is_available(Build& build) const
	{
		return build.fs().which("make") != boost::none;
	}
	std::vector<std::string>
	Makefile::build_command(Build& build, std::string const& target) const
	{
		return {
			"make", "-C", build.directory().string(),
			target.empty() ? "all" : target
		};
	}
}}

<commit_msg>Add a workaround of GNU Make silly parsing rules on Windows.<commit_after>#include "Makefile.hpp"

#include <configure/BuildGraph.hpp>
#include <configure/Build.hpp>
#include <configure/Command.hpp>
#include <configure/Filesystem.hpp>
#include <configure/Graph.hpp>
#include <configure/quote.hpp>

#include <boost/algorithm/string/join.hpp>
#include <boost/graph/topological_sort.hpp>

#include <fstream>
#include <unordered_set>

namespace configure { namespace generators {

	std::string Makefile::name() const
	{ return "Makefile"; }

	void Makefile::generate(Build& build) const
	{
		std::ofstream out((build.directory() / "Makefile").string());
		out << "# Generated makefile" << std::endl;
		BuildGraph const& bg = build.build_graph();
		Graph const& g = bg.graph();

		// Fill the "all" rule
		{
			std::set<Node*> all;
			std::set<Node*> phony;

			// Search leaf nodes in the graph which are not virtual nodes
			for (auto vertex_range = boost::vertices(g);
			     vertex_range.first != vertex_range.second;
			     ++vertex_range.first)
			{
				auto vertex = *vertex_range.first;
				auto& node = bg.node(vertex);
				if (node->is_virtual())
				{
					if (!node->name().empty())
						phony.insert(node.get());
					continue;
				}
				// Check if a node is not a dependency of something else
				// then it should be in the "all" rule.
				auto out_edge_range = boost::out_edges(vertex, g);
				if (out_edge_range.first == out_edge_range.second)
					all.insert(node.get());
			}

			// Add outputs of the all virtual node
			for (auto in_edge_range = boost::in_edges(build.virtual_node("all")->index, g);
			     in_edge_range.first != in_edge_range.second;
			     ++in_edge_range.first)
			{
				all.insert(
					bg.node(boost::source(*in_edge_range.first, g)).get()
				);
			}

			out << ".PHONY:" << std::endl;
			out << ".PHONY: all";
			for (auto node: phony)
				out << ' ' << node->name();
			out << "\n\n";

			if (!all.empty())
			{
				out << "all:";
				for (auto& node: all)
				{
					if (node->is_file())
						out << ' ' << this->node_path(build, *node);
					else if (node->is_virtual() && !node->name().empty())
						out << ' ' << node->name();
				}
				out << "\n\n";
			}
		}

		for (auto vertex_range = boost::vertices(g);
		     vertex_range.first != vertex_range.second;
		     ++vertex_range.first)
		{
			auto vertex = *vertex_range.first;
			auto node = bg.node(vertex).get();

			auto in_edge_range = boost::in_edges(vertex, g);

			if (in_edge_range.first == in_edge_range.second)
				continue;

			if (node->is_virtual())
				out << node->name() << ':';
			else
				out << this->node_path(build, *node) << ':';

			for (GraphTraits::in_edge_iterator i = in_edge_range.first;
			     i != in_edge_range.second; ++i)
			{
				auto node = bg.node(boost::source(*i, g)).get();
				if (node->is_file())
					out << ' ' << this->node_path(build, *node);
				else if (node->is_virtual())
					out << ' ' << node->name();
			}
			out << std::endl;

			std::unordered_set<Command const*> seen_commands;
			for (; in_edge_range.first != in_edge_range.second;
			     ++in_edge_range.first)
			{
				Command const* cmd_ptr = &bg.link(*in_edge_range.first).command();
				if (seen_commands.count(cmd_ptr) != 0)
					continue;
				seen_commands.insert(cmd_ptr);
				for (auto const& shell_command: cmd_ptr->shell_commands())
				{
					out << '\t';
					this->dump_command(out, shell_command.dump());
					out << std::endl;
				}
			}
			out << std::endl;
		}

	}

	std::string Makefile::node_path(Build&, Node& node) const
	{
		return node.path().string();
	}

	void Makefile::dump_command(
		    std::ostream& out,
		    std::vector<std::string> const& cmd) const
	{
#ifdef _WIN32
		if (cmd.size() == 1)
		{
			// Workaroung a bug in GNU Make, when commands contain a double quote
			// they are spawned through CreateProcess() as 'sh -c \"COMMAND HERE\"'
			// when the argument does have any special character other than \
			// and there is only one argument, quotes are left ...
			auto res = quote<CommandParser::make>(cmd);
			if (res[0] == '"' && res.back() == '"' )
			{
				res = res.substr(1, res.size() - 2);
			}
			out << res;
			return;
		}
#endif
		out << quote<CommandParser::make>(cmd);
	}

	bool Makefile::is_available(Build& build) const
	{
		return build.fs().which("make") != boost::none;
	}
	std::vector<std::string>
	Makefile::build_command(Build& build, std::string const& target) const
	{
		return {
			"make", "-C", build.directory().string(),
			target.empty() ? "all" : target
		};
	}
}}

<|endoftext|>


<commit_before>#pragma once

// `krbn::receiver` can be used safely in a multi-threaded environment.

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "device_grabber.hpp"
#include "dispatcher.hpp"
#include "grabbable_state_queues_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "session.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final : public dispatcher::dispatcher_client {
public:
  receiver(const receiver&) = delete;

  receiver(std::weak_ptr<dispatcher::dispatcher> weak_dispatcher) : dispatcher_client(weak_dispatcher) {
    dispatcher_ = std::make_unique<thread_utility::dispatcher>();

    std::string socket_file_path(constants::get_grabber_socket_file_path());

    unlink(socket_file_path.c_str());

    size_t buffer_size = 32 * 1024;
    std::chrono::milliseconds server_check_interval(3000);
    std::chrono::milliseconds reconnect_interval(1000);

    server_manager_ = std::make_unique<local_datagram::server_manager>(weak_dispatcher_,
                                                                       socket_file_path,
                                                                       buffer_size,
                                                                       server_check_interval,
                                                                       reconnect_interval);

    server_manager_->bound.connect([this, socket_file_path] {
      dispatcher_->enqueue([socket_file_path] {
        if (auto uid = session::get_current_console_user_id()) {
          chown(socket_file_path.c_str(), *uid, 0);
        }
        chmod(socket_file_path.c_str(), 0600);

        grabbable_state_queues_manager::get_shared_instance()->clear();
      });
    });

    server_manager_->bind_failed.connect([this](auto&& error_code) {
      dispatcher_->enqueue([] {
        logger::get_logger().error("receiver bind_failed");
      });
    });

    server_manager_->received.connect([this](auto&& buffer) {
      dispatcher_->enqueue([this, buffer] {
        if (auto type = types::find_operation_type(*buffer)) {
          switch (*type) {
            case operation_type::grabbable_state_changed:
              if (buffer->size() != sizeof(operation_type_grabbable_state_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::grabbable_state_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_grabbable_state_changed_struct*>(&((*buffer)[0]));

                grabbable_state_queues_manager::get_shared_instance()->update_grabbable_state(p->grabbable_state);
              }
              break;

            case operation_type::connect_console_user_server:
              if (buffer->size() != sizeof(operation_type_connect_console_user_server_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::connect_console_user_server`.");
              } else {
                auto p = reinterpret_cast<operation_type_connect_console_user_server_struct*>(&((*buffer)[0]));

                // Ensure user_core_configuration_file_path is null-terminated string even if corrupted data is sent.
                p->user_core_configuration_file_path[sizeof(p->user_core_configuration_file_path) - 1] = '\0';
                std::string user_core_configuration_file_path(p->user_core_configuration_file_path);

                logger::get_logger().info("karabiner_console_user_server is connected (pid:{0})", p->pid);

                console_user_server_client_ = nullptr;
                console_user_server_client_ = std::make_shared<console_user_server_client>(weak_dispatcher_);

                console_user_server_client_->connected.connect([this, user_core_configuration_file_path] {
                  dispatcher_->enqueue([this, user_core_configuration_file_path] {
                    stop_device_grabber();
                    start_device_grabber(user_core_configuration_file_path);
                  });
                });

                console_user_server_client_->connect_failed.connect([this](auto&& error_code) {
                  dispatcher_->enqueue([this] {
                    console_user_server_client_ = nullptr;

                    stop_device_grabber();
                    start_grabbing_if_system_core_configuration_file_exists();
                  });
                });

                console_user_server_client_->closed.connect([this] {
                  dispatcher_->enqueue([this] {
                    console_user_server_client_ = nullptr;

                    stop_device_grabber();
                    start_grabbing_if_system_core_configuration_file_exists();
                  });
                });

                console_user_server_client_->async_start();
              }
              break;

            case operation_type::system_preferences_updated:
              if (buffer->size() < sizeof(operation_type_system_preferences_updated_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::system_preferences_updated`.");
              } else {
                auto p = reinterpret_cast<operation_type_system_preferences_updated_struct*>(&((*buffer)[0]));

                system_preferences_ = p->system_preferences;

                if (device_grabber_) {
                  device_grabber_->async_set_system_preferences(p->system_preferences);
                }

                logger::get_logger().info("`system_preferences` is updated.");
              }
              break;

            case operation_type::frontmost_application_changed:
              if (buffer->size() < sizeof(operation_type_frontmost_application_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::frontmost_application_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_frontmost_application_changed_struct*>(&((*buffer)[0]));

                // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
                p->bundle_identifier[sizeof(p->bundle_identifier) - 1] = '\0';
                p->file_path[sizeof(p->file_path) - 1] = '\0';

                frontmost_application_bundle_identifier_ = p->bundle_identifier;
                frontmost_application_file_path_ = p->file_path;

                if (device_grabber_) {
                  device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                                frontmost_application_file_path_);
                }
              }
              break;

            case operation_type::input_source_changed:
              if (buffer->size() < sizeof(operation_type_input_source_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::input_source_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_input_source_changed_struct*>(&((*buffer)[0]));

                // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
                p->language[sizeof(p->language) - 1] = '\0';
                p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
                p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

                input_source_identifiers_ = input_source_identifiers(std::string(p->language),
                                                                     std::string(p->input_source_id),
                                                                     std::string(p->input_mode_id));

                if (device_grabber_) {
                  device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);
                }
              }
              break;

            default:
              break;
          }
        }
      });
    });

    server_manager_->async_start();

    start_grabbing_if_system_core_configuration_file_exists();

    logger::get_logger().info("receiver is initialized");
  }

  ~receiver(void) {
    dispatcher_->enqueue([this] {
      server_manager_ = nullptr;
      console_user_server_client_ = nullptr;
      stop_device_grabber();
    });

    dispatcher_->terminate();
    dispatcher_ = nullptr;

    logger::get_logger().info("receiver is terminated");
  }

private:
  void start_grabbing_if_system_core_configuration_file_exists(void) {
    auto file_path = constants::get_system_core_configuration_file_path();
    if (filesystem::exists(file_path)) {
      stop_device_grabber();
      start_device_grabber(file_path);
    }
  }

  void start_device_grabber(const std::string& configuration_file_path) {
    if (device_grabber_) {
      return;
    }

    device_grabber_ = std::make_unique<device_grabber>(console_user_server_client_);

    device_grabber_->async_set_system_preferences(system_preferences_);
    device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                    frontmost_application_file_path_);
    device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);

    device_grabber_->async_start(configuration_file_path);

    logger::get_logger().info("device_grabber is started.");
  }

  void stop_device_grabber(void) {
    if (!device_grabber_) {
      return;
    }

    device_grabber_ = nullptr;

    logger::get_logger().info("device_grabber is stopped.");
  }

  std::unique_ptr<thread_utility::dispatcher> dispatcher_;

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  std::shared_ptr<console_user_server_client> console_user_server_client_;
  std::unique_ptr<device_grabber> device_grabber_;

  system_preferences system_preferences_;
  std::string frontmost_application_bundle_identifier_;
  std::string frontmost_application_file_path_;
  input_source_identifiers input_source_identifiers_;
};
} // namespace krbn
<commit_msg>use dispatcher::dispatcher @ grabber/receiver<commit_after>#pragma once

// `krbn::receiver` can be used safely in a multi-threaded environment.

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "device_grabber.hpp"
#include "dispatcher.hpp"
#include "grabbable_state_queues_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "session.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final : public dispatcher::dispatcher_client {
public:
  receiver(const receiver&) = delete;

  receiver(std::weak_ptr<dispatcher::dispatcher> weak_dispatcher) : dispatcher_client(weak_dispatcher) {
    std::string socket_file_path(constants::get_grabber_socket_file_path());

    unlink(socket_file_path.c_str());

    size_t buffer_size = 32 * 1024;
    std::chrono::milliseconds server_check_interval(3000);
    std::chrono::milliseconds reconnect_interval(1000);

    server_manager_ = std::make_unique<local_datagram::server_manager>(weak_dispatcher_,
                                                                       socket_file_path,
                                                                       buffer_size,
                                                                       server_check_interval,
                                                                       reconnect_interval);

    server_manager_->bound.connect([this, socket_file_path] {
      enqueue_to_dispatcher([socket_file_path] {
        if (auto uid = session::get_current_console_user_id()) {
          chown(socket_file_path.c_str(), *uid, 0);
        }
        chmod(socket_file_path.c_str(), 0600);

        grabbable_state_queues_manager::get_shared_instance()->clear();
      });
    });

    server_manager_->bind_failed.connect([this](auto&& error_code) {
      enqueue_to_dispatcher([] {
        logger::get_logger().error("receiver bind_failed");
      });
    });

    server_manager_->received.connect([this](auto&& buffer) {
      enqueue_to_dispatcher([this, buffer] {
        if (auto type = types::find_operation_type(*buffer)) {
          switch (*type) {
            case operation_type::grabbable_state_changed:
              if (buffer->size() != sizeof(operation_type_grabbable_state_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::grabbable_state_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_grabbable_state_changed_struct*>(&((*buffer)[0]));

                grabbable_state_queues_manager::get_shared_instance()->update_grabbable_state(p->grabbable_state);
              }
              break;

            case operation_type::connect_console_user_server:
              if (buffer->size() != sizeof(operation_type_connect_console_user_server_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::connect_console_user_server`.");
              } else {
                auto p = reinterpret_cast<operation_type_connect_console_user_server_struct*>(&((*buffer)[0]));

                // Ensure user_core_configuration_file_path is null-terminated string even if corrupted data is sent.
                p->user_core_configuration_file_path[sizeof(p->user_core_configuration_file_path) - 1] = '\0';
                std::string user_core_configuration_file_path(p->user_core_configuration_file_path);

                logger::get_logger().info("karabiner_console_user_server is connected (pid:{0})", p->pid);

                console_user_server_client_ = nullptr;
                console_user_server_client_ = std::make_shared<console_user_server_client>(weak_dispatcher_);

                console_user_server_client_->connected.connect([this, user_core_configuration_file_path] {
                  enqueue_to_dispatcher([this, user_core_configuration_file_path] {
                    stop_device_grabber();
                    start_device_grabber(user_core_configuration_file_path);
                  });
                });

                console_user_server_client_->connect_failed.connect([this](auto&& error_code) {
                  enqueue_to_dispatcher([this] {
                    console_user_server_client_ = nullptr;

                    stop_device_grabber();
                    start_grabbing_if_system_core_configuration_file_exists();
                  });
                });

                console_user_server_client_->closed.connect([this] {
                  enqueue_to_dispatcher([this] {
                    console_user_server_client_ = nullptr;

                    stop_device_grabber();
                    start_grabbing_if_system_core_configuration_file_exists();
                  });
                });

                console_user_server_client_->async_start();
              }
              break;

            case operation_type::system_preferences_updated:
              if (buffer->size() < sizeof(operation_type_system_preferences_updated_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::system_preferences_updated`.");
              } else {
                auto p = reinterpret_cast<operation_type_system_preferences_updated_struct*>(&((*buffer)[0]));

                system_preferences_ = p->system_preferences;

                if (device_grabber_) {
                  device_grabber_->async_set_system_preferences(p->system_preferences);
                }

                logger::get_logger().info("`system_preferences` is updated.");
              }
              break;

            case operation_type::frontmost_application_changed:
              if (buffer->size() < sizeof(operation_type_frontmost_application_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::frontmost_application_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_frontmost_application_changed_struct*>(&((*buffer)[0]));

                // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
                p->bundle_identifier[sizeof(p->bundle_identifier) - 1] = '\0';
                p->file_path[sizeof(p->file_path) - 1] = '\0';

                frontmost_application_bundle_identifier_ = p->bundle_identifier;
                frontmost_application_file_path_ = p->file_path;

                if (device_grabber_) {
                  device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                                frontmost_application_file_path_);
                }
              }
              break;

            case operation_type::input_source_changed:
              if (buffer->size() < sizeof(operation_type_input_source_changed_struct)) {
                logger::get_logger().error("Invalid size for `operation_type::input_source_changed`.");
              } else {
                auto p = reinterpret_cast<operation_type_input_source_changed_struct*>(&((*buffer)[0]));

                // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
                p->language[sizeof(p->language) - 1] = '\0';
                p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
                p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

                input_source_identifiers_ = input_source_identifiers(std::string(p->language),
                                                                     std::string(p->input_source_id),
                                                                     std::string(p->input_mode_id));

                if (device_grabber_) {
                  device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);
                }
              }
              break;

            default:
              break;
          }
        }
      });
    });

    server_manager_->async_start();

    start_grabbing_if_system_core_configuration_file_exists();

    logger::get_logger().info("receiver is initialized");
  }

  ~receiver(void) {
    detach_from_dispatcher([this] {
      server_manager_ = nullptr;
      console_user_server_client_ = nullptr;
      stop_device_grabber();
    });

    logger::get_logger().info("receiver is terminated");
  }

private:
  void start_grabbing_if_system_core_configuration_file_exists(void) {
    auto file_path = constants::get_system_core_configuration_file_path();
    if (filesystem::exists(file_path)) {
      stop_device_grabber();
      start_device_grabber(file_path);
    }
  }

  void start_device_grabber(const std::string& configuration_file_path) {
    if (device_grabber_) {
      return;
    }

    device_grabber_ = std::make_unique<device_grabber>(console_user_server_client_);

    device_grabber_->async_set_system_preferences(system_preferences_);
    device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                    frontmost_application_file_path_);
    device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);

    device_grabber_->async_start(configuration_file_path);

    logger::get_logger().info("device_grabber is started.");
  }

  void stop_device_grabber(void) {
    if (!device_grabber_) {
      return;
    }

    device_grabber_ = nullptr;

    logger::get_logger().info("device_grabber is stopped.");
  }

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  std::shared_ptr<console_user_server_client> console_user_server_client_;
  std::unique_ptr<device_grabber> device_grabber_;

  system_preferences system_preferences_;
  std::string frontmost_application_bundle_identifier_;
  std::string frontmost_application_file_path_;
  input_source_identifiers input_source_identifiers_;
};
} // namespace krbn
<|endoftext|>


<commit_before>#include "core/Genome.hpp"
#include "core/FakeGenome.hpp"
#include "core/Locus.hpp"
#include "loci/PopulationLocus.hpp"
#include "loci/FakePopulationLocus.hpp"
#include "exception/ValueOutOfRangeException.hpp"
#include "exception/ComponentNotPresentException.hpp"
#include <cmath>
#include <sstream>

using namespace std;

Genome::Genome(vector<Locus*> loci, std::string speciesNode) {
	this->loci = loci;
	this->speciesNode = speciesNode;
	generateRandomGenes();
}

Genome::Genome(
	vector<unsigned int> genes,
	vector<Locus*> loci,
	std::string speciesNode
) {
	this->genes = genes;
	this->loci = loci;
	this->speciesNode = speciesNode;
}

Genome::Genome(
	std::tuple<
		std::vector<unsigned int>,
		std::vector<Locus*>
	> genes,
	std::string speciesNode
) {
	std::tie(this->genes, this->loci) = genes;
	this->speciesNode = speciesNode;
}

Genome::Genome(Genome * other) {
	this->loci = other->getLoci();
	this->genes = other->getGenome();
	this->speciesNode = other->getSpeciesNode();
}

Genome::Genome(Genome * other, bool randomize) {
	this->loci = other->getLoci();
	this->speciesNode = other->getSpeciesNode();
	if (randomize) {
		this->generateRandomGenes();
	} else {
		this->genes = other->getGenome();
	}
}

Genome::Genome(Genome * other, unsigned int rawGenes[]) {
	this->loci = other->getLoci();
	this->speciesNode = other->getSpeciesNode();
	vector<unsigned int> genes(rawGenes, rawGenes + loci.size());
	for (unsigned int i = 0; i < genes.size(); i++)
		if (this->loci[i]->outOfRange(genes[i]))
			throw ValueOutOfRangeException();
	this->genes = genes;
}

Genome::~Genome() {}

void Genome::generateRandomGenes() {
	genes.clear();
	for (unsigned int i = 0; i < loci.size(); i++)
		genes.push_back(loci[i]->randomIndex());
}

vector<unsigned int> Genome::getGenome() {
	return this->genes;
}

unsigned int Genome::genomeLength() {
	return this->genes.size();
}

vector<Locus*> Genome::getLoci() {
	return loci;
}

std::string Genome::getSpeciesNode() {
	return this->speciesNode;
}

int Genome::difference(Genome * otherGenome) {
	vector<unsigned int> otherGenes = otherGenome->getGenome();
	int difference = 0;
	unsigned int shorterGenome = fmin(
		this->genes.size(),
		otherGenes.size()
	);
	unsigned int longerGenome = fmax(
		this->genes.size(),
		otherGenes.size()
	);

	for (unsigned int i = 0; i < shorterGenome; i++)
		difference += abs(this->genes[i] - otherGenes[i]);

	// We want to account for genes of different lengths somehow
	if (longerGenome != shorterGenome)
		difference += longerGenome - shorterGenome;

	return difference;
}

string Genome::flatten() {
	stringstream ss;

	for (unsigned int i = 0; i < this->genes.size(); i++)
		ss << this->loci[i]->flatten(this->genes[i]) << " ";

	return ss.str();
}

template <>
Genome * Genome::getIndex<Genome*>(unsigned int index) {
	return ((PopulationLocus*)this->loci[index])
		->getIndex(this->genes[index]);
}

Genome Genome::flattenGenome(Genome * target, bool exclude) {
	vector<unsigned int> rawGenome;
	vector<Locus*> rawLoci;

	for (unsigned int i = 0; i < this->genomeLength(); i++) {
		Locus * tempLocus = this->loci[i];
		if (!tempLocus->isConstructive()) {
			rawGenome.push_back(this->genes[i]);
			rawLoci.push_back(this->loci[i]);
		} else {
			Genome * temp = ((PopulationLocus*)tempLocus)->getIndex(
				this->genes[i]
			);

			if (temp == target) {
				if (!exclude) {
					rawGenome.push_back(this->genes[i]);
					rawLoci.push_back(this->loci[i]);
				}
			} else {
				Genome tempFlattened = temp->flattenGenome();
				vector<unsigned int> tempGenome =
					tempFlattened.getGenome();
				vector<Locus*> tempLoci =
					tempFlattened.getLoci();

				for (
					unsigned int k = 0;
					k < tempGenome.size();
					k++
				) {
					rawGenome.push_back(tempGenome[k]);
					rawLoci.push_back(tempLoci[k]);
				}
			}
		}
	}

	return Genome(rawGenome, rawLoci, this->speciesNode);
}

Genome Genome::flattenGenome() {
	return this->flattenGenome(NULL, false);
}

Genome Genome::flattenExceptFor(Genome * target) {
	return this->flattenGenome(target, false);
}

Genome Genome::flattenWithout(Genome * target) {
	return this->flattenGenome(target, true);
}

Genome * Genome::replaceComponent(Genome * target) {
	std::vector<unsigned int> newGenes;
	std::vector<Locus*> newLoci;
	for (unsigned int i = 0; i < this->loci.size(); i++) {
		if (this->loci[i]->isConstructive()) {
			PopulationLocus * temp =
				(PopulationLocus*)this->loci[i];

			Genome * locusTarget = (temp->usesSpecies(target) ?
				target : this->getIndex<Genome*>(i));
			newLoci.push_back(new FakePopulationLocus(
				locusTarget->replaceComponent(target),
				temp
			));
		} else {
			newLoci.push_back(this->loci[i]);
		}
		newGenes.push_back(this->genes[i]);
	}
	return new FakeGenome(newGenes, newLoci, this->speciesNode);
}

std::vector<unsigned int> Genome::getFlattenedIndices(Genome * target) {
	std::vector<unsigned int> indices;
	unsigned int currentIndex = 0;

	for (unsigned int i = 0; i < this->genes.size(); i++) {
		if (this->loci[i]->isConstructive()) {
			Genome * temp = ((PopulationLocus*)this->loci[i])
				->getIndex(this->genes[i]);
			if (temp == target) {
				indices.push_back(i);
				currentIndex += temp->flattenedGenomeLength();
			} else {
				std::vector<unsigned int> componentIndices =
					temp->getFlattenedIndices(target);

				for (
					unsigned int k = 0;
					k < componentIndices.size();
					k++
				) {
					indices.push_back(
						componentIndices[k]
							+ currentIndex
					);
				}
				currentIndex += temp->flattenedGenomeLength();
			}
		} else {
			currentIndex++;
		}
	}

	return indices;
}

unsigned int Genome::flattenedGenomeLength() {
	return this->flattenGenome().genomeLength();
}

bool Genome::usesComponent(Genome * component) {
	for (unsigned int i = 0; i < this->loci.size(); i++) {
		Locus * locus = this->loci[i];
		if (locus->isConstructive()) {
			Genome * temp = ((PopulationLocus*)locus)->getIndex(
				this->genes[i]
			);

			if (temp == component || temp->usesComponent(component))
				return true;
		}
	}

	return false;
}

set<Locus*> Genome::getConstructiveLoci() {
	set<Locus*> constructiveLoci;
	for (unsigned int i = 0; i < this->loci.size(); i++)
		if (this->loci[i]->isConstructive())
			constructiveLoci.insert(this->loci[i]);

	return constructiveLoci;
}
<commit_msg>[Genome]: Fixed memory leak in replacement code<commit_after>#include "core/Genome.hpp"
#include "core/FakeGenome.hpp"
#include "core/Locus.hpp"
#include "loci/PopulationLocus.hpp"
#include "loci/FakePopulationLocus.hpp"
#include "exception/ValueOutOfRangeException.hpp"
#include "exception/ComponentNotPresentException.hpp"
#include <cmath>
#include <sstream>

using namespace std;

Genome::Genome(vector<Locus*> loci, std::string speciesNode) {
	this->loci = loci;
	this->speciesNode = speciesNode;
	generateRandomGenes();
}

Genome::Genome(
	vector<unsigned int> genes,
	vector<Locus*> loci,
	std::string speciesNode
) {
	this->genes = genes;
	this->loci = loci;
	this->speciesNode = speciesNode;
}

Genome::Genome(
	std::tuple<
		std::vector<unsigned int>,
		std::vector<Locus*>
	> genes,
	std::string speciesNode
) {
	std::tie(this->genes, this->loci) = genes;
	this->speciesNode = speciesNode;
}

Genome::Genome(Genome * other) {
	this->loci = other->getLoci();
	this->genes = other->getGenome();
	this->speciesNode = other->getSpeciesNode();
}

Genome::Genome(Genome * other, bool randomize) {
	this->loci = other->getLoci();
	this->speciesNode = other->getSpeciesNode();
	if (randomize) {
		this->generateRandomGenes();
	} else {
		this->genes = other->getGenome();
	}
}

Genome::Genome(Genome * other, unsigned int rawGenes[]) {
	this->loci = other->getLoci();
	this->speciesNode = other->getSpeciesNode();
	vector<unsigned int> genes(rawGenes, rawGenes + loci.size());
	for (unsigned int i = 0; i < genes.size(); i++)
		if (this->loci[i]->outOfRange(genes[i]))
			throw ValueOutOfRangeException();
	this->genes = genes;
}

Genome::~Genome() {}

void Genome::generateRandomGenes() {
	genes.clear();
	for (unsigned int i = 0; i < loci.size(); i++)
		genes.push_back(loci[i]->randomIndex());
}

vector<unsigned int> Genome::getGenome() {
	return this->genes;
}

unsigned int Genome::genomeLength() {
	return this->genes.size();
}

vector<Locus*> Genome::getLoci() {
	return loci;
}

std::string Genome::getSpeciesNode() {
	return this->speciesNode;
}

int Genome::difference(Genome * otherGenome) {
	vector<unsigned int> otherGenes = otherGenome->getGenome();
	int difference = 0;
	unsigned int shorterGenome = fmin(
		this->genes.size(),
		otherGenes.size()
	);
	unsigned int longerGenome = fmax(
		this->genes.size(),
		otherGenes.size()
	);

	for (unsigned int i = 0; i < shorterGenome; i++)
		difference += abs(this->genes[i] - otherGenes[i]);

	// We want to account for genes of different lengths somehow
	if (longerGenome != shorterGenome)
		difference += longerGenome - shorterGenome;

	return difference;
}

string Genome::flatten() {
	stringstream ss;

	for (unsigned int i = 0; i < this->genes.size(); i++)
		ss << this->loci[i]->flatten(this->genes[i]) << " ";

	return ss.str();
}

template <>
Genome * Genome::getIndex<Genome*>(unsigned int index) {
	return ((PopulationLocus*)this->loci[index])
		->getIndex(this->genes[index]);
}

Genome Genome::flattenGenome(Genome * target, bool exclude) {
	vector<unsigned int> rawGenome;
	vector<Locus*> rawLoci;

	for (unsigned int i = 0; i < this->genomeLength(); i++) {
		Locus * tempLocus = this->loci[i];
		if (!tempLocus->isConstructive()) {
			rawGenome.push_back(this->genes[i]);
			rawLoci.push_back(this->loci[i]);
		} else {
			Genome * temp = ((PopulationLocus*)tempLocus)->getIndex(
				this->genes[i]
			);

			if (temp == target) {
				if (!exclude) {
					rawGenome.push_back(this->genes[i]);
					rawLoci.push_back(this->loci[i]);
				}
			} else {
				Genome tempFlattened = temp->flattenGenome();
				vector<unsigned int> tempGenome =
					tempFlattened.getGenome();
				vector<Locus*> tempLoci =
					tempFlattened.getLoci();

				for (
					unsigned int k = 0;
					k < tempGenome.size();
					k++
				) {
					rawGenome.push_back(tempGenome[k]);
					rawLoci.push_back(tempLoci[k]);
				}
			}
		}
	}

	return Genome(rawGenome, rawLoci, this->speciesNode);
}

Genome Genome::flattenGenome() {
	return this->flattenGenome(NULL, false);
}

Genome Genome::flattenExceptFor(Genome * target) {
	return this->flattenGenome(target, false);
}

Genome Genome::flattenWithout(Genome * target) {
	return this->flattenGenome(target, true);
}

Genome * Genome::replaceComponent(Genome * target) {
	std::vector<unsigned int> newGenes;
	std::vector<Locus*> newLoci;
	for (unsigned int i = 0; i < this->loci.size(); i++) {
		if (this->loci[i]->isConstructive()) {
			PopulationLocus * temp =
				(PopulationLocus*)this->loci[i];

			Genome * replaced = ((temp->usesSpecies(target) ?
				target : this->getIndex<Genome*>(i)))
				->replaceComponent(target);
			newLoci.push_back(new FakePopulationLocus(
				replaced,
				temp
			));
			delete(replaced);
		} else {
			newLoci.push_back(this->loci[i]);
		}
		newGenes.push_back(this->genes[i]);
	}
	return new FakeGenome(newGenes, newLoci, this->speciesNode);
}

std::vector<unsigned int> Genome::getFlattenedIndices(Genome * target) {
	std::vector<unsigned int> indices;
	unsigned int currentIndex = 0;

	for (unsigned int i = 0; i < this->genes.size(); i++) {
		if (this->loci[i]->isConstructive()) {
			Genome * temp = ((PopulationLocus*)this->loci[i])
				->getIndex(this->genes[i]);
			if (temp == target) {
				indices.push_back(i);
				currentIndex += temp->flattenedGenomeLength();
			} else {
				std::vector<unsigned int> componentIndices =
					temp->getFlattenedIndices(target);

				for (
					unsigned int k = 0;
					k < componentIndices.size();
					k++
				) {
					indices.push_back(
						componentIndices[k]
							+ currentIndex
					);
				}
				currentIndex += temp->flattenedGenomeLength();
			}
		} else {
			currentIndex++;
		}
	}

	return indices;
}

unsigned int Genome::flattenedGenomeLength() {
	return this->flattenGenome().genomeLength();
}

bool Genome::usesComponent(Genome * component) {
	for (unsigned int i = 0; i < this->loci.size(); i++) {
		Locus * locus = this->loci[i];
		if (locus->isConstructive()) {
			Genome * temp = ((PopulationLocus*)locus)->getIndex(
				this->genes[i]
			);

			if (
				temp == component
				|| temp->usesComponent(component)
			) return true;
		}
	}

	return false;
}

set<Locus*> Genome::getConstructiveLoci() {
	set<Locus*> constructiveLoci;
	for (unsigned int i = 0; i < this->loci.size(); i++)
		if (this->loci[i]->isConstructive())
			constructiveLoci.insert(this->loci[i]);

	return constructiveLoci;
}
<|endoftext|>


<commit_before>#include "../../src/gameboy_prelude.h"
#include "../cli/cli.h"

#include <SDL.h>

#include <fstream>
#include <iterator>

static uint pixel_size = 2;

static uint width = GAMEBOY_WIDTH * pixel_size;
static uint height = GAMEBOY_HEIGHT * pixel_size;

static SDL_Window* window;
static SDL_Renderer* renderer;
static SDL_Texture* gb_screen_texture;

static std::unique_ptr<Gameboy> gameboy;

static CliOptions cliOptions;

static bool should_exit = false;

static std::unique_ptr<GbButton> get_gb_button(int keyCode) {
    switch (keyCode) {
        case SDLK_UP: return std::make_unique<GbButton>(GbButton::Up);
        case SDLK_DOWN: return std::make_unique<GbButton>(GbButton::Down);
        case SDLK_LEFT: return std::make_unique<GbButton>(GbButton::Left);
        case SDLK_RIGHT: return std::make_unique<GbButton>(GbButton::Right);
        case SDLK_x: return std::make_unique<GbButton>(GbButton::A);
        case SDLK_z: return std::make_unique<GbButton>(GbButton::B);
        case SDLK_BACKSPACE: return std::make_unique<GbButton>(GbButton::Select);
        case SDLK_RETURN: return std::make_unique<GbButton>(GbButton::Start);
        case SDLK_b: gameboy->debug_toggle_background(); return nullptr;
        case SDLK_s: gameboy->debug_toggle_sprites(); return nullptr;
        case SDLK_w: gameboy->debug_toggle_window(); return nullptr;
        default: return nullptr;
    }
}

static uint32_t get_real_color(Color color) {
    uint8_t r;
    uint8_t g;
    uint8_t b;

    switch (color) {
        case Color::White: r = g = b = 255; break;
        case Color::LightGray: r = g = b = 170; break;
        case Color::DarkGray: r = g = b = 85; break;
        case Color::Black: r = g = b = 0; break;
    }

    return (r << 16) | (g << 8) | (b << 0);
}

static void set_pixel(uint32_t* pixels, uint x, uint y, uint32_t pixel_argb) {
    pixels[width * y + x] = pixel_argb;
}

static void set_large_pixel(uint32_t* pixels, uint x, uint y, uint32_t pixel_argb) {
    for (uint w = 0; w < pixel_size; w++) {
        for (uint h = 0; h < pixel_size; h++) {
            set_pixel(pixels, x * pixel_size + w, y * pixel_size + h, pixel_argb);
        }
    }
}

static void set_pixels(uint32_t* pixels, const FrameBuffer& buffer) {
    for (uint y = 0; y < GAMEBOY_HEIGHT; y++) {
        for (uint x = 0; x < GAMEBOY_WIDTH; x++) {
            Color color = buffer.get_pixel(x, y);
            uint32_t pixel_argb = get_real_color(color);
            set_large_pixel(pixels, x, y, pixel_argb);
        }
    }
}

static std::string get_save_filename() {
    return cliOptions.filename + ".sav";
}

static bool file_exists(const std::string& filename) {
    std::ifstream f(filename.c_str());
    return f.good();
}

static void save_state() {
    auto cartridge_ram = gameboy->get_cartridge_ram();

    // Don't save empty cartridge RAM
    if (cartridge_ram.size() == 0) { return; }

    auto filename = get_save_filename();
    std::ofstream output_file(filename);
    std::copy(cartridge_ram.begin(), cartridge_ram.end(), std::ostreambuf_iterator<char>(output_file));
    log_info("Wrote %d KB to %s", cartridge_ram.size() / 1024, filename.c_str());
}

static std::vector<u8> load_state() {
    auto filename = get_save_filename();
    if (!file_exists(filename)) {
        return {};
    } else {
        auto save_data = read_bytes(filename);
        log_info("Read %d KB from %s", save_data.size() / 1024, filename.c_str());
        return save_data;
    }
}

static void process_events() {
    SDL_Event event;

    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_KEYDOWN:
                if (event.key.repeat == true) { break; }
                if (auto button_pressed = get_gb_button(event.key.keysym.sym); button_pressed != nullptr) {
                    gameboy->button_pressed(*button_pressed);
                }
                break;
            case SDL_KEYUP:
                if (event.key.repeat == true) { break; }
                if (auto button_released = get_gb_button(event.key.keysym.sym); button_released != nullptr) {
                    gameboy->button_released(*button_released);
                }
                break;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
                    should_exit = true;
                }
                break;
            case SDL_QUIT:
                should_exit = true;
                break;
        }
    }
}

static void draw(const FrameBuffer& buffer) {
    process_events();

    SDL_RenderClear(renderer);

    void* pixels_ptr;
    int pitch;
    SDL_LockTexture(gb_screen_texture, nullptr, &pixels_ptr, &pitch);

    uint32_t* pixels = static_cast<uint32_t*>(pixels_ptr);
    set_pixels(pixels, buffer);
    SDL_UnlockTexture(gb_screen_texture);

    SDL_RenderCopy(renderer, gb_screen_texture, nullptr, nullptr);
    SDL_RenderPresent(renderer);
}

static bool is_closed() {
    return should_exit;
}

int main(int argc, char* argv[]) {
    cliOptions = get_cli_options(argc, argv);

    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow(
        "gbemu",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        width,
        height,
        SDL_WINDOW_OPENGL
    );

    if (window == nullptr) {
        fatal_error("Failed to initialise SDL");
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    gb_screen_texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_ARGB8888,
        SDL_TEXTUREACCESS_STREAMING,
        width, height
    );

    auto rom_data = read_bytes(cliOptions.filename);
    log_info("Read %d KB from %s", rom_data.size() / 1024, cliOptions.filename.c_str());

    auto save_data = load_state();
    log_info("");

    gameboy = std::make_unique<Gameboy>(rom_data, cliOptions.options, save_data);
    gameboy->run(&is_closed, &draw);

    save_state();
    SDL_DestroyTexture(gb_screen_texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
<commit_msg>Use std::optional in SDL platform implementation<commit_after>#include "../../src/gameboy_prelude.h"
#include "../cli/cli.h"

#include <SDL.h>

#include <fstream>
#include <iterator>
#include <optional>

static uint pixel_size = 2;

static uint width = GAMEBOY_WIDTH * pixel_size;
static uint height = GAMEBOY_HEIGHT * pixel_size;

static SDL_Window* window;
static SDL_Renderer* renderer;
static SDL_Texture* gb_screen_texture;

static std::unique_ptr<Gameboy> gameboy;

static CliOptions cliOptions;

static bool should_exit = false;

static std::optional<GbButton> get_gb_button(int keyCode) {
    switch (keyCode) {
        case SDLK_UP: return GbButton::Up;
        case SDLK_DOWN: return GbButton::Down;
        case SDLK_LEFT: return GbButton::Left;
        case SDLK_RIGHT: return GbButton::Right;
        case SDLK_x: return GbButton::A;
        case SDLK_z: return GbButton::B;
        case SDLK_BACKSPACE: return GbButton::Select;
        case SDLK_RETURN: return GbButton::Start;
        case SDLK_b: gameboy->debug_toggle_background(); return {};
        case SDLK_s: gameboy->debug_toggle_sprites(); return {};
        case SDLK_w: gameboy->debug_toggle_window(); return {};
        default: return {};
    }
}

static uint32_t get_real_color(Color color) {
    uint8_t r;
    uint8_t g;
    uint8_t b;

    switch (color) {
        case Color::White: r = g = b = 255; break;
        case Color::LightGray: r = g = b = 170; break;
        case Color::DarkGray: r = g = b = 85; break;
        case Color::Black: r = g = b = 0; break;
    }

    return (r << 16) | (g << 8) | (b << 0);
}

static void set_pixel(uint32_t* pixels, uint x, uint y, uint32_t pixel_argb) {
    pixels[width * y + x] = pixel_argb;
}

static void set_large_pixel(uint32_t* pixels, uint x, uint y, uint32_t pixel_argb) {
    for (uint w = 0; w < pixel_size; w++) {
        for (uint h = 0; h < pixel_size; h++) {
            set_pixel(pixels, x * pixel_size + w, y * pixel_size + h, pixel_argb);
        }
    }
}

static void set_pixels(uint32_t* pixels, const FrameBuffer& buffer) {
    for (uint y = 0; y < GAMEBOY_HEIGHT; y++) {
        for (uint x = 0; x < GAMEBOY_WIDTH; x++) {
            Color color = buffer.get_pixel(x, y);
            uint32_t pixel_argb = get_real_color(color);
            set_large_pixel(pixels, x, y, pixel_argb);
        }
    }
}

static std::string get_save_filename() {
    return cliOptions.filename + ".sav";
}

static bool file_exists(const std::string& filename) {
    std::ifstream f(filename.c_str());
    return f.good();
}

static void save_state() {
    auto cartridge_ram = gameboy->get_cartridge_ram();

    // Don't save empty cartridge RAM
    if (cartridge_ram.size() == 0) { return; }

    auto filename = get_save_filename();
    std::ofstream output_file(filename);
    std::copy(cartridge_ram.begin(), cartridge_ram.end(), std::ostreambuf_iterator<char>(output_file));
    log_info("Wrote %d KB to %s", cartridge_ram.size() / 1024, filename.c_str());
}

static std::vector<u8> load_state() {
    auto filename = get_save_filename();
    if (!file_exists(filename)) {
        return {};
    } else {
        auto save_data = read_bytes(filename);
        log_info("Read %d KB from %s", save_data.size() / 1024, filename.c_str());
        return save_data;
    }
}

static void process_events() {
    SDL_Event event;

    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_KEYDOWN:
                if (event.key.repeat == true) { break; }
                if (auto button_pressed = get_gb_button(event.key.keysym.sym); button_pressed) {
                    gameboy->button_pressed(*button_pressed);
                }
                break;
            case SDL_KEYUP:
                if (event.key.repeat == true) { break; }
                if (auto button_released = get_gb_button(event.key.keysym.sym); button_released) {
                    gameboy->button_released(*button_released);
                }
                break;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
                    should_exit = true;
                }
                break;
            case SDL_QUIT:
                should_exit = true;
                break;
        }
    }
}

static void draw(const FrameBuffer& buffer) {
    process_events();

    SDL_RenderClear(renderer);

    void* pixels_ptr;
    int pitch;
    SDL_LockTexture(gb_screen_texture, nullptr, &pixels_ptr, &pitch);

    uint32_t* pixels = static_cast<uint32_t*>(pixels_ptr);
    set_pixels(pixels, buffer);
    SDL_UnlockTexture(gb_screen_texture);

    SDL_RenderCopy(renderer, gb_screen_texture, nullptr, nullptr);
    SDL_RenderPresent(renderer);
}

static bool is_closed() {
    return should_exit;
}

int main(int argc, char* argv[]) {
    cliOptions = get_cli_options(argc, argv);

    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow(
        "gbemu",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        width,
        height,
        SDL_WINDOW_OPENGL
    );

    if (window == nullptr) {
        fatal_error("Failed to initialise SDL");
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    gb_screen_texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_ARGB8888,
        SDL_TEXTUREACCESS_STREAMING,
        width, height
    );

    auto rom_data = read_bytes(cliOptions.filename);
    log_info("Read %d KB from %s", rom_data.size() / 1024, cliOptions.filename.c_str());

    auto save_data = load_state();
    log_info("");

    gameboy = std::make_unique<Gameboy>(rom_data, cliOptions.options, save_data);
    gameboy->run(&is_closed, &draw);

    save_state();
    SDL_DestroyTexture(gb_screen_texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2015, Stuart W Baker
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file select.h
 * This file imlements POSIX select().
 *
 * @author Stuart W. Baker
 * @date 26 January 2015
 */

#include <fcntl.h>
#include <sys/select.h>

#include "Devtab.hxx"

extern File *fd_find(int);

/** event used to wakeup select calls */
static OSEvent wakeup;

/** Returns an event bit unique to the current thread.
 * @return event bit unique to the current thread
 */
static OSEventType get_event()
{
    static int thread_count = 0;
    portENTER_CRITICAL();
    if(thread_count >= OSEvent::number_of_bits())
    {
        thread_count = 0;
    }
    ThreadPriv *priv = (ThreadPriv*)xTaskGetApplicationTaskTag(NULL);
    if (priv->selectEventBit == 0)
    {
        priv->selectEventBit = 0x1 << thread_count;
        ++thread_count;
    }
    portEXIT_CRITICAL();

    return priv->selectEventBit;
}

/** POSIX select().
 * @param nfds highest numbered file descriptor in any of the three, sets plus 1
 * @param readfds fd_set of file descritpors to pend on read active
 * @param writefds fd_set of file descritpors to pend on write active
 * @param exceptfds fd_set of file descritpors to pend on error active
 * @param timeout timeout value to wait, if 0, return immediately, if NULL
 *                wait forever
 * @return on success, number of file descriptors in the three sets that are
           active, 0 on timeout, -1 with errno set appropriately upon error.
 */
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout)
{
    long long until = 0;
    if (timeout)
    {
        until = OSTime::get_monotonic() +
                ((long long)timeout->tv_sec * 1000LL * 1000LL *1000LL) +
                ((long long)timeout->tv_usec * 1000LL);
    }
    fd_set rd_result;
    fd_set wr_result;
    fd_set ex_result;

    int mode_type[] = {FREAD, FWRITE, 0};
    fd_set *in_set[3] = {readfds, writefds, exceptfds};
    fd_set *out_set[3] = {&rd_result, &wr_result, &ex_result};
    int number = 0;

    FD_ZERO(&rd_result);
    FD_ZERO(&wr_result);
    FD_ZERO(&ex_result);

    OSEventType event = get_event();
    wakeup.clear(event);

    for ( ; /* forever */ ; )
    {
        /* cycle through all the FD Sets */
        for (int mode = 0; mode < 3; ++mode)
        {
            /* cycle through all the file descriptors */
            for (int i = 0; i < nfds; ++i)
            {
                if (FD_ISSET(i, in_set[mode]))
                {
                    File *file = fd_find(i);
                    if (!file)
                    {
                        errno = EBADF;
                        return -1;
                    }
                    if (file->dev->select(file, mode_type[mode]))
                    {
                        /* active */
                        FD_SET(i, out_set[mode]);
                        ++number;
                    }
                }
            }
        }

        if (number)
        {
            *readfds = rd_result;
            *writefds = wr_result;
            *exceptfds = ex_result;
            return number;
        }

        if (timeout)
        {
            long long now = OSTime::get_monotonic();
            if (now > until)
            {
                return 0;
            }
            wakeup.timedwait(event, NULL, true, OSEvent::WAIT_ANY, until - now);
        }
        else
        {
            wakeup.wait(event, NULL, true, OSEvent::WAIT_ANY);
        }
    }
}

/** Device select method. Default impementation returns true.
 * @param file reference to the file
 * @param mode FREAD for read active, FWRITE for write active, 0 for
 *        exceptions
 * @return true if active, false if inactive
 */
bool Device::select(File* file, int mode)
{
    return true;
}

/** Add client to list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_insert(SelectInfo *info)
{
    portENTER_CRITICAL();
    info->event |= get_event();
    portEXIT_CRITICAL();
}

/** Wakeup the list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_wakeup(SelectInfo *info)
{
    portENTER_CRITICAL();
    if (info->event != 0)
    {
        wakeup.set(info->event);
        info->event = 0;
    }
    portEXIT_CRITICAL();
}

/** Wakeup the list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_wakeup_from_isr(SelectInfo *info, int *woken)
{
    /** @todo do we need the critical section lock since we are already in
     * an ISR?
     */
    portENTER_CRITICAL();
    if (info->event != 0)
    {
        wakeup.set_from_isr(info->event, woken);
        info->event = 0;
    }
    portEXIT_CRITICAL();
}

<commit_msg>slight code efficiency improvement<commit_after>/** \copyright
 * Copyright (c) 2015, Stuart W Baker
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file select.h
 * This file imlements POSIX select().
 *
 * @author Stuart W. Baker
 * @date 26 January 2015
 */

#include <fcntl.h>
#include <sys/select.h>

#include "Devtab.hxx"

extern File *fd_find(int);

/** event used to wakeup select calls */
static OSEvent wakeup;

/** Returns an event bit unique to the current thread.
 * @return event bit unique to the current thread
 */
static OSEventType get_event()
{
    static int thread_count = 0;
    portENTER_CRITICAL();
    ThreadPriv *priv = (ThreadPriv*)xTaskGetApplicationTaskTag(NULL);
    if (priv->selectEventBit == 0)
    {
        if(thread_count >= OSEvent::number_of_bits())
        {
            thread_count = 0;
        }
        priv->selectEventBit = 0x1 << thread_count;
        ++thread_count;
    }
    portEXIT_CRITICAL();

    return priv->selectEventBit;
}

/** POSIX select().
 * @param nfds highest numbered file descriptor in any of the three, sets plus 1
 * @param readfds fd_set of file descritpors to pend on read active
 * @param writefds fd_set of file descritpors to pend on write active
 * @param exceptfds fd_set of file descritpors to pend on error active
 * @param timeout timeout value to wait, if 0, return immediately, if NULL
 *                wait forever
 * @return on success, number of file descriptors in the three sets that are
           active, 0 on timeout, -1 with errno set appropriately upon error.
 */
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout)
{
    long long until = 0;
    if (timeout)
    {
        until = OSTime::get_monotonic() +
                ((long long)timeout->tv_sec * 1000LL * 1000LL *1000LL) +
                ((long long)timeout->tv_usec * 1000LL);
    }
    fd_set rd_result;
    fd_set wr_result;
    fd_set ex_result;

    int mode_type[] = {FREAD, FWRITE, 0};
    fd_set *in_set[3] = {readfds, writefds, exceptfds};
    fd_set *out_set[3] = {&rd_result, &wr_result, &ex_result};
    int number = 0;

    FD_ZERO(&rd_result);
    FD_ZERO(&wr_result);
    FD_ZERO(&ex_result);

    OSEventType event = get_event();
    wakeup.clear(event);

    for ( ; /* forever */ ; )
    {
        /* cycle through all the FD Sets */
        for (int mode = 0; mode < 3; ++mode)
        {
            /* cycle through all the file descriptors */
            for (int i = 0; i < nfds; ++i)
            {
                if (FD_ISSET(i, in_set[mode]))
                {
                    File *file = fd_find(i);
                    if (!file)
                    {
                        errno = EBADF;
                        return -1;
                    }
                    if (file->dev->select(file, mode_type[mode]))
                    {
                        /* active */
                        FD_SET(i, out_set[mode]);
                        ++number;
                    }
                }
            }
        }

        if (number)
        {
            *readfds = rd_result;
            *writefds = wr_result;
            *exceptfds = ex_result;
            return number;
        }

        if (timeout)
        {
            long long now = OSTime::get_monotonic();
            if (now > until)
            {
                return 0;
            }
            wakeup.timedwait(event, NULL, true, OSEvent::WAIT_ANY, until - now);
        }
        else
        {
            wakeup.wait(event, NULL, true, OSEvent::WAIT_ANY);
        }
    }
}

/** Device select method. Default impementation returns true.
 * @param file reference to the file
 * @param mode FREAD for read active, FWRITE for write active, 0 for
 *        exceptions
 * @return true if active, false if inactive
 */
bool Device::select(File* file, int mode)
{
    return true;
}

/** Add client to list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_insert(SelectInfo *info)
{
    portENTER_CRITICAL();
    info->event |= get_event();
    portEXIT_CRITICAL();
}

/** Wakeup the list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_wakeup(SelectInfo *info)
{
    portENTER_CRITICAL();
    if (info->event != 0)
    {
        wakeup.set(info->event);
        info->event = 0;
    }
    portEXIT_CRITICAL();
}

/** Wakeup the list of clients needing woken,
 * @param info wakeup event instance
 */
void Device::select_wakeup_from_isr(SelectInfo *info, int *woken)
{
    /** @todo do we need the critical section lock since we are already in
     * an ISR?
     */
    portENTER_CRITICAL();
    if (info->event != 0)
    {
        wakeup.set_from_isr(info->event, woken);
        info->event = 0;
    }
    portEXIT_CRITICAL();
}

<|endoftext|>


<commit_before>#ifndef BUW_SCENE_HPP
#define BUW_SCENE_HPP
//#include <vector>
#include <map>
#include <shape.hpp>
#include <light.hpp>
#include <camera.hpp>

struct Scene{
	Scene() :
		ambient_light{ Color{0.0,0.0,0.0} },
		shapes{},
		lights{},
		materials{}
		camera{ nullptr }	{}
		/*shape_vec{},
		light_vec{},
		cam_vec{} {}*/

	~Scene() {
		for (auto element:shapes)
		{
			delete element;
		}

		for (auto element:lights)
		{
			delete element;
		}

		for (auto element:materials)
		{
			delete element;
		}
	}

	Color ambient_light;
	std::vector<Shape*> shapes;
	std::vector<Light*> lights;
	std::vector<Material*> materials;
	Camera* camera;
/*	std::vector<Shape> shape_vec;
	std::vector<Light> light_vec;
	std::vector<Camera> cam_vec;*/
};

#endif
<commit_msg>fixed the destructor<commit_after>#ifndef BUW_SCENE_HPP
#define BUW_SCENE_HPP
//#include <vector>
#include <map>
#include <shape.hpp>
#include <light.hpp>
#include <camera.hpp>

struct Scene{
	Scene() :
		ambient_light{ Color{0.0,0.0,0.0} },
		shapes{},
		lights{},
		materials{}
		camera{ nullptr }	{}
		/*shape_vec{},
		light_vec{},
		cam_vec{} {}*/

	~Scene() {
		for (auto element:shapes)
		{
			delete element;
		}

		for (auto element:lights)
		{
			delete element;
		}

		for (auto element:materials)
		{
			delete element;
		}
		delete camera;
	}

	Color ambient_light;
	std::vector<Shape*> shapes;
	std::vector<Light*> lights;
	std::vector<Material*> materials;
	Camera* camera;
/*	std::vector<Shape> shape_vec;
	std::vector<Light> light_vec;
	std::vector<Camera> cam_vec;*/
};

#endif
<|endoftext|>