<commit_before>#include <elf.hh>
#include <ptrace.hh>
#include <function.hh>
#include <disassembly.hh>
#include <utils.hh>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>
#include <map>
#include <string>

#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <link.h>

using namespace coincident;

class Function : public IFunction, IDisassembly::IInstructionListener
{
public:
	Function(const char *name, void *addr, size_t size,
			IFunction::FunctionType type)
	{
		m_refsValid = false;
		m_name = xstrdup(name);
		m_size = size;
		m_entry = addr;
		m_data = new uint8_t[m_size];
		m_type = type;
	}

	virtual ~Function()
	{
		delete m_name;
		delete m_data;
	}

	enum IFunction::FunctionType getType()
	{
		return m_type;
	}

	const char *getName()
	{
		return m_name;
	}

	size_t getSize()
	{
		return m_size;
	}

	void *getEntry()
	{
		return m_entry;
	}

	void setAddress(void *addr)
	{
		m_entry = addr;
	}

	void setSize(size_t size)
	{
		m_size = size;
	}

	void disassembleFunction()
	{
		bool res = IPtrace::getInstance().readMemory(m_data,
				m_entry, m_size);

		m_loadList.clear();
		m_storeList.clear();

		m_refsValid = true;
		if (!res) {
			error("Can't read memory at %p", m_entry);
			return;
		}

		IDisassembly::getInstance().execute(this, m_data, m_size);
	}

	ReferenceList_t &getMemoryLoads()
	{
		if (!m_refsValid)
			disassembleFunction();

		return m_loadList;
	}

	ReferenceList_t &getMemoryStores()
	{
		if (!m_refsValid)
			disassembleFunction();

		return m_storeList;
	}

	// These three functions are the IInstructionListerners
	void onMemoryReference(off_t offset, bool isLoad)
	{
		off_t addr = (off_t)m_entry + offset;

		if (isLoad)
			m_loadList.push_back((void *)addr);
		else
			m_storeList.push_back((void *)addr);
	}

	void onCall(off_t offset)
	{
	}

	void onBranch(off_t offset)
	{
	}

private:
	bool m_refsValid;
	const char *m_name;
	size_t m_size;
	void *m_entry;
	uint8_t *m_data;
	enum IFunction::FunctionType m_type;

	ReferenceList_t m_loadList;
	ReferenceList_t m_storeList;
};

class Elf : public IElf
{
public:
	Elf(const char *filename)
	{
		m_elf = NULL;
		m_listener = NULL;
		m_filename = strdup(filename);
	}

	~Elf()
	{
		free((void *)m_filename);
	}

	bool checkFile()
	{
		Elf *elf;
		bool out = true;
		int fd;

		fd = ::open(m_filename, O_RDONLY, 0);
		if (fd < 0) {
				error("Cannot open %s\n", m_filename);
				return false;
		}

		if (!(elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
				error("elf_begin failed on %s\n", m_filename);
				out = false;
				goto out_open;
		}
		if (!elf32_getehdr(elf)) {
				error("elf32_getehdr failed on %s\n", m_filename);
				out = false;
		}
		elf_end(elf);

out_open:
		close(fd);

		return out;
	}

	static int phdrCallback(struct dl_phdr_info *info, size_t size,
			void *data)
	{
		struct args
		{
			Elf *p;
			IFunctionListener *listener;
		};
		struct args *a = (struct args *)data;

		a->p->handlePhdr(a->listener, info, size);

		return 0;
	}

	void handlePhdr(IFunctionListener *listener,
			struct dl_phdr_info *info, size_t size)
	{
		int phdr;

		if (strlen(info->dlpi_name) != 0) {
			free( (void *)m_filename );
			m_filename = strdup(info->dlpi_name);
		}
		coin_debug(ELF_MSG, "ELF parsing %s\n", m_filename);

		m_curSegments.clear();
		for (phdr = 0; phdr < info->dlpi_phnum; phdr++) {
			const ElfW(Phdr) *cur = &info->dlpi_phdr[phdr];

			if (cur->p_type != PT_LOAD)
				continue;

			m_curSegments.push_back(Segment(cur->p_paddr, info->dlpi_addr + cur->p_vaddr,
					cur->p_memsz, cur->p_align));
		}
		parseOne(listener);
	}

	bool parse(IFunctionListener *listener)
	{
		struct
		{
			Elf *p;
			IFunctionListener *listener;
		} cbArgs;

		m_functionsByAddress.clear();
		m_functionsByName.clear();

		cbArgs.p = this;
		cbArgs.listener = listener;
		dl_iterate_phdr(phdrCallback, (void *)&cbArgs);

		return true;
	}

	bool parseOne(IFunctionListener *listener)
	{
		Elf_Scn *scn = NULL;
		Elf32_Ehdr *ehdr;
		size_t shstrndx;
		bool ret = false;
		int fd;

		m_listener = listener;

		fd = ::open(m_filename, O_RDONLY, 0);
		if (fd < 0) {
				error("Cannot open %s\n", m_filename);
				return false;
		}

		if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
				error("elf_begin failed on %s\n", m_filename);
				goto out_open;
		}

		if (!(ehdr = elf32_getehdr(m_elf))) {
				error("elf32_getehdr failed on %s\n", m_filename);
				goto out_elf_begin;
		}

		if (elf_getshdrstrndx(m_elf, &shstrndx) < 0) {
				error("elf_getshstrndx failed on %s\n", m_filename);
				goto out_elf_begin;
		}

		while ( (scn = elf_nextscn(m_elf, scn)) != NULL )
		{
			Elf32_Shdr *shdr = elf32_getshdr(scn);
			Elf_Data *data = elf_getdata(scn, NULL);
			char *name;

			name = elf_strptr(m_elf, shstrndx, shdr->sh_name);
			if(!data) {
					error("elf_getdata failed on section %s in %s\n",
							name, m_filename);
					goto out_elf_begin;
			}

			/* Handle symbols */
			if (shdr->sh_type == SHT_SYMTAB)
				handleSymtab(scn);
			if (shdr->sh_type == SHT_DYNSYM)
				handleDynsym(scn);
		}
		elf_end(m_elf);
		if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
			error("elf_begin failed on %s\n", m_filename);
			goto out_open;
		}
		while ( (scn = elf_nextscn(m_elf, scn)) != NULL )
		{
			Elf32_Shdr *shdr = elf32_getshdr(scn);
			char *name = elf_strptr(m_elf, shstrndx, shdr->sh_name);

			// .rel.plt
			if (shdr->sh_type == SHT_REL && strcmp(name, ".rel.plt") == 0)
				handleRelPlt(scn);
		}
		m_fixupFunctions.clear();
		for (FunctionsByAddress_t::iterator it = m_functionsByAddress.begin();
				it != m_functionsByAddress.end();
				it++) {
			Function *fn = it->second;

			m_listener->onFunction(*fn);
		}

		ret = true;

out_elf_begin:
		elf_end(m_elf);
out_open:
		close(fd);

		return ret;
	}

	IFunction *functionByAddress(void *addr)
	{
		return m_functionsByAddress[addr];
	}

	IElf::FunctionList_t functionByName(const char *name)
	{
		return m_functionsByName[std::string(name)];
	}

private:
	class Segment
	{
	public:
		Segment(ElfW(Addr) paddr, ElfW(Addr) vaddr, size_t size, ElfW(Word) align) :
			m_paddr(paddr), m_vaddr(vaddr), m_size(size), m_align(align)
		{
		}

		ElfW(Addr) m_paddr;
		ElfW(Addr) m_vaddr;
		ElfW(Word) m_align;
		size_t m_size;
	};

	typedef std::map<std::string, IElf::FunctionList_t> FunctionsByName_t;
	typedef std::map<void *, Function *> FunctionsByAddress_t;
	typedef std::map<int, Function *> FixupMap_t;
	typedef std::list<Segment> SegmentList_t;

	void *offsetTableToAddress(Elf32_Addr addr)
	{
		/*
		 * The .got.plt table contains a pointer to the push instruction
		 * below:
		 *
		 *  08070f10 <pthread_self@plt>:
		 *   8070f10:       ff 25 58 93 0b 08       jmp    *0x80b9358
		 *   8070f16:       68 b0 06 00 00          push   $0x6b0
		 *
		 * so to get the entry point, we rewind the pointer to the start
		 * of the jmp.
		 */
		return (void *)(addr - 6);
	}

	ElfW(Addr) adjustAddressBySegment(ElfW(Addr) addr)
	{
		for (SegmentList_t::iterator it = m_curSegments.begin();
				it != m_curSegments.end(); it++) {
			Segment cur = *it;

			if (addr >= cur.m_paddr && addr < cur.m_paddr + cur.m_size) {
				addr = (addr - cur.m_paddr + cur.m_vaddr);
				break;
			}
		}

		return addr;
	}

	void handleRelPlt(Elf_Scn *scn)
	{
		Elf32_Shdr *shdr = elf32_getshdr(scn);
		Elf_Data *data = elf_getdata(scn, NULL);
		Elf32_Rel *r = (Elf32_Rel *)data->d_buf;
		int n = data->d_size / sizeof(Elf32_Rel);

		panic_if(n <= 0,
				"Section data too small (%zd) - no symbols\n",
				data->d_size);

		for (int i = 0; i < n; i++, r++) {
			Elf32_Addr *got_plt = (Elf32_Addr *)adjustAddressBySegment(r->r_offset);

			FixupMap_t::iterator it = m_fixupFunctions.find(ELF32_R_SYM(r->r_info));

			if (it == m_fixupFunctions.end())
				continue;
			Function *fn = it->second;

			fn->setAddress(offsetTableToAddress(*got_plt));
			fn->setSize(1);
			m_functionsByAddress[fn->getEntry()] = fn;
		}
	}

	void handleDynsym(Elf_Scn *scn)
	{
		handleSymtabGeneric(scn, IFunction::SYM_DYNAMIC);
	}

	void handleSymtab(Elf_Scn *scn)
	{
		handleSymtabGeneric(scn, IFunction::SYM_NORMAL);
	}

	void handleSymtabGeneric(Elf_Scn *scn, enum IFunction::FunctionType symType)
	{
		Elf32_Shdr *shdr = elf32_getshdr(scn);
		Elf_Data *data = elf_getdata(scn, NULL);
		Elf32_Sym *s = (Elf32_Sym *)data->d_buf;
		int n_syms = 0;
		int n_fns = 0;
		int n_datas = 0;
		int n = data->d_size / sizeof(Elf32_Sym);

		panic_if(n <= 0,
				"Section data too small (%zd) - no symbols\n",
				data->d_size);

		/* Iterate through all symbols */
		for (int i = 0; i < n; i++)
		{
			const char *sym_name = elf_strptr(m_elf, shdr->sh_link, s->st_name);
			int type = ELF32_ST_TYPE(s->st_info);

			/* Ohh... This is an interesting symbol, add it! */
			if ( type == STT_FUNC) {
				Elf32_Addr addr = adjustAddressBySegment(s->st_value);
				Elf32_Word size = s->st_size;
				Function *fn = new Function(sym_name, (void *)addr, size, symType);

				m_functionsByName[std::string(sym_name)].push_back(fn);
				// Needs fixup?
				if (shdr->sh_type == SHT_DYNSYM && size == 0)
					m_fixupFunctions[i] = fn;
				else
					m_functionsByAddress[(void *)addr] = fn;
			}

			s++;
		}
	}

	FunctionsByName_t m_functionsByName;
	FunctionsByAddress_t m_functionsByAddress;
	FixupMap_t m_fixupFunctions;
	SegmentList_t m_curSegments;

	Elf *m_elf;
	IFunctionListener *m_listener;
	const char *m_filename;
};

IElf *IElf::open(const char *filename)
{
	static bool initialized = false;
	Elf *p;

	if (!initialized) {
		panic_if(elf_version(EV_CURRENT) == EV_NONE,
				"ELF version failed\n");
		initialized = true;
	}

	p = new Elf(filename);

	if (p->checkFile() == false) {
		delete p;

		return NULL;
	}

	return p;
}
<commit_msg>elf: Add debug dumping of segments<commit_after>#include <elf.hh>
#include <ptrace.hh>
#include <function.hh>
#include <disassembly.hh>
#include <utils.hh>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>
#include <map>
#include <string>

#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <link.h>

using namespace coincident;

class Function : public IFunction, IDisassembly::IInstructionListener
{
public:
	Function(const char *name, void *addr, size_t size,
			IFunction::FunctionType type)
	{
		m_refsValid = false;
		m_name = xstrdup(name);
		m_size = size;
		m_entry = addr;
		m_data = new uint8_t[m_size];
		m_type = type;
	}

	virtual ~Function()
	{
		delete m_name;
		delete m_data;
	}

	enum IFunction::FunctionType getType()
	{
		return m_type;
	}

	const char *getName()
	{
		return m_name;
	}

	size_t getSize()
	{
		return m_size;
	}

	void *getEntry()
	{
		return m_entry;
	}

	void setAddress(void *addr)
	{
		m_entry = addr;
	}

	void setSize(size_t size)
	{
		m_size = size;
	}

	void disassembleFunction()
	{
		bool res = IPtrace::getInstance().readMemory(m_data,
				m_entry, m_size);

		m_loadList.clear();
		m_storeList.clear();

		m_refsValid = true;
		if (!res) {
			error("Can't read memory at %p", m_entry);
			return;
		}

		IDisassembly::getInstance().execute(this, m_data, m_size);
	}

	ReferenceList_t &getMemoryLoads()
	{
		if (!m_refsValid)
			disassembleFunction();

		return m_loadList;
	}

	ReferenceList_t &getMemoryStores()
	{
		if (!m_refsValid)
			disassembleFunction();

		return m_storeList;
	}

	// These three functions are the IInstructionListerners
	void onMemoryReference(off_t offset, bool isLoad)
	{
		off_t addr = (off_t)m_entry + offset;

		if (isLoad)
			m_loadList.push_back((void *)addr);
		else
			m_storeList.push_back((void *)addr);
	}

	void onCall(off_t offset)
	{
	}

	void onBranch(off_t offset)
	{
	}

private:
	bool m_refsValid;
	const char *m_name;
	size_t m_size;
	void *m_entry;
	uint8_t *m_data;
	enum IFunction::FunctionType m_type;

	ReferenceList_t m_loadList;
	ReferenceList_t m_storeList;
};

class Elf : public IElf
{
public:
	Elf(const char *filename)
	{
		m_elf = NULL;
		m_listener = NULL;
		m_filename = strdup(filename);
	}

	~Elf()
	{
		free((void *)m_filename);
	}

	bool checkFile()
	{
		Elf *elf;
		bool out = true;
		int fd;

		fd = ::open(m_filename, O_RDONLY, 0);
		if (fd < 0) {
				error("Cannot open %s\n", m_filename);
				return false;
		}

		if (!(elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
				error("elf_begin failed on %s\n", m_filename);
				out = false;
				goto out_open;
		}
		if (!elf32_getehdr(elf)) {
				error("elf32_getehdr failed on %s\n", m_filename);
				out = false;
		}
		elf_end(elf);

out_open:
		close(fd);

		return out;
	}

	static int phdrCallback(struct dl_phdr_info *info, size_t size,
			void *data)
	{
		struct args
		{
			Elf *p;
			IFunctionListener *listener;
		};
		struct args *a = (struct args *)data;

		a->p->handlePhdr(a->listener, info, size);

		return 0;
	}

	void handlePhdr(IFunctionListener *listener,
			struct dl_phdr_info *info, size_t size)
	{
		int phdr;

		if (strlen(info->dlpi_name) != 0) {
			free( (void *)m_filename );
			m_filename = strdup(info->dlpi_name);
		}
		coin_debug(ELF_MSG, "ELF parsing %s\n", m_filename);

		m_curSegments.clear();
		for (phdr = 0; phdr < info->dlpi_phnum; phdr++) {
			const ElfW(Phdr) *cur = &info->dlpi_phdr[phdr];

			if (cur->p_type != PT_LOAD)
				continue;

			coin_debug(ELF_MSG, "ELF seg 0x%08x -> 0x%08x...0x%08x\n",
					cur->p_paddr, info->dlpi_addr + cur->p_vaddr,
					info->dlpi_addr + cur->p_vaddr + cur->p_memsz);
			m_curSegments.push_back(Segment(cur->p_paddr, info->dlpi_addr + cur->p_vaddr,
					cur->p_memsz, cur->p_align));
		}
		parseOne(listener);
	}

	bool parse(IFunctionListener *listener)
	{
		struct
		{
			Elf *p;
			IFunctionListener *listener;
		} cbArgs;

		m_functionsByAddress.clear();
		m_functionsByName.clear();

		cbArgs.p = this;
		cbArgs.listener = listener;
		dl_iterate_phdr(phdrCallback, (void *)&cbArgs);

		return true;
	}

	bool parseOne(IFunctionListener *listener)
	{
		Elf_Scn *scn = NULL;
		Elf32_Ehdr *ehdr;
		size_t shstrndx;
		bool ret = false;
		int fd;

		m_listener = listener;

		fd = ::open(m_filename, O_RDONLY, 0);
		if (fd < 0) {
				error("Cannot open %s\n", m_filename);
				return false;
		}

		if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
				error("elf_begin failed on %s\n", m_filename);
				goto out_open;
		}

		if (!(ehdr = elf32_getehdr(m_elf))) {
				error("elf32_getehdr failed on %s\n", m_filename);
				goto out_elf_begin;
		}

		if (elf_getshdrstrndx(m_elf, &shstrndx) < 0) {
				error("elf_getshstrndx failed on %s\n", m_filename);
				goto out_elf_begin;
		}

		while ( (scn = elf_nextscn(m_elf, scn)) != NULL )
		{
			Elf32_Shdr *shdr = elf32_getshdr(scn);
			Elf_Data *data = elf_getdata(scn, NULL);
			char *name;

			name = elf_strptr(m_elf, shstrndx, shdr->sh_name);
			if(!data) {
					error("elf_getdata failed on section %s in %s\n",
							name, m_filename);
					goto out_elf_begin;
			}

			/* Handle symbols */
			if (shdr->sh_type == SHT_SYMTAB)
				handleSymtab(scn);
			if (shdr->sh_type == SHT_DYNSYM)
				handleDynsym(scn);
		}
		elf_end(m_elf);
		if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) {
			error("elf_begin failed on %s\n", m_filename);
			goto out_open;
		}
		while ( (scn = elf_nextscn(m_elf, scn)) != NULL )
		{
			Elf32_Shdr *shdr = elf32_getshdr(scn);
			char *name = elf_strptr(m_elf, shstrndx, shdr->sh_name);

			// .rel.plt
			if (shdr->sh_type == SHT_REL && strcmp(name, ".rel.plt") == 0)
				handleRelPlt(scn);
		}
		m_fixupFunctions.clear();
		for (FunctionsByAddress_t::iterator it = m_functionsByAddress.begin();
				it != m_functionsByAddress.end();
				it++) {
			Function *fn = it->second;

			m_listener->onFunction(*fn);
		}

		ret = true;

out_elf_begin:
		elf_end(m_elf);
out_open:
		close(fd);

		return ret;
	}

	IFunction *functionByAddress(void *addr)
	{
		return m_functionsByAddress[addr];
	}

	IElf::FunctionList_t functionByName(const char *name)
	{
		return m_functionsByName[std::string(name)];
	}

private:
	class Segment
	{
	public:
		Segment(ElfW(Addr) paddr, ElfW(Addr) vaddr, size_t size, ElfW(Word) align) :
			m_paddr(paddr), m_vaddr(vaddr), m_size(size), m_align(align)
		{
		}

		ElfW(Addr) m_paddr;
		ElfW(Addr) m_vaddr;
		ElfW(Word) m_align;
		size_t m_size;
	};

	typedef std::map<std::string, IElf::FunctionList_t> FunctionsByName_t;
	typedef std::map<void *, Function *> FunctionsByAddress_t;
	typedef std::map<int, Function *> FixupMap_t;
	typedef std::list<Segment> SegmentList_t;

	void *offsetTableToAddress(Elf32_Addr addr)
	{
		/*
		 * The .got.plt table contains a pointer to the push instruction
		 * below:
		 *
		 *  08070f10 <pthread_self@plt>:
		 *   8070f10:       ff 25 58 93 0b 08       jmp    *0x80b9358
		 *   8070f16:       68 b0 06 00 00          push   $0x6b0
		 *
		 * so to get the entry point, we rewind the pointer to the start
		 * of the jmp.
		 */
		return (void *)(addr - 6);
	}

	ElfW(Addr) adjustAddressBySegment(ElfW(Addr) addr)
	{
		for (SegmentList_t::iterator it = m_curSegments.begin();
				it != m_curSegments.end(); it++) {
			Segment cur = *it;

			if (addr >= cur.m_paddr && addr < cur.m_paddr + cur.m_size) {
				addr = (addr - cur.m_paddr + cur.m_vaddr);
				break;
			}
		}

		return addr;
	}

	void handleRelPlt(Elf_Scn *scn)
	{
		Elf32_Shdr *shdr = elf32_getshdr(scn);
		Elf_Data *data = elf_getdata(scn, NULL);
		Elf32_Rel *r = (Elf32_Rel *)data->d_buf;
		int n = data->d_size / sizeof(Elf32_Rel);

		panic_if(n <= 0,
				"Section data too small (%zd) - no symbols\n",
				data->d_size);

		for (int i = 0; i < n; i++, r++) {
			Elf32_Addr *got_plt = (Elf32_Addr *)adjustAddressBySegment(r->r_offset);

			FixupMap_t::iterator it = m_fixupFunctions.find(ELF32_R_SYM(r->r_info));

			if (it == m_fixupFunctions.end())
				continue;
			Function *fn = it->second;

			fn->setAddress(offsetTableToAddress(*got_plt));
			fn->setSize(1);
			m_functionsByAddress[fn->getEntry()] = fn;
		}
	}

	void handleDynsym(Elf_Scn *scn)
	{
		handleSymtabGeneric(scn, IFunction::SYM_DYNAMIC);
	}

	void handleSymtab(Elf_Scn *scn)
	{
		handleSymtabGeneric(scn, IFunction::SYM_NORMAL);
	}

	void handleSymtabGeneric(Elf_Scn *scn, enum IFunction::FunctionType symType)
	{
		Elf32_Shdr *shdr = elf32_getshdr(scn);
		Elf_Data *data = elf_getdata(scn, NULL);
		Elf32_Sym *s = (Elf32_Sym *)data->d_buf;
		int n_syms = 0;
		int n_fns = 0;
		int n_datas = 0;
		int n = data->d_size / sizeof(Elf32_Sym);

		panic_if(n <= 0,
				"Section data too small (%zd) - no symbols\n",
				data->d_size);

		/* Iterate through all symbols */
		for (int i = 0; i < n; i++)
		{
			const char *sym_name = elf_strptr(m_elf, shdr->sh_link, s->st_name);
			int type = ELF32_ST_TYPE(s->st_info);

			/* Ohh... This is an interesting symbol, add it! */
			if ( type == STT_FUNC) {
				Elf32_Addr addr = adjustAddressBySegment(s->st_value);
				Elf32_Word size = s->st_size;
				Function *fn = new Function(sym_name, (void *)addr, size, symType);

				m_functionsByName[std::string(sym_name)].push_back(fn);
				// Needs fixup?
				if (shdr->sh_type == SHT_DYNSYM && size == 0)
					m_fixupFunctions[i] = fn;
				else
					m_functionsByAddress[(void *)addr] = fn;
			}

			s++;
		}
	}

	FunctionsByName_t m_functionsByName;
	FunctionsByAddress_t m_functionsByAddress;
	FixupMap_t m_fixupFunctions;
	SegmentList_t m_curSegments;

	Elf *m_elf;
	IFunctionListener *m_listener;
	const char *m_filename;
};

IElf *IElf::open(const char *filename)
{
	static bool initialized = false;
	Elf *p;

	if (!initialized) {
		panic_if(elf_version(EV_CURRENT) == EV_NONE,
				"ELF version failed\n");
		initialized = true;
	}

	p = new Elf(filename);

	if (p->checkFile() == false) {
		delete p;

		return NULL;
	}

	return p;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

namespace etl {

/*!
 * \brief A serializer for ETL expressions
 */
template <typename Stream>
struct serializer {
    using stream_t = Stream;                       ///< The type of stream to use
    using char_t   = typename stream_t::char_type; ///< The char type of the stream

    stream_t stream; ///< The stream

    /*!
     * \brief Construct the serializer by forwarding the arguments
     * to the stream
     * \param args The arguments to forward to the stream constructor
     */
    template <typename... Args>
    serializer(Args&&... args)
            : stream(std::forward<Args>(args)...) {}

    /*!
     * \brief Outputs the given value to the stream
     * \param value The value to write to the stream
     * \return the serializer
     */
    template <typename T, cpp_enable_if(std::is_arithmetic<T>::value)>
    serializer& operator<<(const T& value) {
        stream.write(reinterpret_cast<const char_t*>(&value), sizeof(T));
        return *this;
    }

    /*!
     * \brief Outputs the given ETL expression to the stream
     * \param value The ETL expression to write to the stream
     * \return the serializer
     */
    template <typename T, cpp_disable_if(std::is_arithmetic<T>::value)>
    serializer& operator<<(const T& value) {
        serialize(*this, value);
        return *this;
    }
};

} //end of namespace etl
<commit_msg>Make the constructor explicit<commit_after>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

namespace etl {

/*!
 * \brief A serializer for ETL expressions
 */
template <typename Stream>
struct serializer {
    using stream_t = Stream;                       ///< The type of stream to use
    using char_t   = typename stream_t::char_type; ///< The char type of the stream

    stream_t stream; ///< The stream

    /*!
     * \brief Construct the serializer by forwarding the arguments
     * to the stream
     * \param args The arguments to forward to the stream constructor
     */
    template <typename... Args>
    explicit serializer(Args&&... args)
            : stream(std::forward<Args>(args)...) {}

    /*!
     * \brief Outputs the given value to the stream
     * \param value The value to write to the stream
     * \return the serializer
     */
    template <typename T, cpp_enable_if(std::is_arithmetic<T>::value)>
    serializer& operator<<(const T& value) {
        stream.write(reinterpret_cast<const char_t*>(&value), sizeof(T));
        return *this;
    }

    /*!
     * \brief Outputs the given ETL expression to the stream
     * \param value The ETL expression to write to the stream
     * \return the serializer
     */
    template <typename T, cpp_disable_if(std::is_arithmetic<T>::value)>
    serializer& operator<<(const T& value) {
        serialize(*this, value);
        return *this;
    }
};

} //end of namespace etl
<|endoftext|>


<commit_before>/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer
 * 2. 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 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.
 */

#define BOOST_TEST_MODULE DSSP_Test
#include <boost/test/included/unit_test.hpp>
#include <boost/algorithm/string.hpp>

#include <stdexcept>

#include <cif++/Structure.hpp>
#include <cif++/Secondary.hpp>
#include <cif++/CifUtils.hpp>
#include <cif++/Cif2PDB.hpp>

#include "dssp.hpp"

namespace ba = boost::algorithm;

// --------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(ut_dssp)
{
	using namespace std::literals;

	mmcif::File f("1cbs.cif.gz");
	mmcif::Structure structure(f, 1, mmcif::StructureOpenOptions::SkipHydrogen);

	mmcif::DSSP dssp(structure, 3, true);

	std::stringstream test;

	writeDSSP(structure, dssp, test);

	std::ifstream reference("1cbs.dssp");

	BOOST_ASSERT(reference.is_open());

	std::string line_t, line_r;
	BOOST_ASSERT (std::getline(test, line_t) and std::getline(reference, line_r));
	BOOST_ASSERT(line_t.compare(0, 104,  "==== Secondary Structure Definition by the program DSSP, NKI version 4.0                           ==== ") == 0);

	for (int line_nr = 1; ; ++line_nr)
	{
		bool done_t = not std::getline(test, line_t);
		bool done_r = not std::getline(reference, line_r);

		BOOST_CHECK_EQUAL(done_r, done_t);
		if (done_r)
			break;

		if (line_t != line_r)
			std::cerr << line_nr << std::endl
					<< line_t << std::endl
					<< line_r << std::endl;

		BOOST_CHECK(line_t == line_r);
	}

	BOOST_CHECK(test.eof());
	BOOST_CHECK(reference.eof());
}

BOOST_AUTO_TEST_CASE(ut_mmcif)
{
	using namespace std::literals;

	mmcif::File f("1cbs.cif.gz");
	mmcif::Structure structure(f, 1, mmcif::StructureOpenOptions::SkipHydrogen);

	mmcif::DSSP dssp(structure, 3, true);

	std::stringstream test;

	annotateDSSP(structure, dssp, true, test);

	std::ifstream reference("1cbs-dssp.cif");

	BOOST_ASSERT(reference.is_open());

	std::string line_t, line_r;
	BOOST_ASSERT (std::getline(test, line_t) and std::getline(reference, line_r));
	BOOST_ASSERT(line_t.compare(0, 104,  "==== Secondary Structure Definition by the program DSSP, NKI version 4.0                           ==== ") == 0);

	for (int line_nr = 1; ; ++line_nr)
	{
		bool done_t = not std::getline(test, line_t);
		bool done_r = not std::getline(reference, line_r);

		BOOST_CHECK_EQUAL(done_r, done_t);
		if (done_r)
			break;

		if (line_t != line_r)
			std::cerr << line_nr << std::endl
					<< line_t << std::endl
					<< line_r << std::endl;

		BOOST_CHECK(line_t == line_r);
	}

	BOOST_CHECK(test.eof());
	BOOST_CHECK(reference.eof());
}
<commit_msg>fix unit test<commit_after>/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer
 * 2. 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 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.
 */

#define BOOST_TEST_MODULE DSSP_Test
#include <boost/test/included/unit_test.hpp>
#include <boost/algorithm/string.hpp>

#include <stdexcept>

#include <cif++/Structure.hpp>
#include <cif++/Secondary.hpp>
#include <cif++/CifUtils.hpp>
#include <cif++/Cif2PDB.hpp>

#include "dssp.hpp"

namespace ba = boost::algorithm;

// --------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(ut_dssp)
{
	using namespace std::literals;

	mmcif::File f("1cbs.cif.gz");
	mmcif::Structure structure(f, 1, mmcif::StructureOpenOptions::SkipHydrogen);

	mmcif::DSSP dssp(structure, 3, true);

	std::stringstream test;

	writeDSSP(structure, dssp, test);

	std::ifstream reference("1cbs.dssp");

	BOOST_ASSERT(reference.is_open());

	std::string line_t, line_r;
	BOOST_ASSERT (std::getline(test, line_t) and std::getline(reference, line_r));

	const char* kHeaderLineStart = "==== Secondary Structure Definition by the program DSSP, NKI version 4.0                           ====";
	BOOST_ASSERT(line_t.compare(0, std::strlen(kHeaderLineStart), kHeaderLineStart) == 0);

	for (int line_nr = 2; ; ++line_nr)
	{
		bool done_t = not std::getline(test, line_t);
		bool done_r = not std::getline(reference, line_r);

		BOOST_CHECK_EQUAL(done_r, done_t);
		if (done_r)
			break;

		if (line_t != line_r)
			std::cerr << line_nr << std::endl
					<< line_t << std::endl
					<< line_r << std::endl;

		BOOST_CHECK(line_t == line_r);
	}

	BOOST_CHECK(test.eof());
	BOOST_CHECK(reference.eof());
}

BOOST_AUTO_TEST_CASE(ut_mmcif)
{
	using namespace std::literals;

	mmcif::File f("1cbs.cif.gz");
	mmcif::Structure structure(f, 1, mmcif::StructureOpenOptions::SkipHydrogen);

	mmcif::DSSP dssp(structure, 3, true);

	std::stringstream test;

	annotateDSSP(structure, dssp, true, test);

	std::ifstream reference("1cbs-dssp.cif");

	BOOST_ASSERT(reference.is_open());

	std::string line_t, line_r;

	for (int line_nr = 1; ; ++line_nr)
	{
		bool done_t = not std::getline(test, line_t);
		bool done_r = not std::getline(reference, line_r);

		BOOST_CHECK_EQUAL(done_r, done_t);
		if (done_r)
			break;

		if (line_t != line_r)
			std::cerr << line_nr << std::endl
					<< line_t << std::endl
					<< line_r << std::endl;

		BOOST_CHECK(line_t == line_r);
	}

	BOOST_CHECK(test.eof());
	BOOST_CHECK(reference.eof());
}
<|endoftext|>


<commit_before>#include "config.h"

Config* Config::getHandle() {
	static Config instance;
	return &instance;
}

void Config::setMainConfigFile(const std::string& configFileName) {
	if (confname.empty())
		confname = configFileName;
}

void Config::readConfig() {
	config = readConfig(confname, std::ifstream(confname));
	for (auto callback : notifyList)
		callback();
}

enum ConfigState { CONFIG_BLOCK, CONFIG_KEY, CONFIG_VALUE, CONFIG_VALUE_NEXT, CONFIG_VALUE_VAR, CONFIG_VALUE_STR, CONFIG_VALUE_STR_ESCAPED };
// This enum is simply an implementation detail for the configuration parser; hence it is placed here instead of the header file.

std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>> Config::readConfig(const std::string& filename, std::istream&& configData) {
	ConfigState state = CONFIG_BLOCK;
	unsigned int lineNum = 1;
	LogManager* logger = LogManager::getHandle();
	std::string blockName, key, value, valueVar;
	std::unordered_map<std::string, std::string> blockValues;
	std::unordered_multimap<std::string, std::string> localConfig;
	while (configData.good()) {
		char nextChar = configData.get();
		if (nextChar == std::istream::traits_type::eof())
			break;
		if (nextChar == '\n')
			lineNum++;
		if (state == CONFIG_BLOCK) {
			if (nextChar == '{') {
				if (blockName.empty())
					throw ConfigError (filename, lineNum, "A block without a name was opened.");
				state = CONFIG_KEY;
			} else if (nextChar == '}')
				throw ConfigError (filename, lineNum, "A closing brace could not be mached to an open block.");
			else if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n')
				blockName += nextChar;
		} else if (state == CONFIG_KEY) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == '{')
				throw ConfigError (filename, lineNum, "A block was opened inside another block.");
			else if (nextChar == '}') {
				if (blockValues.empty())
					throw ConfigError (filename, lineNum, "An empty block was defined.  What was the point of that?");
				if (!key.empty() || !value.empty())
					throw ConfigError (filename, lineNum, "A key-value pair was unfinished when the block was terminated.");
				localConfig.insert(std::pair<std::string, std::unordered_map<std::string, std::string>> (blockName, blockValues));
				blockName.clear();
				blockValues.clear();
				logger->log(LOG_ALL, "config", "Block " + blockName + " read.");
				state = CONFIG_BLOCK;
			} else if (nextChar == ';')
				throw ConfigError (filename, lineNum, "A key was specified without a value.");
			else if (nextChar == '=')
				state = CONFIG_VALUE_NEXT;
			else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n')
				key += nextChar;
		} else if (state == CONFIG_VALUE) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == ';') {
				blockValues.insert(std::pair<std::string, std::string> (key, value));
				key.clear();
				value.clear();
				state = CONFIG_KEY;
			} else if (nextChar == '+')
				state = CONFIG_VALUE_NEXT;
			else
				throw ConfigError (filename, lineNum, "An unexpected character was encountered; expected ';' or '+' (perhaps you forgot to end or properly concatenate your value?).");
		} else if (state == CONFIG_VALUE_NEXT) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == '"')
				state = CONFIG_VALUE_STR;
			else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n') {
				valueVar += nextChar;
				state = CONFIG_VALUE_VAR;
			}
		} else if (state == CONFIG_VALUE_VAR) {
			if (nextChar == '"')
				throw ConfigError (filename, lineNum, "A string value was specified in the middle of a variable (perhaps you forgot to properly concatenate with '+'?).");
			else if (nextChar == '{')
				throw ConfigError (filename, lineNum, "A block was opened inside another block.");
			else if (nextChar == '}')
				throw ConfigError (filename, lineNum, "A key-value pair was unfinished when the block was terminated.");
			else if (nextChar == ' ' || nextChar == '\t' || nextChar == '\r' || nextChar == '\n' || nextChar == '+' || nextChar == ';') {
				auto keyIter = blockValues.find(valueVar);
				if (keyIter == blockValues.end())
					throw ConfigError (filename, lineNum, "The variable " + valueVar + " was specified, but did not match a variable already specified in the block.");
				value += keyIter->second;
				valueVar.clear();
				if (nextChar == ';') {
					state = CONFIG_KEY;
					blockValues.insert(std::pair<std::string, std::string> (key, value));
					key.clear();
					value.clear();
				} else if (nextChar == '+')
					state = CONFIG_VALUE_NEXT;
				else
					state = CONFIG_VALUE;
			} else
				valueVar += nextChar;
		} else if (state == CONFIG_VALUE_STR) {
			if (nextChar == '\\')
				state = CONFIG_VALUE_STR_ESCAPED;
			else if (nextChar == '"')
				state = CONFIG_VALUE;
			else
				value += nextChar;
		} else if (state == CONFIG_VALUE_STR_ESCAPED) {
			if (nextChar == '\\')
				value += '\\';
			else if (nextChar == 'n')
				value += '\n';
			else if (nextChar == 't')
				value += '\t';
			else if (nextChar == 'r')
				value += '\r';
			else
				value += nextChar;
			state = CONFIG_VALUE_STR;
		}
	}
	if (!blockValues.empty())
		throw ConfigError (filename, lineNum, "A block remained unterminated at the end of the file.");
	std::list<std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>> includedConfigs;
	for (auto block : localConfig) {
		if (block.first == "include") {
			auto inclfIter = block.second.find("file");
			if (inclfIter != block.second.end()) {
				std::string name = inclfIter->second;
				logger->log(LOG_ALL, "config", "Reading included file " + inclfIter->second);
				includedConfigs.push_back(readConfig(inclfIter->second, std::ifstream(name)));
			} else {
				auto incleIter = block.second.find("executable");
				if (incleIter != block.second.end()) {
					std::string cmd = incleIter->second;
					logger->log(LOG_ALL, "config", "Getting configuration data from `" + incleIter->second + "`");
					FILE* pipe = popen(cmd, "r");
					if (!pipe)
						raise ConfigError (cmd, 0, "Could not open pipe to read output.");
					char buffer[256];
					std::stringstream output;
					while (!feof(pipe)) {
						if (fgets(buffer, 256, pipe) != NULL)
							output << buffer;
					}
					pclose(pipe);
					includedConfigs.push_back(readConfig(cmd, output));
				} else
					throw ConfigError (filename, lineNum, "An include block was present that contained neither a file nor an executable.");
			}
		}
	}
	for (auto includes : includedConfigs) {
		for (auto conf : includes.second)
			localConfig.insert(std::pair<std::string, std::unordered_map<std::string, std::string>> (conf));
	}
	return localConfig;
}

void Config::addRehashNotify(std::function<void()> notifyCallback) {
	notifyList.push_back(notifyCallback);
}

size_t Config::blockCount(const std::string& block) const {
	return config.count(block);
}

std::list<std::unordered_map<std::string, std::string>> Config::getBlock(const std::string& block) const {
	std::list<std::unordered_map<std::string, std::string>> blockList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter)
		blockList.push_back(startIter->second);
	return blockList;
}

std::unordered_map<std::string, std::string> Config::getSingleBlock(const std::string& block, const std::unordered_map<std::string, std::string>& conditions) const {
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter) {
		for (auto check : conditions) {
			auto blockIter = startIter->second.find(check.first);
			if (blockIter == startIter->second.end())
				continue;
			if (check.second == blockIter->second)
				return startIter->second;
		}
	}
	return std::unordered_map<std::string, std::string> ();
}

std::string Config::getValue(const std::string& block, const std::string& key) const {
	auto blockIter = config.find(block);
	if (blockIter == config.end())
		return "";
	auto dataIter = blockIter->second.find(key);
	if (dataIter == blockIter->second.end())
		return "";
	return dataIter->second;
}

bool Config::getBoolValue(const std::string& block, const std::string& key) const {
	const std::string& value = getValue(block, key);
	std::string lowerValue;
	std::transform(value.begin(), value.end(), std::back_inserter(lowerValue), ::tolower);
	if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
		return true;
	return false;
}

std::list<std::string> Config::getAllValues(const std::string& block, const std::string& key) const {
	std::list<std::string> valueList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter) {
		auto dataIter = startIter->second.find(key);
		if (dataIter == startIter->second.end())
			valueList.push_back("");
		else
			valueList.push_back(dataIter->second);
	}
	return valueList;
}

std::list<bool> Config::getAllBoolValues(const std::string& block, const std::string& key) const {
	std::list<bool> valueList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (std::string lowerValue; startIter != endIter; ++startIter) {
		auto dataIter = startIter->second.find(key);
		if (dataIter == startIter->second.end())
			valueList.push_back(false);
		else {
			lowerValue.clear();
			std::transform(dataIter->second.begin(), dataIter->second.end(), std::back_inserter(lowerValue), ::tolower);
			if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
				valueList.push_back(true);
			else
				valueList.push_back(false);
		}
	}
	return valueList;
}

static bool Config::makeBool(const std::string& value) {
	std::string lowerValue;
	std::transform(value.begin(), value.end(), std::back_inserter(lowerValue), ::tolower);
	if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
		return true;
	return false;
}

Config::Config() {}

ConfigError::ConfigError(const std::string& filename, unsigned int lineNum, const std::string& description) {
	std::ostringstream descStr;
	descStr << "An error occurred reading the configuration from '" << filename << "' on line " << lineNum << ": " << description;
	desc = descStr.str();
}<commit_msg>Fix type of localConfig in the configuration parser<commit_after>#include "config.h"

Config* Config::getHandle() {
	static Config instance;
	return &instance;
}

void Config::setMainConfigFile(const std::string& configFileName) {
	if (confname.empty())
		confname = configFileName;
}

void Config::readConfig() {
	config = readConfig(confname, std::ifstream(confname));
	for (auto callback : notifyList)
		callback();
}

enum ConfigState { CONFIG_BLOCK, CONFIG_KEY, CONFIG_VALUE, CONFIG_VALUE_NEXT, CONFIG_VALUE_VAR, CONFIG_VALUE_STR, CONFIG_VALUE_STR_ESCAPED };
// This enum is simply an implementation detail for the configuration parser; hence it is placed here instead of the header file.

std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>> Config::readConfig(const std::string& filename, std::istream&& configData) {
	ConfigState state = CONFIG_BLOCK;
	unsigned int lineNum = 1;
	LogManager* logger = LogManager::getHandle();
	std::string blockName, key, value, valueVar;
	std::unordered_map<std::string, std::string> blockValues;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>> localConfig;
	while (configData.good()) {
		char nextChar = configData.get();
		if (nextChar == std::istream::traits_type::eof())
			break;
		if (nextChar == '\n')
			lineNum++;
		if (state == CONFIG_BLOCK) {
			if (nextChar == '{') {
				if (blockName.empty())
					throw ConfigError (filename, lineNum, "A block without a name was opened.");
				state = CONFIG_KEY;
			} else if (nextChar == '}')
				throw ConfigError (filename, lineNum, "A closing brace could not be mached to an open block.");
			else if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n')
				blockName += nextChar;
		} else if (state == CONFIG_KEY) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == '{')
				throw ConfigError (filename, lineNum, "A block was opened inside another block.");
			else if (nextChar == '}') {
				if (blockValues.empty())
					throw ConfigError (filename, lineNum, "An empty block was defined.  What was the point of that?");
				if (!key.empty() || !value.empty())
					throw ConfigError (filename, lineNum, "A key-value pair was unfinished when the block was terminated.");
				localConfig.insert(std::pair<std::string, std::unordered_map<std::string, std::string>> (blockName, blockValues));
				blockName.clear();
				blockValues.clear();
				logger->log(LOG_ALL, "config", "Block " + blockName + " read.");
				state = CONFIG_BLOCK;
			} else if (nextChar == ';')
				throw ConfigError (filename, lineNum, "A key was specified without a value.");
			else if (nextChar == '=')
				state = CONFIG_VALUE_NEXT;
			else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n')
				key += nextChar;
		} else if (state == CONFIG_VALUE) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == ';') {
				blockValues.insert(std::pair<std::string, std::string> (key, value));
				key.clear();
				value.clear();
				state = CONFIG_KEY;
			} else if (nextChar == '+')
				state = CONFIG_VALUE_NEXT;
			else
				throw ConfigError (filename, lineNum, "An unexpected character was encountered; expected ';' or '+' (perhaps you forgot to end or properly concatenate your value?).");
		} else if (state == CONFIG_VALUE_NEXT) {
			if (nextChar == '#') {
				while (configData.good() && configData.get() != '\n') {}
				lineNum++;
			} else if (nextChar == '"')
				state = CONFIG_VALUE_STR;
			else if (nextChar != ' ' && nextChar != '\t' && nextChar != '\r' && nextChar != '\n') {
				valueVar += nextChar;
				state = CONFIG_VALUE_VAR;
			}
		} else if (state == CONFIG_VALUE_VAR) {
			if (nextChar == '"')
				throw ConfigError (filename, lineNum, "A string value was specified in the middle of a variable (perhaps you forgot to properly concatenate with '+'?).");
			else if (nextChar == '{')
				throw ConfigError (filename, lineNum, "A block was opened inside another block.");
			else if (nextChar == '}')
				throw ConfigError (filename, lineNum, "A key-value pair was unfinished when the block was terminated.");
			else if (nextChar == ' ' || nextChar == '\t' || nextChar == '\r' || nextChar == '\n' || nextChar == '+' || nextChar == ';') {
				auto keyIter = blockValues.find(valueVar);
				if (keyIter == blockValues.end())
					throw ConfigError (filename, lineNum, "The variable " + valueVar + " was specified, but did not match a variable already specified in the block.");
				value += keyIter->second;
				valueVar.clear();
				if (nextChar == ';') {
					state = CONFIG_KEY;
					blockValues.insert(std::pair<std::string, std::string> (key, value));
					key.clear();
					value.clear();
				} else if (nextChar == '+')
					state = CONFIG_VALUE_NEXT;
				else
					state = CONFIG_VALUE;
			} else
				valueVar += nextChar;
		} else if (state == CONFIG_VALUE_STR) {
			if (nextChar == '\\')
				state = CONFIG_VALUE_STR_ESCAPED;
			else if (nextChar == '"')
				state = CONFIG_VALUE;
			else
				value += nextChar;
		} else if (state == CONFIG_VALUE_STR_ESCAPED) {
			if (nextChar == '\\')
				value += '\\';
			else if (nextChar == 'n')
				value += '\n';
			else if (nextChar == 't')
				value += '\t';
			else if (nextChar == 'r')
				value += '\r';
			else
				value += nextChar;
			state = CONFIG_VALUE_STR;
		}
	}
	if (!blockValues.empty())
		throw ConfigError (filename, lineNum, "A block remained unterminated at the end of the file.");
	std::list<std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>> includedConfigs;
	for (auto block : localConfig) {
		if (block.first == "include") {
			auto inclfIter = block.second.find("file");
			if (inclfIter != block.second.end()) {
				std::string name = inclfIter->second;
				logger->log(LOG_ALL, "config", "Reading included file " + inclfIter->second);
				includedConfigs.push_back(readConfig(inclfIter->second, std::ifstream(name)));
			} else {
				auto incleIter = block.second.find("executable");
				if (incleIter != block.second.end()) {
					std::string cmd = incleIter->second;
					logger->log(LOG_ALL, "config", "Getting configuration data from `" + incleIter->second + "`");
					FILE* pipe = popen(cmd, "r");
					if (!pipe)
						raise ConfigError (cmd, 0, "Could not open pipe to read output.");
					char buffer[256];
					std::stringstream output;
					while (!feof(pipe)) {
						if (fgets(buffer, 256, pipe) != NULL)
							output << buffer;
					}
					pclose(pipe);
					includedConfigs.push_back(readConfig(cmd, output));
				} else
					throw ConfigError (filename, lineNum, "An include block was present that contained neither a file nor an executable.");
			}
		}
	}
	for (auto includes : includedConfigs) {
		for (auto conf : includes.second)
			localConfig.insert(std::pair<std::string, std::unordered_map<std::string, std::string>> (conf));
	}
	return localConfig;
}

void Config::addRehashNotify(std::function<void()> notifyCallback) {
	notifyList.push_back(notifyCallback);
}

size_t Config::blockCount(const std::string& block) const {
	return config.count(block);
}

std::list<std::unordered_map<std::string, std::string>> Config::getBlock(const std::string& block) const {
	std::list<std::unordered_map<std::string, std::string>> blockList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter)
		blockList.push_back(startIter->second);
	return blockList;
}

std::unordered_map<std::string, std::string> Config::getSingleBlock(const std::string& block, const std::unordered_map<std::string, std::string>& conditions) const {
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter) {
		for (auto check : conditions) {
			auto blockIter = startIter->second.find(check.first);
			if (blockIter == startIter->second.end())
				continue;
			if (check.second == blockIter->second)
				return startIter->second;
		}
	}
	return std::unordered_map<std::string, std::string> ();
}

std::string Config::getValue(const std::string& block, const std::string& key) const {
	auto blockIter = config.find(block);
	if (blockIter == config.end())
		return "";
	auto dataIter = blockIter->second.find(key);
	if (dataIter == blockIter->second.end())
		return "";
	return dataIter->second;
}

bool Config::getBoolValue(const std::string& block, const std::string& key) const {
	const std::string& value = getValue(block, key);
	std::string lowerValue;
	std::transform(value.begin(), value.end(), std::back_inserter(lowerValue), ::tolower);
	if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
		return true;
	return false;
}

std::list<std::string> Config::getAllValues(const std::string& block, const std::string& key) const {
	std::list<std::string> valueList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (; startIter != endIter; ++startIter) {
		auto dataIter = startIter->second.find(key);
		if (dataIter == startIter->second.end())
			valueList.push_back("");
		else
			valueList.push_back(dataIter->second);
	}
	return valueList;
}

std::list<bool> Config::getAllBoolValues(const std::string& block, const std::string& key) const {
	std::list<bool> valueList;
	std::unordered_multimap<std::string, std::unordered_map<std::string, std::string>>::const_iterator startIter, endIter;
	std::tie(startIter, endIter) = config.equal_range(block);
	for (std::string lowerValue; startIter != endIter; ++startIter) {
		auto dataIter = startIter->second.find(key);
		if (dataIter == startIter->second.end())
			valueList.push_back(false);
		else {
			lowerValue.clear();
			std::transform(dataIter->second.begin(), dataIter->second.end(), std::back_inserter(lowerValue), ::tolower);
			if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
				valueList.push_back(true);
			else
				valueList.push_back(false);
		}
	}
	return valueList;
}

static bool Config::makeBool(const std::string& value) {
	std::string lowerValue;
	std::transform(value.begin(), value.end(), std::back_inserter(lowerValue), ::tolower);
	if (lowerValue == "yes" || lowerValue == "on" || lowerValue == "true")
		return true;
	return false;
}

Config::Config() {}

ConfigError::ConfigError(const std::string& filename, unsigned int lineNum, const std::string& description) {
	std::ostringstream descStr;
	descStr << "An error occurred reading the configuration from '" << filename << "' on line " << lineNum << ": " << description;
	desc = descStr.str();
}<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Daniele Bartolini, Michele Rossi
Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
OTHER DEALINGS IN THE SOFTWARE.
*/

#include <cstdlib>

#include "Config.h"
#include "Device.h"
#include "Accelerometer.h"
#include "Args.h"
#include "DiskFile.h"
#include "DiskFilesystem.h"
#include "JSONParser.h"
#include "Keyboard.h"
#include "Log.h"
#include "LuaEnvironment.h"
#include "Memory.h"
#include "Mouse.h"
#include "OS.h"
#include "OsWindow.h"
#include "Renderer.h"
#include "ResourceManager.h"
#include "StringSetting.h"
#include "StringUtils.h"
#include "TextReader.h"
#include "Touch.h"
#include "Types.h"
#include "Bundle.h"
#include "TempAllocator.h"
#include "ResourcePackage.h"
#include "ConsoleServer.h"
#include "World.h"
#include "LuaStack.h"
#include "WorldManager.h"
#include "NetworkFilesystem.h"
#include "LuaSystem.h"

#if defined(LINUX) || defined(WINDOWS)
	#include "BundleCompiler.h"
#endif

#if defined(ANDROID)
	#include "ApkFilesystem.h"
#endif

#define MAX_SUBSYSTEMS_HEAP 8 * 1024 * 1024

namespace crown
{
//-----------------------------------------------------------------------------
Device::Device()
	: m_allocator(default_allocator(), MAX_SUBSYSTEMS_HEAP)

	, m_argc(0)
	, m_argv(NULL)

	, m_fileserver(0)
	, m_console_port(10001)

	, m_is_init(false)
	, m_is_running(false)
	, m_is_paused(false)

	, m_frame_count(0)

	, m_last_time(0)
	, m_current_time(0)
	, m_last_delta_time(0.0f)
	, m_time_since_start(0.0)

	, m_filesystem(NULL)
	, m_lua_environment(NULL)
	, m_renderer(NULL)

	, m_bundle_compiler(NULL)
	, m_console(NULL)
	, m_resource_manager(NULL)
	, m_resource_bundle(NULL)

	, m_world_manager(NULL)
{
	// Bundle dir is current dir by default.
	string::strncpy(m_bundle_dir, os::get_cwd(), MAX_PATH_LENGTH);
	string::strncpy(m_source_dir, "", MAX_PATH_LENGTH);
	string::strncpy(m_boot_file, "lua/game", MAX_PATH_LENGTH);
}

//-----------------------------------------------------------------------------
Device::~Device()
{
}

//-----------------------------------------------------------------------------
void Device::init()
{
	// Initialize
	Log::i("Initializing Crown Engine %d.%d.%d...", CROWN_VERSION_MAJOR, CROWN_VERSION_MINOR, CROWN_VERSION_MICRO);

	// RPC only in debug or development builds
	#if defined(CROWN_DEBUG) || defined(CROWN_DEVELOPMENT)
		m_console = CE_NEW(m_allocator, ConsoleServer)(m_console_port);
		m_console->init(false);
	#endif

	Log::d("Creating filesystem...");
	// Default bundle filesystem
	#if defined (LINUX) || defined(WINDOWS)
		if (m_fileserver == 1)
		{
			m_filesystem = CE_NEW(m_allocator, NetworkFilesystem)(NetAddress(127, 0, 0, 1), 10001);
		}
		else
		{
			m_filesystem = CE_NEW(m_allocator, DiskFilesystem)(m_bundle_dir);
		}
	#elif defined(ANDROID)
		if (m_fileserver == 1)
		{
			m_filesystem = CE_NEW(m_allocator, NetworkFilesystem)(NetAddress(192, 168, 0, 7), 10001);
		}
		else
		{
			m_filesystem = CE_NEW(m_allocator, ApkFilesystem)();
		}
	#endif

	m_resource_bundle = Bundle::create(m_allocator, *m_filesystem);

	// Create resource manager
	Log::d("Creating resource manager...");
	m_resource_manager = CE_NEW(m_allocator, ResourceManager)(*m_resource_bundle, 0);
	Log::d("Resource seed: %d", m_resource_manager->seed());

	// Create world manager
	Log::d("Creating world manager...");
	m_world_manager = CE_NEW(m_allocator, WorldManager)();

	// Create window
	Log::d("Creating main window...");
	m_window = CE_NEW(m_allocator, OsWindow);

	// Create input devices
	m_keyboard = CE_NEW(m_allocator, Keyboard);
	m_mouse = CE_NEW(m_allocator, Mouse);
	m_touch = CE_NEW(m_allocator, Touch);

	// Create renderer
	Log::d("Creating renderer...");
	m_renderer = CE_NEW(m_allocator, Renderer)(m_allocator);
	m_renderer->init();

	Log::d("Creating lua system...");
	lua_system::init();
	m_lua_environment = CE_NEW(m_allocator, LuaEnvironment)(lua_system::state());

	Log::d("Creating physics...");
	physics_system::init();

	Log::d("Creating audio...");
	audio_system::init();

	Log::d("Crown Engine initialized.");
	Log::d("Initializing Game...");

	m_physics_config = m_resource_manager->load(PHYSICS_CONFIG_EXTENSION, "global");
	m_resource_manager->flush();

	m_is_init = true;
	start();

	// Execute lua boot file
	if (m_lua_environment->load_and_execute(m_boot_file))
	{
		if (!m_lua_environment->call_global("init", 0))
		{
			pause();
		}
	}
	else
	{
		pause();
	}

	Log::d("Total allocated size: %ld", m_allocator.allocated_size());
}

//-----------------------------------------------------------------------------
void Device::shutdown()
{
	CE_ASSERT(is_init(), "Engine is not initialized");

	// Shutdowns the game
	m_lua_environment->call_global("shutdown", 0);

	m_resource_manager->unload(m_physics_config);

	Log::d("Releasing audio...");
	audio_system::shutdown();

	Log::d("Releasing physics...");
	physics_system::shutdown();

	Log::d("Releasing lua system...");
	lua_system::shutdown();
	if (m_lua_environment)
	{
		CE_DELETE(m_allocator, m_lua_environment);
	}

	Log::d("Releasing input devices...");
	CE_DELETE(m_allocator, m_touch);
	CE_DELETE(m_allocator, m_mouse);
	CE_DELETE(m_allocator, m_keyboard);

	Log::d("Releasing renderer...");
	if (m_renderer)
	{
		m_renderer->shutdown();
		CE_DELETE(m_allocator, m_renderer);
	}

	Log::d("Releasing world manager...");
	CE_DELETE(m_allocator, m_world_manager);

	Log::d("Releasing resource manager...");
	if (m_resource_manager)
	{
		CE_DELETE(m_allocator, m_resource_manager);
	}

	if (m_resource_bundle)
	{
		Bundle::destroy(m_allocator, m_resource_bundle);
	}

	Log::d("Releasing filesystem...");
	if (m_filesystem)
	{
		CE_DELETE(m_allocator, m_filesystem);
	}

	#if defined(CROWN_DEBUG) || defined(CROWN_DEVELOPMENT)
		m_console->shutdown();
		CE_DELETE(m_allocator, m_console);
		m_console = NULL;
	#endif

	m_allocator.clear();
	m_is_init = false;
}

//-----------------------------------------------------------------------------
bool Device::is_init() const
{
	return m_is_init;
}

//-----------------------------------------------------------------------------
bool Device::is_paused() const
{
	return m_is_paused;
}

//-----------------------------------------------------------------------------
Filesystem* Device::filesystem()
{
	return m_filesystem;
}

//-----------------------------------------------------------------------------
ResourceManager* Device::resource_manager()
{
	return m_resource_manager;
}

//-----------------------------------------------------------------------------
LuaEnvironment* Device::lua_environment()
{
	return m_lua_environment;
}

//-----------------------------------------------------------------------------
OsWindow* Device::window()
{
	return m_window;
}

//-----------------------------------------------------------------------------
Renderer* Device::renderer()
{
	return m_renderer;
}

//-----------------------------------------------------------------------------
Keyboard* Device::keyboard()
{
	return m_keyboard;
}

//-----------------------------------------------------------------------------
Mouse* Device::mouse()
{
	return m_mouse;
}

//-----------------------------------------------------------------------------
Touch* Device::touch()
{
	return m_touch;
}

//-----------------------------------------------------------------------------
Accelerometer* Device::accelerometer()
{
	return NULL;
}

//-----------------------------------------------------------------------------
void Device::start()
{
	CE_ASSERT(m_is_init, "Cannot start uninitialized engine.");

	m_is_running = true;
	m_last_time = os::milliseconds();
}

//-----------------------------------------------------------------------------
void Device::stop()
{
	CE_ASSERT(m_is_init, "Cannot stop uninitialized engine.");

	m_is_running = false;
}

//-----------------------------------------------------------------------------
void Device::pause()
{
	m_is_paused = true;
}

//-----------------------------------------------------------------------------
void Device::unpause()
{
	m_is_paused = false;
}

//-----------------------------------------------------------------------------
bool Device::is_running() const
{
	return m_is_running;
}

//-----------------------------------------------------------------------------
uint64_t Device::frame_count() const
{
	return m_frame_count;
}

//-----------------------------------------------------------------------------
float Device::last_delta_time() const
{
	return m_last_delta_time;
}

//-----------------------------------------------------------------------------
double Device::time_since_start() const
{
	return m_time_since_start;
}

//-----------------------------------------------------------------------------
void Device::frame()
{
	m_current_time = os::microseconds();
	m_last_delta_time = (m_current_time - m_last_time) / 1000000.0f;
	m_last_time = m_current_time;
	m_time_since_start += m_last_delta_time;

	m_console->update();

	if (!m_is_paused)
	{
		m_resource_manager->poll_resource_loader();

		if (!m_lua_environment->call_global("frame", 1, ARGUMENT_FLOAT, last_delta_time()))
		{
			pause();
		}

		m_renderer->frame();
	}

	lua_system::clear_temporaries();

	m_frame_count++;
}

//-----------------------------------------------------------------------------
void Device::update_world(World* world, float dt)
{
	world->update(dt);
}

//-----------------------------------------------------------------------------
void Device::render_world(World* world, Camera* camera)
{
	world->render(camera);
}

//-----------------------------------------------------------------------------
WorldId Device::create_world()
{
	return m_world_manager->create_world();
}

//-----------------------------------------------------------------------------
void Device::destroy_world(WorldId world)
{
	m_world_manager->destroy_world(world);
}

//-----------------------------------------------------------------------------
ResourcePackage* Device::create_resource_package(const char* name)
{
	CE_ASSERT_NOT_NULL(name);

	ResourceId package_id = m_resource_manager->load("package", name);
	m_resource_manager->flush();

	PackageResource* package_res = (PackageResource*) m_resource_manager->data(package_id);
	ResourcePackage* package = CE_NEW(default_allocator(), ResourcePackage)(*m_resource_manager, package_id, package_res);

	return package;
}

//-----------------------------------------------------------------------------
void Device::destroy_resource_package(ResourcePackage* package)
{
	CE_ASSERT_NOT_NULL(package);

	m_resource_manager->unload(package->resource_id());
	CE_DELETE(default_allocator(), package);
}

//-----------------------------------------------------------------------------
void Device::compile(const char* , const char* , const char* )
{
}

//-----------------------------------------------------------------------------
void Device::reload(const char* type, const char* name)
{
	#if defined(LINUX) || defined(WINDOWS)
		TempAllocator4096 temp;
		DynamicString filename(temp);
		filename += name;
		filename += '.';
		filename += type;

		if (!m_bundle_compiler->compile(m_bundle_dir, m_source_dir, filename.c_str()))
		{
			Log::d("Compilation failed.");
			return;
		}

		ResourceId old_res_id = m_resource_manager->resource_id(type, name);
		const void* old_res = m_resource_manager->data(old_res_id);
		m_resource_manager->unload(old_res_id, true);

		ResourceId res_id = m_resource_manager->load(type, name);
		m_resource_manager->flush();
		const void* new_res = m_resource_manager->data(res_id);

		uint32_t type_hash = hash::murmur2_32(type, string::strlen(type), 0);

		switch (type_hash)
		{
			case UNIT_TYPE:
			{
				Log::d("Reloading unit: %s", name);
				/// Reload unit in all worlds
				for (uint32_t i = 0; i < m_world_manager->worlds().size(); i++)
				{
					m_world_manager->worlds()[i]->reload_units((UnitResource*) old_res, (UnitResource*) new_res);
				}
				break;
			}
			case SOUND_TYPE:
			{
				Log::d("Reloading sound: %s", name);
				for (uint32_t i = 0; i < m_world_manager->worlds().size(); i++)
				{
					m_world_manager->worlds()[i]->sound_world()->reload_sounds((SoundResource*) old_res, (SoundResource*) new_res);
				}
				break;
			}
			default:
			{
				CE_ASSERT(false, "Oops, unknown resource type: %s", type);
				break;
			}
		}
	#endif
}

static Device* g_device;
void set_device(Device* device)
{
	g_device = device;
}

Device* device()
{
	return g_device;
}

} // namespace crown
<commit_msg>Update Device<commit_after>/*
Copyright (c) 2013 Daniele Bartolini, Michele Rossi
Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
OTHER DEALINGS IN THE SOFTWARE.
*/

#include <cstdlib>

#include "Config.h"
#include "Device.h"
#include "Accelerometer.h"
#include "Args.h"
#include "DiskFile.h"
#include "DiskFilesystem.h"
#include "JSONParser.h"
#include "Keyboard.h"
#include "Log.h"
#include "LuaEnvironment.h"
#include "Memory.h"
#include "Mouse.h"
#include "OS.h"
#include "OsWindow.h"
#include "Renderer.h"
#include "ResourceManager.h"
#include "StringSetting.h"
#include "StringUtils.h"
#include "TextReader.h"
#include "Touch.h"
#include "Types.h"
#include "Bundle.h"
#include "TempAllocator.h"
#include "ResourcePackage.h"
#include "ConsoleServer.h"
#include "World.h"
#include "LuaStack.h"
#include "WorldManager.h"
#include "NetworkFilesystem.h"
#include "LuaSystem.h"

#if defined(LINUX) || defined(WINDOWS)
	#include "BundleCompiler.h"
#endif

#if defined(ANDROID)
	#include "ApkFilesystem.h"
#endif

#define MAX_SUBSYSTEMS_HEAP 8 * 1024 * 1024

namespace crown
{
//-----------------------------------------------------------------------------
Device::Device()
	: m_allocator(default_allocator(), MAX_SUBSYSTEMS_HEAP)

	, m_argc(0)
	, m_argv(NULL)

	, m_fileserver(0)
	, m_console_port(10001)

	, m_is_init(false)
	, m_is_running(false)
	, m_is_paused(false)

	, m_frame_count(0)

	, m_last_time(0)
	, m_current_time(0)
	, m_last_delta_time(0.0f)
	, m_time_since_start(0.0)

	, m_filesystem(NULL)
	, m_lua_environment(NULL)
	, m_renderer(NULL)

	, m_bundle_compiler(NULL)
	, m_console(NULL)
	, m_resource_manager(NULL)
	, m_resource_bundle(NULL)

	, m_world_manager(NULL)
{
	// Bundle dir is current dir by default.
	string::strncpy(m_bundle_dir, os::get_cwd(), MAX_PATH_LENGTH);
	string::strncpy(m_source_dir, "", MAX_PATH_LENGTH);
	string::strncpy(m_boot_file, "lua/game", MAX_PATH_LENGTH);
}

//-----------------------------------------------------------------------------
Device::~Device()
{
}

//-----------------------------------------------------------------------------
void Device::init()
{
	// Initialize
	Log::i("Initializing Crown Engine %d.%d.%d...", CROWN_VERSION_MAJOR, CROWN_VERSION_MINOR, CROWN_VERSION_MICRO);

	// RPC only in debug or development builds
	#if defined(CROWN_DEBUG) || defined(CROWN_DEVELOPMENT)
		m_console = CE_NEW(m_allocator, ConsoleServer)(m_console_port);
		m_console->init(false);
	#endif

	Log::d("Creating filesystem...");
	// Default bundle filesystem
	#if defined (LINUX) || defined(WINDOWS)
		if (m_fileserver == 1)
		{
			m_filesystem = CE_NEW(m_allocator, NetworkFilesystem)(NetAddress(127, 0, 0, 1), 10001);
		}
		else
		{
			m_filesystem = CE_NEW(m_allocator, DiskFilesystem)(m_bundle_dir);
		}
	#elif defined(ANDROID)
		if (m_fileserver == 1)
		{
			m_filesystem = CE_NEW(m_allocator, NetworkFilesystem)(NetAddress(192, 168, 0, 7), 10001);
		}
		else
		{
			m_filesystem = CE_NEW(m_allocator, ApkFilesystem)();
		}
	#endif

	m_resource_bundle = Bundle::create(m_allocator, *m_filesystem);

	// Create resource manager
	Log::d("Creating resource manager...");
	m_resource_manager = CE_NEW(m_allocator, ResourceManager)(*m_resource_bundle, 0);
	Log::d("Resource seed: %d", m_resource_manager->seed());

	// Create world manager
	Log::d("Creating world manager...");
	m_world_manager = CE_NEW(m_allocator, WorldManager)();

	// Create window
	Log::d("Creating main window...");
	m_window = CE_NEW(m_allocator, OsWindow);

	// Create input devices
	m_keyboard = CE_NEW(m_allocator, Keyboard);
	m_mouse = CE_NEW(m_allocator, Mouse);
	m_touch = CE_NEW(m_allocator, Touch);

	// Create renderer
	Log::d("Creating renderer...");
	m_renderer = CE_NEW(m_allocator, Renderer)(m_allocator);
	m_renderer->init();

	Log::d("Creating lua system...");
	lua_system::init();
	m_lua_environment = CE_NEW(m_allocator, LuaEnvironment)(lua_system::state());

	Log::d("Creating physics...");
	physics_system::init();

	Log::d("Creating audio...");
	audio_system::init();

	Log::d("Crown Engine initialized.");
	Log::d("Initializing Game...");

	m_physics_config = m_resource_manager->load(PHYSICS_CONFIG_EXTENSION, "global");
	m_resource_manager->flush();

	m_is_init = true;
	start();

	// Execute lua boot file
	m_lua_environment->load_and_execute(m_boot_file);
	m_lua_environment->call_global("init", 0);

	Log::d("Total allocated size: %ld", m_allocator.allocated_size());
}

//-----------------------------------------------------------------------------
void Device::shutdown()
{
	CE_ASSERT(is_init(), "Engine is not initialized");

	// Shutdowns the game
	m_lua_environment->call_global("shutdown", 0);

	m_resource_manager->unload(m_physics_config);

	Log::d("Releasing audio...");
	audio_system::shutdown();

	Log::d("Releasing physics...");
	physics_system::shutdown();

	Log::d("Releasing lua system...");
	lua_system::shutdown();
	if (m_lua_environment)
	{
		CE_DELETE(m_allocator, m_lua_environment);
	}

	Log::d("Releasing input devices...");
	CE_DELETE(m_allocator, m_touch);
	CE_DELETE(m_allocator, m_mouse);
	CE_DELETE(m_allocator, m_keyboard);

	Log::d("Releasing renderer...");
	if (m_renderer)
	{
		m_renderer->shutdown();
		CE_DELETE(m_allocator, m_renderer);
	}

	Log::d("Releasing world manager...");
	CE_DELETE(m_allocator, m_world_manager);

	Log::d("Releasing resource manager...");
	if (m_resource_manager)
	{
		CE_DELETE(m_allocator, m_resource_manager);
	}

	if (m_resource_bundle)
	{
		Bundle::destroy(m_allocator, m_resource_bundle);
	}

	Log::d("Releasing filesystem...");
	if (m_filesystem)
	{
		CE_DELETE(m_allocator, m_filesystem);
	}

	#if defined(CROWN_DEBUG) || defined(CROWN_DEVELOPMENT)
		m_console->shutdown();
		CE_DELETE(m_allocator, m_console);
		m_console = NULL;
	#endif

	m_allocator.clear();
	m_is_init = false;
}

//-----------------------------------------------------------------------------
bool Device::is_init() const
{
	return m_is_init;
}

//-----------------------------------------------------------------------------
bool Device::is_paused() const
{
	return m_is_paused;
}

//-----------------------------------------------------------------------------
Filesystem* Device::filesystem()
{
	return m_filesystem;
}

//-----------------------------------------------------------------------------
ResourceManager* Device::resource_manager()
{
	return m_resource_manager;
}

//-----------------------------------------------------------------------------
LuaEnvironment* Device::lua_environment()
{
	return m_lua_environment;
}

//-----------------------------------------------------------------------------
OsWindow* Device::window()
{
	return m_window;
}

//-----------------------------------------------------------------------------
Renderer* Device::renderer()
{
	return m_renderer;
}

//-----------------------------------------------------------------------------
Keyboard* Device::keyboard()
{
	return m_keyboard;
}

//-----------------------------------------------------------------------------
Mouse* Device::mouse()
{
	return m_mouse;
}

//-----------------------------------------------------------------------------
Touch* Device::touch()
{
	return m_touch;
}

//-----------------------------------------------------------------------------
Accelerometer* Device::accelerometer()
{
	return NULL;
}

//-----------------------------------------------------------------------------
void Device::start()
{
	CE_ASSERT(m_is_init, "Cannot start uninitialized engine.");

	m_is_running = true;
	m_last_time = os::milliseconds();
}

//-----------------------------------------------------------------------------
void Device::stop()
{
	CE_ASSERT(m_is_init, "Cannot stop uninitialized engine.");

	m_is_running = false;
}

//-----------------------------------------------------------------------------
void Device::pause()
{
	m_is_paused = true;
}

//-----------------------------------------------------------------------------
void Device::unpause()
{
	m_is_paused = false;
}

//-----------------------------------------------------------------------------
bool Device::is_running() const
{
	return m_is_running;
}

//-----------------------------------------------------------------------------
uint64_t Device::frame_count() const
{
	return m_frame_count;
}

//-----------------------------------------------------------------------------
float Device::last_delta_time() const
{
	return m_last_delta_time;
}

//-----------------------------------------------------------------------------
double Device::time_since_start() const
{
	return m_time_since_start;
}

//-----------------------------------------------------------------------------
void Device::frame()
{
	m_current_time = os::microseconds();
	m_last_delta_time = (m_current_time - m_last_time) / 1000000.0f;
	m_last_time = m_current_time;
	m_time_since_start += m_last_delta_time;

	m_console->update();

	if (!m_is_paused)
	{
		m_resource_manager->poll_resource_loader();

		m_lua_environment->call_global("frame", 1, ARGUMENT_FLOAT, last_delta_time());

		m_renderer->frame();
	}

	lua_system::clear_temporaries();

	m_frame_count++;
}

//-----------------------------------------------------------------------------
void Device::update_world(World* world, float dt)
{
	world->update(dt);
}

//-----------------------------------------------------------------------------
void Device::render_world(World* world, Camera* camera)
{
	world->render(camera);
}

//-----------------------------------------------------------------------------
WorldId Device::create_world()
{
	return m_world_manager->create_world();
}

//-----------------------------------------------------------------------------
void Device::destroy_world(WorldId world)
{
	m_world_manager->destroy_world(world);
}

//-----------------------------------------------------------------------------
ResourcePackage* Device::create_resource_package(const char* name)
{
	CE_ASSERT_NOT_NULL(name);

	ResourceId package_id = m_resource_manager->load("package", name);
	m_resource_manager->flush();

	PackageResource* package_res = (PackageResource*) m_resource_manager->data(package_id);
	ResourcePackage* package = CE_NEW(default_allocator(), ResourcePackage)(*m_resource_manager, package_id, package_res);

	return package;
}

//-----------------------------------------------------------------------------
void Device::destroy_resource_package(ResourcePackage* package)
{
	CE_ASSERT_NOT_NULL(package);

	m_resource_manager->unload(package->resource_id());
	CE_DELETE(default_allocator(), package);
}

//-----------------------------------------------------------------------------
void Device::compile(const char* , const char* , const char* )
{
}

//-----------------------------------------------------------------------------
void Device::reload(const char* type, const char* name)
{
	#if defined(LINUX) || defined(WINDOWS)
		TempAllocator4096 temp;
		DynamicString filename(temp);
		filename += name;
		filename += '.';
		filename += type;

		if (!m_bundle_compiler->compile(m_bundle_dir, m_source_dir, filename.c_str()))
		{
			Log::d("Compilation failed.");
			return;
		}

		ResourceId old_res_id = m_resource_manager->resource_id(type, name);
		const void* old_res = m_resource_manager->data(old_res_id);
		m_resource_manager->unload(old_res_id, true);

		ResourceId res_id = m_resource_manager->load(type, name);
		m_resource_manager->flush();
		const void* new_res = m_resource_manager->data(res_id);

		uint32_t type_hash = hash::murmur2_32(type, string::strlen(type), 0);

		switch (type_hash)
		{
			case UNIT_TYPE:
			{
				Log::d("Reloading unit: %s", name);
				/// Reload unit in all worlds
				for (uint32_t i = 0; i < m_world_manager->worlds().size(); i++)
				{
					m_world_manager->worlds()[i]->reload_units((UnitResource*) old_res, (UnitResource*) new_res);
				}
				break;
			}
			case SOUND_TYPE:
			{
				Log::d("Reloading sound: %s", name);
				for (uint32_t i = 0; i < m_world_manager->worlds().size(); i++)
				{
					m_world_manager->worlds()[i]->sound_world()->reload_sounds((SoundResource*) old_res, (SoundResource*) new_res);
				}
				break;
			}
			default:
			{
				CE_ASSERT(false, "Oops, unknown resource type: %s", type);
				break;
			}
		}
	#endif
}

static Device* g_device;
void set_device(Device* device)
{
	g_device = device;
}

Device* device()
{
	return g_device;
}

} // namespace crown
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_UNARY_EXPR_HPP
#define ETL_UNARY_EXPR_HPP

#include <iostream>     //For stream support

#include "cpp_utils/assert.hpp"

#include "traits_lite.hpp"
#include "iterator.hpp"

// CRTP classes
#include "crtp/comparable.hpp"
#include "crtp/iterable.hpp"
#include "crtp/inplace_assignable.hpp"

namespace etl {

struct identity_op;
struct virtual_op;

template <typename T, typename Expr, typename UnaryOp>
struct unary_expr final : comparable<unary_expr<T, Expr, UnaryOp>>, iterable<unary_expr<T, Expr, UnaryOp>> {
private:
    static_assert(is_etl_expr<Expr>::value, "Only ETL expressions can be used in unary_expr");

    using this_type = unary_expr<T, Expr, UnaryOp>;

    Expr _value;

public:
    using        value_type = T;
    using       memory_type = void;
    using const_memory_type = void;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Expression are invariant
    unary_expr& operator=(const unary_expr& rhs) = delete;
    unary_expr& operator=(unary_expr&& rhs) = delete;

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return UnaryOp::apply(value()[i]);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, value_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return UnaryOp::apply(value()(args...));
    }

    iterator<const this_type> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type> end() const noexcept {
        return {*this, size(*this)};
    }
};

template <typename T, typename Expr>
struct unary_expr<T, Expr, identity_op> : inplace_assignable<unary_expr<T, Expr, identity_op>>, comparable<unary_expr<T, Expr, identity_op>>, iterable<unary_expr<T, Expr, identity_op>>  {
private:
    static_assert(is_etl_expr<Expr>::value, "Only ETL expressions can be used in unary_expr");

    using this_type = unary_expr<T, Expr, identity_op>;

    Expr _value;

    static constexpr const bool non_const_return_ref =
        cpp::and_c<
            std::is_lvalue_reference<decltype(_value[0])>,
            cpp::not_c<std::is_const<std::remove_reference_t<decltype(_value[0])>>>
        >::value;

    static constexpr const bool const_return_ref =
        std::is_lvalue_reference<decltype(_value[0])>::value;

public:
    using        value_type = T;
    using       memory_type = memory_t<Expr>;
    using const_memory_type = std::add_const_t<memory_t<Expr>>;
    using       return_type = std::conditional_t<non_const_return_ref, value_type&, value_type>;
    using const_return_type = std::conditional_t<const_return_ref, const value_type&, value_type>;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Assign expressions to the unary expr

    template<typename E, cpp::enable_if_all_u<non_const_return_ref, is_copy_expr<E>::value> = cpp::detail::dummy>
    unary_expr& operator=(E&& e){
        ensure_same_size(*this, e);
        assign_evaluate(std::forward<E>(e), *this);
        return *this;
    }

    template<bool B = non_const_return_ref, cpp::enable_if_u<B> = cpp::detail::dummy>
    unary_expr& operator=(const value_type& e){
        for(std::size_t i = 0; i < size(*this); ++i){
            (*this)[i] = e;
        }

        return *this;
    }

    template<typename Container, cpp::enable_if_all_c<cpp::not_c<is_etl_expr<Container>>, std::is_convertible<typename Container::value_type, value_type>> = cpp::detail::dummy>
    unary_expr& operator=(const Container& vec){
        cpp_assert(vec.size() == size(*this), "Cannot copy from a vector of different size");

        for(std::size_t i = 0; i < size(*this); ++i){
            (*this)[i] = vec[i];
        }

        return *this;
    }

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    return_type operator[](std::size_t i){
        return value()[i];
    }

    const_return_type operator[](std::size_t i) const {
        return value()[i];
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    auto operator()(std::size_t i){
        return sub(*this, i);
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    auto operator()(std::size_t i) const {
        return sub(*this, i);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, return_type> operator()(S... args){
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, const_return_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    iterator<this_type, non_const_return_ref, false> begin() noexcept {
        return {*this, 0};
    }

    iterator<this_type, non_const_return_ref, false> end() noexcept {
        return {*this, size(*this)};
    }

    iterator<const this_type, true> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type, true> end() const noexcept {
        return {*this, size(*this)};
    }

    //{{{ Direct memory access

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    memory_type memory_start() noexcept {
        return value().memory_start();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    const_memory_type memory_start() const noexcept {
        return value().memory_start();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    memory_type memory_end() noexcept {
        return value().memory_end();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    const_memory_type memory_end() const noexcept {
        return value().memory_end();
    }

    //}}}
};

template <typename T, typename Expr>
struct unary_expr<T, Expr, virtual_op> : comparable<unary_expr<T, Expr, virtual_op>>, iterable<unary_expr<T, Expr, virtual_op>>  {
private:
    using this_type = unary_expr<T, Expr, virtual_op>;

    Expr _value;

public:
    using        value_type = T;
    using       memory_type = void;
    using const_memory_type = void;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Expression are invariant
    unary_expr& operator=(const unary_expr&) = delete;
    unary_expr& operator=(unary_expr&&) = delete;

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return value()[i];
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    value_type operator()(std::size_t i) const {
        return sub(*this, i);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, value_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    iterator<const this_type, false, false> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type, false, false> end() const noexcept {
        return {*this, size(*this)};
    }
};

template <typename T, typename Expr>
std::ostream& operator<<(std::ostream& os, const unary_expr<T, Expr, identity_op>& expr){
    return os << expr.value();
}

template <typename T, typename Expr, typename UnaryOp>
std::ostream& operator<<(std::ostream& os, const unary_expr<T, Expr, UnaryOp>& expr){
    return os << UnaryOp::desc() << '(' << expr.value() << ')';
}

} //end of namespace etl

#endif
<commit_msg>Assign generator to unary_expr<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_UNARY_EXPR_HPP
#define ETL_UNARY_EXPR_HPP

#include <iostream>     //For stream support

#include "cpp_utils/assert.hpp"

#include "traits_lite.hpp"
#include "iterator.hpp"

// CRTP classes
#include "crtp/comparable.hpp"
#include "crtp/iterable.hpp"
#include "crtp/inplace_assignable.hpp"

namespace etl {

struct identity_op;
struct virtual_op;

template <typename Generator>
class generator_expr;

template <typename T, typename Expr, typename UnaryOp>
struct unary_expr final : comparable<unary_expr<T, Expr, UnaryOp>>, iterable<unary_expr<T, Expr, UnaryOp>> {
private:
    static_assert(is_etl_expr<Expr>::value, "Only ETL expressions can be used in unary_expr");

    using this_type = unary_expr<T, Expr, UnaryOp>;

    Expr _value;

public:
    using        value_type = T;
    using       memory_type = void;
    using const_memory_type = void;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Expression are invariant
    unary_expr& operator=(const unary_expr& rhs) = delete;
    unary_expr& operator=(unary_expr&& rhs) = delete;

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return UnaryOp::apply(value()[i]);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, value_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return UnaryOp::apply(value()(args...));
    }

    iterator<const this_type> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type> end() const noexcept {
        return {*this, size(*this)};
    }
};

template <typename T, typename Expr>
struct unary_expr<T, Expr, identity_op> : inplace_assignable<unary_expr<T, Expr, identity_op>>, comparable<unary_expr<T, Expr, identity_op>>, iterable<unary_expr<T, Expr, identity_op>>  {
private:
    static_assert(is_etl_expr<Expr>::value, "Only ETL expressions can be used in unary_expr");

    using this_type = unary_expr<T, Expr, identity_op>;

    Expr _value;

    static constexpr const bool non_const_return_ref =
        cpp::and_c<
            std::is_lvalue_reference<decltype(_value[0])>,
            cpp::not_c<std::is_const<std::remove_reference_t<decltype(_value[0])>>>
        >::value;

    static constexpr const bool const_return_ref =
        std::is_lvalue_reference<decltype(_value[0])>::value;

public:
    using        value_type = T;
    using       memory_type = memory_t<Expr>;
    using const_memory_type = std::add_const_t<memory_t<Expr>>;
    using       return_type = std::conditional_t<non_const_return_ref, value_type&, value_type>;
    using const_return_type = std::conditional_t<const_return_ref, const value_type&, value_type>;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Assign expressions to the unary expr

    template<typename E, cpp::enable_if_all_u<non_const_return_ref, is_copy_expr<E>::value> = cpp::detail::dummy>
    unary_expr& operator=(E&& e){
        ensure_same_size(*this, e);
        assign_evaluate(std::forward<E>(e), *this);
        return *this;
    }

    template<typename Generator>
    unary_expr& operator=(generator_expr<Generator>&& e){
        assign_evaluate(e, *this);
        return *this;
    }

    template<bool B = non_const_return_ref, cpp::enable_if_u<B> = cpp::detail::dummy>
    unary_expr& operator=(const value_type& e){
        for(std::size_t i = 0; i < size(*this); ++i){
            (*this)[i] = e;
        }

        return *this;
    }

    template<typename Container, cpp::enable_if_all_c<cpp::not_c<is_etl_expr<Container>>, std::is_convertible<typename Container::value_type, value_type>> = cpp::detail::dummy>
    unary_expr& operator=(const Container& vec){
        cpp_assert(vec.size() == size(*this), "Cannot copy from a vector of different size");

        for(std::size_t i = 0; i < size(*this); ++i){
            (*this)[i] = vec[i];
        }

        return *this;
    }

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    return_type operator[](std::size_t i){
        return value()[i];
    }

    const_return_type operator[](std::size_t i) const {
        return value()[i];
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    auto operator()(std::size_t i){
        return sub(*this, i);
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    auto operator()(std::size_t i) const {
        return sub(*this, i);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, return_type> operator()(S... args){
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, const_return_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    iterator<this_type, non_const_return_ref, false> begin() noexcept {
        return {*this, 0};
    }

    iterator<this_type, non_const_return_ref, false> end() noexcept {
        return {*this, size(*this)};
    }

    iterator<const this_type, true> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type, true> end() const noexcept {
        return {*this, size(*this)};
    }

    //{{{ Direct memory access

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    memory_type memory_start() noexcept {
        return value().memory_start();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    const_memory_type memory_start() const noexcept {
        return value().memory_start();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    memory_type memory_end() noexcept {
        return value().memory_end();
    }

    template<typename SS = Expr, cpp_enable_if(has_direct_access<SS>::value)>
    const_memory_type memory_end() const noexcept {
        return value().memory_end();
    }

    //}}}
};

template <typename T, typename Expr>
struct unary_expr<T, Expr, virtual_op> : comparable<unary_expr<T, Expr, virtual_op>>, iterable<unary_expr<T, Expr, virtual_op>>  {
private:
    using this_type = unary_expr<T, Expr, virtual_op>;

    Expr _value;

public:
    using        value_type = T;
    using       memory_type = void;
    using const_memory_type = void;

    //Cannot be constructed with no args
    unary_expr() = delete;

    //Construct a new expression
    unary_expr(Expr l) : _value(std::forward<Expr>(l)){
        //Nothing else to init
    }

    unary_expr(const unary_expr& rhs) = default;
    unary_expr(unary_expr&& rhs) = default;

    //Expression are invariant
    unary_expr& operator=(const unary_expr&) = delete;
    unary_expr& operator=(unary_expr&&) = delete;

    //Accessors

    std::add_lvalue_reference_t<Expr> value() noexcept {
        return _value;
    }

    cpp::add_const_lvalue_t<Expr> value() const noexcept {
        return _value;
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return value()[i];
    }

    template<bool B = (sub_size_compare<this_type>::value > 1), cpp::enable_if_u<B> = cpp::detail::dummy>
    value_type operator()(std::size_t i) const {
        return sub(*this, i);
    }

    template<typename... S>
    std::enable_if_t<sizeof...(S) == sub_size_compare<this_type>::value, value_type> operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return value()(args...);
    }

    iterator<const this_type, false, false> begin() const noexcept {
        return {*this, 0};
    }

    iterator<const this_type, false, false> end() const noexcept {
        return {*this, size(*this)};
    }
};

template <typename T, typename Expr>
std::ostream& operator<<(std::ostream& os, const unary_expr<T, Expr, identity_op>& expr){
    return os << expr.value();
}

template <typename T, typename Expr, typename UnaryOp>
std::ostream& operator<<(std::ostream& os, const unary_expr<T, Expr, UnaryOp>& expr){
    return os << UnaryOp::desc() << '(' << expr.value() << ')';
}

} //end of namespace etl

#endif
<|endoftext|>