<commit_before>
//
// ===============================================================================
// clReflect
// -------------------------------------------------------------------------------
// Copyright (c) 2011 Don Williamson
//
// 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.
// ===============================================================================
//

#include <clcpp/Database.h>
#include "DatabaseLoader.h"

namespace
{
	unsigned int GetNameHash(clcpp::Name name)
	{
		return name.hash;
	}
	unsigned int GetPrimitiveHash(const clcpp::Primitive& primitive)
	{
		return primitive.name.hash;
	}
	unsigned int GetPrimitivePtrHash(const clcpp::Primitive* primitive)
	{
		return primitive->name.hash;
	}

	template <typename ARRAY_TYPE, typename COMPARE_L_TYPE, unsigned int (GET_HASH_FUNC)(COMPARE_L_TYPE)>
	int BinarySearch(const clcpp::CArray<ARRAY_TYPE>& entries, unsigned int compare_hash)
	{
		int first = 0;
		int last = entries.size() - 1;

		// Binary search
		while (first <= last)
		{
			// Identify the mid point
			int mid = (first + last) / 2;

			unsigned entry_hash = GET_HASH_FUNC(entries[mid]);
			if (compare_hash > entry_hash)
			{
				// Shift search to local upper half
				first = mid + 1;
			}
			else if (compare_hash < entry_hash)
			{
				// Shift search to local lower half
				last = mid - 1;
			}
			else
			{
				// Exact match found
				return mid;
			}
		}

		return -1;
	}

	template <typename ARRAY_TYPE, typename COMPARE_L_TYPE, unsigned int (GET_HASH_FUNC)(COMPARE_L_TYPE)>
	clcpp::Range SearchNeighbours(const clcpp::CArray<ARRAY_TYPE>& entries, unsigned int compare_hash, int index)
	{
		clcpp::Range range;
		range.first = index;
		range.last = index + 1;

		// Search either side of the result, gathering further matches
		while (range.first > 0 && GET_HASH_FUNC(entries[range.first - 1]) == compare_hash)
			range.first--;
		while (range.last < entries.size() && GET_HASH_FUNC(entries[range.last]) == compare_hash)
			range.last++;

		return range;
	}

    void RebaseFunctions(clcpp::internal::DatabaseMem& dbmem, clcpp::pointer_type base_address)
	{
		// Move all function addresses from their current location to their new location
		for (int i = 0; i < dbmem.functions.size(); i++)
		{
			clcpp::Function& f = dbmem.functions[i];
			if (f.address)
				f.address = f.address - dbmem.function_base_address + base_address;
		}

		// Do the same for the GetType family of functions
		for (int i = 0; i < dbmem.get_type_functions.size(); i++)
		{
			clcpp::internal::GetTypeFunctions& f = dbmem.get_type_functions[i];
			if (f.get_typename_address)
				f.get_typename_address = f.get_typename_address - dbmem.function_base_address + base_address;
			if (f.get_type_address)
				f.get_type_address = f.get_type_address - dbmem.function_base_address + base_address;
		}
	}

#if defined(CLCPP_USING_GNUC)

    // GCC specific patch functions
    template<typename T>
    void GccPatchFunction(clcpp::pointer_type function_address,
        const T& patch_value, const T& original_value)
    {
        unsigned char* function_pointer = (unsigned char*) function_address;

        // searches for 0x20 instruction headers at most
        for (int i = 0; i < 0x20; i++)
        {
#if defined(CLCPP_USING_64_BIT)
            // 64 bit patch starts here
            if (function_pointer[i] == 0x8B) {
                // MOV instruction
                // this may be what we are looking for
                clcpp::uint32 target_offset = *((clcpp::uint32*) &function_pointer[i + 2]);
                T* target_ptr = (T*) (&function_pointer[i + 6] + target_offset);

                // although we get the real target address here, we still cannot
                // say immediately if this is our target pointer to patch. On
                // Mac OS X, this pointer would first point to a stub, which
                // then points to the real pointer, so we may have two levels
                // of pointers here. In this case, we would test for both cases.
                if (*target_ptr == original_value) {
                    // there's no stubs, this is the pointer we are looking for
                    *target_ptr = patch_value;
                    return;
                }

#if defined(CLCPP_USING_GNUC_MAC)
                // we test for stub pointer on mac
                // TODO: check if other compiler has the same behaviour
                T* target_stub = *((T**) target_ptr);
                if (*target_stub == original_value) {
                    *target_stub = patch_value;
                    return;
                }
#endif // CLCPP_USING_GNUC_MAC

            }
            // 64 bit patch ends here
#else
            // 32 bit patch starts here
            if (function_pointer[i] == 0xA1) {
                T* target_addr = *((T**) (&function_pointer[i + 1]));

                if (*target_addr == original_value) {
                    *target_addr = patch_value;
                    return;
                }
            }
            // 32 bit patch ends here
#endif // CLCPP_USING_64_BIT
        }
        // TODO: we may want to add error raising code here since we failed to do the patch
        return;
    }

#endif // CLCPP_USING_GNUC

	void PatchGetTypeAddresses(clcpp::Database& db, clcpp::internal::DatabaseMem& dbmem)
	{
        unsigned int original_hash = CLCPP_INVALID_HASH;
        const clcpp::Type* original_type = (const clcpp::Type*) CLCPP_INVALID_ADDRESS;

		for (int i = 0; i < dbmem.get_type_functions.size(); i++)
		{
			clcpp::internal::GetTypeFunctions& f = dbmem.get_type_functions[i];

			// Patch up the type name hash static variable
			if (f.get_typename_address)
			{
                unsigned int hash = db.GetName(f.type_hash).hash;

#if defined(CLCPP_USING_MSVC)
				unsigned char* mov_instruction = (unsigned char*)f.get_typename_address;
				clcpp::internal::Assert(*mov_instruction == 0xA1);
                unsigned int* hash_address = *(unsigned int**)(mov_instruction + 1);
				*hash_address = hash;
#endif // CLCPP_USING_MSVC

#if defined(CLCPP_USING_GNUC)
                GccPatchFunction<unsigned int>(f.get_typename_address,
                    hash,
                    original_hash);
#endif // CLCPP_USING_GNUC
			}

			// Patch up the type pointer static variable
			if (f.get_type_address)
			{
                const clcpp::Type* type = db.GetType(f.type_hash);

#if defined(CLCPP_USING_MSVC)
				unsigned char* mov_instruction = (unsigned char*)f.get_type_address;
				clcpp::internal::Assert(*mov_instruction == 0xA1);
				const clcpp::Type** type_address = *(const clcpp::Type***)(mov_instruction + 1);
				*type_address = type;
#endif // CLCPP_USING_MSVC

#if defined(CLCPP_USING_GNUC)
                GccPatchFunction<const clcpp::Type*>(f.get_type_address,
                    type,
                    original_type);
#endif // CLCPP_USING_GNUC
			}
		}
	}

	template <typename TYPE>
	void ParentPrimitivesToDatabase(clcpp::CArray<TYPE>& primitives, clcpp::Database* database)
	{
		for (int i = 0; i < primitives.size(); i++)
			primitives[i].database = database;
	}
}

const clcpp::Primitive* clcpp::internal::FindPrimitive(const CArray<const Primitive*>& primitives, unsigned int hash)
{
	int index = BinarySearch<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash);
	if (index == -1)
		return 0;
	return primitives[index];
}

clcpp::Range clcpp::internal::FindOverloadedPrimitive(const CArray<const Primitive*>& primitives, unsigned int hash)
{
	// Search for the first entry
	int index = BinarySearch<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash);
	if (index == -1)
		return Range();

	// Look at its neighbours to widen the primitives found
	return SearchNeighbours<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash, index);
}

clcpp::Database::Database()
	: m_DatabaseMem(0)
	, m_Allocator(0)
{
}

clcpp::Database::~Database()
{
	if (m_DatabaseMem)
		m_Allocator->Free(m_DatabaseMem);
}

bool clcpp::Database::Load(IFile* file, IAllocator* allocator, clcpp::pointer_type base_address, unsigned int options)
{
	// Load the database
	internal::Assert(m_DatabaseMem == 0 && "Database already loaded");
	m_Allocator = allocator;
	m_DatabaseMem = internal::LoadMemoryMappedDatabase(file, m_Allocator);

	if (m_DatabaseMem != 0)
	{
		// If no base address is provided, rebasing will not occur and it is assumed the addresses
		// loaded are already correct. Rebasing is usually only needed for DLLs that have been moved
		// by the OS due to another DLL occupying its preferred load address.
		if (base_address != 0)
			RebaseFunctions(*m_DatabaseMem, base_address);

		if ((options & OPT_DONT_PATCH_GETTYPE) == 0)
			PatchGetTypeAddresses(*this, *m_DatabaseMem);

		// Tell each loaded primitive that they belong to this database
		ParentPrimitivesToDatabase(m_DatabaseMem->types, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->enum_constants, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->enums, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->fields, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->functions, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->classes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->templates, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->template_types, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->namespaces, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->flag_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->int_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->float_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->primitive_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->text_attributes, this);
	}

	return m_DatabaseMem != 0;
}

clcpp::Name clcpp::Database::GetName(unsigned int hash) const
{
	// Lookup the name by hash
	int index = BinarySearch<Name, Name, GetNameHash>(m_DatabaseMem->names, hash);
	if (index == -1)
		return clcpp::Name();
	return m_DatabaseMem->names[index];
}

clcpp::Name clcpp::Database::GetName(const char* text) const
{
	// Null pointer
	if (text == 0)
		return clcpp::Name();

	// Hash and exit on no value
	unsigned int hash = internal::HashNameString(text);
	if (hash == 0)
		return clcpp::Name();

	return GetName(hash);
}

const clcpp::Type* clcpp::Database::GetType(unsigned int hash) const
{
	return FindPrimitive(m_DatabaseMem->type_primitives, hash);
}

const clcpp::Namespace* clcpp::Database::GetNamespace(unsigned int hash) const
{
	int index = BinarySearch<Namespace, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->namespaces, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->namespaces[index];
}

const clcpp::Namespace* clcpp::Database::GetGlobalNamespace() const
{
	return &m_DatabaseMem->global_namespace;
}

const clcpp::Template* clcpp::Database::GetTemplate(unsigned int hash) const
{
	int index = BinarySearch<Template, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->templates, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->templates[index];
}

const clcpp::Function* clcpp::Database::GetFunction(unsigned int hash) const
{
	int index = BinarySearch<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->functions[index];
}

clcpp::Range clcpp::Database::GetOverloadedFunction(unsigned int hash) const
{
	// Quickly locate the first match
	int index = BinarySearch<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash);
	if (index == -1)
		return Range();

	// Functions can be overloaded so look at the neighbours to widen the primitives found
	return SearchNeighbours<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash, index);
}
<commit_msg>Fix windows build<commit_after>
//
// ===============================================================================
// clReflect
// -------------------------------------------------------------------------------
// Copyright (c) 2011 Don Williamson
//
// 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.
// ===============================================================================
//

#include <clcpp/Database.h>
#include "DatabaseLoader.h"

namespace
{
	unsigned int GetNameHash(clcpp::Name name)
	{
		return name.hash;
	}
	unsigned int GetPrimitiveHash(const clcpp::Primitive& primitive)
	{
		return primitive.name.hash;
	}
	unsigned int GetPrimitivePtrHash(const clcpp::Primitive* primitive)
	{
		return primitive->name.hash;
	}

	template <typename ARRAY_TYPE, typename COMPARE_L_TYPE, unsigned int (GET_HASH_FUNC)(COMPARE_L_TYPE)>
	int BinarySearch(const clcpp::CArray<ARRAY_TYPE>& entries, unsigned int compare_hash)
	{
		int first = 0;
		int last = entries.size() - 1;

		// Binary search
		while (first <= last)
		{
			// Identify the mid point
			int mid = (first + last) / 2;

			unsigned entry_hash = GET_HASH_FUNC(entries[mid]);
			if (compare_hash > entry_hash)
			{
				// Shift search to local upper half
				first = mid + 1;
			}
			else if (compare_hash < entry_hash)
			{
				// Shift search to local lower half
				last = mid - 1;
			}
			else
			{
				// Exact match found
				return mid;
			}
		}

		return -1;
	}

	template <typename ARRAY_TYPE, typename COMPARE_L_TYPE, unsigned int (GET_HASH_FUNC)(COMPARE_L_TYPE)>
	clcpp::Range SearchNeighbours(const clcpp::CArray<ARRAY_TYPE>& entries, unsigned int compare_hash, int index)
	{
		clcpp::Range range;
		range.first = index;
		range.last = index + 1;

		// Search either side of the result, gathering further matches
		while (range.first > 0 && GET_HASH_FUNC(entries[range.first - 1]) == compare_hash)
			range.first--;
		while (range.last < entries.size() && GET_HASH_FUNC(entries[range.last]) == compare_hash)
			range.last++;

		return range;
	}

    void RebaseFunctions(clcpp::internal::DatabaseMem& dbmem, clcpp::pointer_type base_address)
	{
		// Move all function addresses from their current location to their new location
		for (int i = 0; i < dbmem.functions.size(); i++)
		{
			clcpp::Function& f = dbmem.functions[i];
			if (f.address)
				f.address = f.address - dbmem.function_base_address + base_address;
		}

		// Do the same for the GetType family of functions
		for (int i = 0; i < dbmem.get_type_functions.size(); i++)
		{
			clcpp::internal::GetTypeFunctions& f = dbmem.get_type_functions[i];
			if (f.get_typename_address)
				f.get_typename_address = f.get_typename_address - dbmem.function_base_address + base_address;
			if (f.get_type_address)
				f.get_type_address = f.get_type_address - dbmem.function_base_address + base_address;
		}
	}

#if defined(CLCPP_USING_GNUC)

    // GCC specific patch functions
    template<typename T>
    void GccPatchFunction(clcpp::pointer_type function_address,
        const T& patch_value, const T& original_value)
    {
        unsigned char* function_pointer = (unsigned char*) function_address;

        // searches for 0x20 instruction headers at most
        for (int i = 0; i < 0x20; i++)
        {
#if defined(CLCPP_USING_64_BIT)
            // 64 bit patch starts here
            if (function_pointer[i] == 0x8B) {
                // MOV instruction
                // this may be what we are looking for
                clcpp::uint32 target_offset = *((clcpp::uint32*) &function_pointer[i + 2]);
                T* target_ptr = (T*) (&function_pointer[i + 6] + target_offset);

                // although we get the real target address here, we still cannot
                // say immediately if this is our target pointer to patch. On
                // Mac OS X, this pointer would first point to a stub, which
                // then points to the real pointer, so we may have two levels
                // of pointers here. In this case, we would test for both cases.
                if (*target_ptr == original_value) {
                    // there's no stubs, this is the pointer we are looking for
                    *target_ptr = patch_value;
                    return;
                }

#if defined(CLCPP_USING_GNUC_MAC)
                // we test for stub pointer on mac
                // TODO: check if other compiler has the same behaviour
                T* target_stub = *((T**) target_ptr);
                if (*target_stub == original_value) {
                    *target_stub = patch_value;
                    return;
                }
#endif // CLCPP_USING_GNUC_MAC

            }
            // 64 bit patch ends here
#else
            // 32 bit patch starts here
            if (function_pointer[i] == 0xA1) {
                T* target_addr = *((T**) (&function_pointer[i + 1]));

                if (*target_addr == original_value) {
                    *target_addr = patch_value;
                    return;
                }
            }
            // 32 bit patch ends here
#endif // CLCPP_USING_64_BIT
        }
        // TODO: we may want to add error raising code here since we failed to do the patch
        return;
    }

#endif // CLCPP_USING_GNUC

	void PatchGetTypeAddresses(clcpp::Database& db, clcpp::internal::DatabaseMem& dbmem)
	{
#if defined(CLCPP_USING_GNUC)
        unsigned int original_hash = CLCPP_INVALID_HASH;
        const clcpp::Type* original_type = (const clcpp::Type*) CLCPP_INVALID_ADDRESS;
#endif // CLCPP_USING_GNUC

		for (int i = 0; i < dbmem.get_type_functions.size(); i++)
		{
			clcpp::internal::GetTypeFunctions& f = dbmem.get_type_functions[i];

			// Patch up the type name hash static variable
			if (f.get_typename_address)
			{
                unsigned int hash = db.GetName(f.type_hash).hash;

#if defined(CLCPP_USING_MSVC)
				unsigned char* mov_instruction = (unsigned char*)f.get_typename_address;
				clcpp::internal::Assert(*mov_instruction == 0xA1);
                unsigned int* hash_address = *(unsigned int**)(mov_instruction + 1);
				*hash_address = hash;
#endif // CLCPP_USING_MSVC

#if defined(CLCPP_USING_GNUC)
                GccPatchFunction<unsigned int>(f.get_typename_address,
                    hash,
                    original_hash);
#endif // CLCPP_USING_GNUC
			}

			// Patch up the type pointer static variable
			if (f.get_type_address)
			{
                const clcpp::Type* type = db.GetType(f.type_hash);

#if defined(CLCPP_USING_MSVC)
				unsigned char* mov_instruction = (unsigned char*)f.get_type_address;
				clcpp::internal::Assert(*mov_instruction == 0xA1);
				const clcpp::Type** type_address = *(const clcpp::Type***)(mov_instruction + 1);
				*type_address = type;
#endif // CLCPP_USING_MSVC

#if defined(CLCPP_USING_GNUC)
                GccPatchFunction<const clcpp::Type*>(f.get_type_address,
                    type,
                    original_type);
#endif // CLCPP_USING_GNUC
			}
		}
	}

	template <typename TYPE>
	void ParentPrimitivesToDatabase(clcpp::CArray<TYPE>& primitives, clcpp::Database* database)
	{
		for (int i = 0; i < primitives.size(); i++)
			primitives[i].database = database;
	}
}

const clcpp::Primitive* clcpp::internal::FindPrimitive(const CArray<const Primitive*>& primitives, unsigned int hash)
{
	int index = BinarySearch<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash);
	if (index == -1)
		return 0;
	return primitives[index];
}

clcpp::Range clcpp::internal::FindOverloadedPrimitive(const CArray<const Primitive*>& primitives, unsigned int hash)
{
	// Search for the first entry
	int index = BinarySearch<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash);
	if (index == -1)
		return Range();

	// Look at its neighbours to widen the primitives found
	return SearchNeighbours<const Primitive*, const Primitive*, GetPrimitivePtrHash>(primitives, hash, index);
}

clcpp::Database::Database()
	: m_DatabaseMem(0)
	, m_Allocator(0)
{
}

clcpp::Database::~Database()
{
	if (m_DatabaseMem)
		m_Allocator->Free(m_DatabaseMem);
}

bool clcpp::Database::Load(IFile* file, IAllocator* allocator, clcpp::pointer_type base_address, unsigned int options)
{
	// Load the database
	internal::Assert(m_DatabaseMem == 0 && "Database already loaded");
	m_Allocator = allocator;
	m_DatabaseMem = internal::LoadMemoryMappedDatabase(file, m_Allocator);

	if (m_DatabaseMem != 0)
	{
		// If no base address is provided, rebasing will not occur and it is assumed the addresses
		// loaded are already correct. Rebasing is usually only needed for DLLs that have been moved
		// by the OS due to another DLL occupying its preferred load address.
		if (base_address != 0)
			RebaseFunctions(*m_DatabaseMem, base_address);

		if ((options & OPT_DONT_PATCH_GETTYPE) == 0)
			PatchGetTypeAddresses(*this, *m_DatabaseMem);

		// Tell each loaded primitive that they belong to this database
		ParentPrimitivesToDatabase(m_DatabaseMem->types, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->enum_constants, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->enums, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->fields, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->functions, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->classes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->templates, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->template_types, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->namespaces, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->flag_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->int_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->float_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->primitive_attributes, this);
		ParentPrimitivesToDatabase(m_DatabaseMem->text_attributes, this);
	}

	return m_DatabaseMem != 0;
}

clcpp::Name clcpp::Database::GetName(unsigned int hash) const
{
	// Lookup the name by hash
	int index = BinarySearch<Name, Name, GetNameHash>(m_DatabaseMem->names, hash);
	if (index == -1)
		return clcpp::Name();
	return m_DatabaseMem->names[index];
}

clcpp::Name clcpp::Database::GetName(const char* text) const
{
	// Null pointer
	if (text == 0)
		return clcpp::Name();

	// Hash and exit on no value
	unsigned int hash = internal::HashNameString(text);
	if (hash == 0)
		return clcpp::Name();

	return GetName(hash);
}

const clcpp::Type* clcpp::Database::GetType(unsigned int hash) const
{
	return FindPrimitive(m_DatabaseMem->type_primitives, hash);
}

const clcpp::Namespace* clcpp::Database::GetNamespace(unsigned int hash) const
{
	int index = BinarySearch<Namespace, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->namespaces, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->namespaces[index];
}

const clcpp::Namespace* clcpp::Database::GetGlobalNamespace() const
{
	return &m_DatabaseMem->global_namespace;
}

const clcpp::Template* clcpp::Database::GetTemplate(unsigned int hash) const
{
	int index = BinarySearch<Template, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->templates, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->templates[index];
}

const clcpp::Function* clcpp::Database::GetFunction(unsigned int hash) const
{
	int index = BinarySearch<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash);
	if (index == -1)
		return 0;
	return &m_DatabaseMem->functions[index];
}

clcpp::Range clcpp::Database::GetOverloadedFunction(unsigned int hash) const
{
	// Quickly locate the first match
	int index = BinarySearch<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash);
	if (index == -1)
		return Range();

	// Functions can be overloaded so look at the neighbours to widen the primitives found
	return SearchNeighbours<Function, const Primitive&, GetPrimitiveHash>(m_DatabaseMem->functions, hash, index);
}
<|endoftext|>


<commit_before>
//
// ===============================================================================
// clReflect
// -------------------------------------------------------------------------------
// Copyright (c) 2011-2012 Don Williamson & clReflect Authors (see AUTHORS file)
// Released under MIT License (see LICENSE file)
// ===============================================================================
//

// TODO: Lots of stuff happening in here that needs logging

#include <clutl/Objects.h>

struct clutl::ObjectGroup::HashEntry
{
	HashEntry() : hash(0), object(0) { }
	unsigned int hash;
	Object* object;
};

clutl::Object* clutl::CreateObject(const clcpp::Type *type, unsigned int unique_id, ObjectGroup* object_group)
{
	if (type == 0)
		return 0;

	// Can only create class objects
	if (type->kind != clcpp::Primitive::KIND_CLASS)
		return 0;
	const clcpp::Class* class_type = type->AsClass();

	// The object group has no registered constructor so construct manually
	// if it comes through
	Object* object = 0;
	if (type->name.hash == clcpp::GetTypeNameHash<ObjectGroup>())
	{
		object = new ObjectGroup();
	}
	else
	{
		// Need a constructor to new and a destructor to delete at a later point
		if (class_type->constructor == 0 || class_type->destructor == 0)
			return 0;

		// Allocate and call the constructor
		object = (Object*)new char[type->size];
		typedef void (*CallFunc)(clutl::Object*);
		CallFunc call_func = (CallFunc)class_type->constructor->address;
		call_func(object);
	}

	// Construct the object and add to its object group
	object->type = type;
	object->unique_id = unique_id;
	if (object_group)
		object_group->AddObject(object);

	return object;
}

void clutl::DestroyObject(const Object* object)
{
	// These represent fatal code errors
	clcpp::internal::Assert(object != 0);
	clcpp::internal::Assert(object->type != 0);

	// Remove from any attached object group
	if (object->object_group != 0)
		object->object_group->RemoveObject(object);

	if (object->type->name.hash == clcpp::GetTypeNameHash<ObjectGroup>())
	{
		// ObjecGroup class does not have a registered destructor
		delete (ObjectGroup*)object;
	}

	else
	{
		// Call the destructor and release the memory
		const clcpp::Class* class_type = object->type->AsClass();
		clcpp::internal::Assert(class_type->destructor != 0);
		typedef void (*CallFunc)(const clutl::Object*);
		CallFunc call_func = (CallFunc)class_type->destructor->address;
		call_func(object);
		delete [] (char*)object;
	}
}

clutl::ObjectGroup::ObjectGroup()
	: m_MaxNbObjects(8)
	, m_NbObjects(0)
	, m_NbOccupiedEntries(0)
	, m_NamedObjects(0)
{
	// Allocate the hash table
	m_NamedObjects = new HashEntry[m_MaxNbObjects];
}

clutl::ObjectGroup::~ObjectGroup()
{
	if (m_NamedObjects != 0)
		delete [] m_NamedObjects;
}

void clutl::ObjectGroup::AddObject(Object* object)
{
	object->object_group = this;
	if (object->unique_id != 0)
		AddHashEntry(object);
}

void clutl::ObjectGroup::RemoveObject(const Object* object)
{
	// Remove from the hash table if it's named
	if (object->unique_id != 0)
		RemoveHashEntry(object->unique_id);
}

clutl::Object* clutl::ObjectGroup::FindObject(unsigned int unique_id) const
{
	// Linear probe from the natural hash location for matching hash
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = unique_id & index_mask;
	while (m_NamedObjects[index].hash)
	{
		// Ensure dummy objects are skipped
		if (m_NamedObjects[index].hash == unique_id &&
			m_NamedObjects[index].object != 0)
			break;

		index = (index + 1) & index_mask;
	}

	// Get the object here
	HashEntry& he = m_NamedObjects[index];
	return he.object;
}

clutl::Object* clutl::ObjectGroup::FindObjectSearchParents(unsigned int unique_id) const
{
	// Search up through the object group hierarchy
	const ObjectGroup* group = this;
	Object* object = 0;
	while (group != 0)
	{
		object = group->FindObject(unique_id);
		if (object != 0)
			break;

		group = group->object_group;
	}

	return object;
}

clutl::Object* clutl::ObjectGroup::FindObjectRelative(unsigned int* unique_ids, unsigned int nb_ids) const
{
	// Locate the containing object group
	const ObjectGroup* object_group = this;
	while (nb_ids - 1 > 0)
	{
		Object* object = FindObject(*unique_ids++);
		if (object == 0)
			return 0;

		// Ensure this is an object group
		if (object->type->kind != clcpp::Primitive::KIND_CLASS)
			return 0;
		const clcpp::Class* class_type = (clcpp::Class*)object->type;
		if (!(class_type->flag_attributes & FLAG_ATTR_IS_OBJECT_GROUP))
			return 0;

		object_group = (ObjectGroup*)object;
		nb_ids--;
	}

	return object_group->FindObject(*unique_ids);
}

void clutl::ObjectGroup::AddHashEntry(Object* object)
{
	// Linear probe from the natural hash location for a free slot, reusing any dummy slots
	unsigned int hash = object->unique_id;
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = hash & index_mask;
	while (m_NamedObjects[index].hash && m_NamedObjects[index].object != 0)
		index = (index + 1) & index_mask;

	// Add to the table
	HashEntry& he = m_NamedObjects[index];
	he.hash = hash;
	he.object = object;
	m_NbObjects++;
	m_NbOccupiedEntries++;

	// Resize when load factor is greather than 2/3
	if (m_NbObjects > (m_MaxNbObjects * 2) / 3)
		Resize(true);

	// Or flush dummy objects so that there is always at least on empty slot
	// This is required for the FindObject loop to terminate when an object can't be find
	else if (m_NbOccupiedEntries == m_MaxNbObjects)
		Resize(false);
}

void clutl::ObjectGroup::RemoveHashEntry(unsigned int hash)
{
	// Linear probe from the natural hash location for matching hash
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = hash & index_mask;
	while (m_NamedObjects[index].hash && m_NamedObjects[index].hash != hash)
		index = (index + 1) & index_mask;

	// Leave the has key in-place, clearing the object pointer, marking the object as a dummy object
	HashEntry& he = m_NamedObjects[index];
	he.object = 0;
	m_NbObjects--;
}

void clutl::ObjectGroup::Resize(bool increase)
{
	// Backup existing table
	unsigned int old_max_nb_objects = m_MaxNbObjects;
	HashEntry* old_named_objects = m_NamedObjects;

	// Either make the table bigger or leave it the same size to flush all dummy objects
	if (increase)
	{
		if (m_MaxNbObjects < 8192 * 4)
			m_MaxNbObjects *= 4;
		else
			m_MaxNbObjects *= 2;
	}
	m_NamedObjects = new HashEntry[m_MaxNbObjects];

	// Reinsert all objects into the new hash table
	m_NbObjects = 0;
	m_NbOccupiedEntries = 0;
	for (unsigned int i = 0; i < old_max_nb_objects; i++)
	{
		HashEntry& he = old_named_objects[i];
		if (he.object != 0)
			AddHashEntry(he.object);
	}

	delete [] old_named_objects;
}

clutl::ObjectDatabase::ObjectDatabase()
	: m_RootGroup(0)
{
	m_RootGroup = New<ObjectGroup>();
}

clutl::ObjectDatabase::~ObjectDatabase()
{
	Delete(m_RootGroup);
}

clutl::ObjectIterator::ObjectIterator(const ObjectGroup* object_group)
	: m_ObjectGroup(object_group)
	, m_Position(0)
{
	// Search for the first non-empty slot
	ScanForEntry();
}

clutl::Object* clutl::ObjectIterator::GetObject() const
{
	clcpp::internal::Assert(IsValid());
	return m_ObjectGroup->m_NamedObjects[m_Position].object;
}

void clutl::ObjectIterator::MoveNext()
{
	m_Position++;
	ScanForEntry();
}

bool clutl::ObjectIterator::IsValid() const
{
	return m_Position < m_ObjectGroup->m_MaxNbObjects;
}

void clutl::ObjectIterator::ScanForEntry()
{
	// Search for the next non-empty slot
	while (m_Position < m_ObjectGroup->m_MaxNbObjects &&
		m_ObjectGroup->m_NamedObjects[m_Position].object == 0)
		m_Position++;
}
<commit_msg>Support for using the util libs without requiring generated reflection info: * Store explicitly calculated hash of clutl::ObjectGroup instead of using GetTypeNameHash. * Go back to using new for the root group allocation instead of New (should ObjectDatabase be a supported type anymore?)<commit_after>
//
// ===============================================================================
// clReflect
// -------------------------------------------------------------------------------
// Copyright (c) 2011-2012 Don Williamson & clReflect Authors (see AUTHORS file)
// Released under MIT License (see LICENSE file)
// ===============================================================================
//

// TODO: Lots of stuff happening in here that needs logging

#include <clutl/Objects.h>

// Store this here, rather than using GetTypeNameHash so that this library
// can be used without generating an implementation of GetTypeNameHash.
static unsigned int g_ObjectGroupHash = clcpp::internal::HashNameString("clutl::ObjectGroup");

struct clutl::ObjectGroup::HashEntry
{
	HashEntry() : hash(0), object(0) { }
	unsigned int hash;
	Object* object;
};

clutl::Object* clutl::CreateObject(const clcpp::Type *type, unsigned int unique_id, ObjectGroup* object_group)
{
	if (type == 0)
		return 0;

	// Can only create class objects
	if (type->kind != clcpp::Primitive::KIND_CLASS)
		return 0;
	const clcpp::Class* class_type = type->AsClass();

	// The object group has no registered constructor so construct manually
	// if it comes through
	Object* object = 0;
	if (type->name.hash == g_ObjectGroupHash)
	{
		object = new ObjectGroup();
	}
	else
	{
		// Need a constructor to new and a destructor to delete at a later point
		if (class_type->constructor == 0 || class_type->destructor == 0)
			return 0;

		// Allocate and call the constructor
		object = (Object*)new char[type->size];
		typedef void (*CallFunc)(clutl::Object*);
		CallFunc call_func = (CallFunc)class_type->constructor->address;
		call_func(object);
	}

	// Construct the object and add to its object group
	object->type = type;
	object->unique_id = unique_id;
	if (object_group)
		object_group->AddObject(object);

	return object;
}

void clutl::DestroyObject(const Object* object)
{
	// These represent fatal code errors
	clcpp::internal::Assert(object != 0);
	clcpp::internal::Assert(object->type != 0);

	// Remove from any attached object group
	if (object->object_group != 0)
		object->object_group->RemoveObject(object);

	if (object->type->name.hash == g_ObjectGroupHash)
	{
		// ObjecGroup class does not have a registered destructor
		delete (ObjectGroup*)object;
	}

	else
	{
		// Call the destructor and release the memory
		const clcpp::Class* class_type = object->type->AsClass();
		clcpp::internal::Assert(class_type->destructor != 0);
		typedef void (*CallFunc)(const clutl::Object*);
		CallFunc call_func = (CallFunc)class_type->destructor->address;
		call_func(object);
		delete [] (char*)object;
	}
}

clutl::ObjectGroup::ObjectGroup()
	: m_MaxNbObjects(8)
	, m_NbObjects(0)
	, m_NbOccupiedEntries(0)
	, m_NamedObjects(0)
{
	// Allocate the hash table
	m_NamedObjects = new HashEntry[m_MaxNbObjects];
}

clutl::ObjectGroup::~ObjectGroup()
{
	if (m_NamedObjects != 0)
		delete [] m_NamedObjects;
}

void clutl::ObjectGroup::AddObject(Object* object)
{
	object->object_group = this;
	if (object->unique_id != 0)
		AddHashEntry(object);
}

void clutl::ObjectGroup::RemoveObject(const Object* object)
{
	// Remove from the hash table if it's named
	if (object->unique_id != 0)
		RemoveHashEntry(object->unique_id);
}

clutl::Object* clutl::ObjectGroup::FindObject(unsigned int unique_id) const
{
	// Linear probe from the natural hash location for matching hash
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = unique_id & index_mask;
	while (m_NamedObjects[index].hash)
	{
		// Ensure dummy objects are skipped
		if (m_NamedObjects[index].hash == unique_id &&
			m_NamedObjects[index].object != 0)
			break;

		index = (index + 1) & index_mask;
	}

	// Get the object here
	HashEntry& he = m_NamedObjects[index];
	return he.object;
}

clutl::Object* clutl::ObjectGroup::FindObjectSearchParents(unsigned int unique_id) const
{
	// Search up through the object group hierarchy
	const ObjectGroup* group = this;
	Object* object = 0;
	while (group != 0)
	{
		object = group->FindObject(unique_id);
		if (object != 0)
			break;

		group = group->object_group;
	}

	return object;
}

clutl::Object* clutl::ObjectGroup::FindObjectRelative(unsigned int* unique_ids, unsigned int nb_ids) const
{
	// Locate the containing object group
	const ObjectGroup* object_group = this;
	while (nb_ids - 1 > 0)
	{
		Object* object = FindObject(*unique_ids++);
		if (object == 0)
			return 0;

		// Ensure this is an object group
		if (object->type->kind != clcpp::Primitive::KIND_CLASS)
			return 0;
		const clcpp::Class* class_type = (clcpp::Class*)object->type;
		if (!(class_type->flag_attributes & FLAG_ATTR_IS_OBJECT_GROUP))
			return 0;

		object_group = (ObjectGroup*)object;
		nb_ids--;
	}

	return object_group->FindObject(*unique_ids);
}

void clutl::ObjectGroup::AddHashEntry(Object* object)
{
	// Linear probe from the natural hash location for a free slot, reusing any dummy slots
	unsigned int hash = object->unique_id;
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = hash & index_mask;
	while (m_NamedObjects[index].hash && m_NamedObjects[index].object != 0)
		index = (index + 1) & index_mask;

	// Add to the table
	HashEntry& he = m_NamedObjects[index];
	he.hash = hash;
	he.object = object;
	m_NbObjects++;
	m_NbOccupiedEntries++;

	// Resize when load factor is greather than 2/3
	if (m_NbObjects > (m_MaxNbObjects * 2) / 3)
		Resize(true);

	// Or flush dummy objects so that there is always at least on empty slot
	// This is required for the FindObject loop to terminate when an object can't be find
	else if (m_NbOccupiedEntries == m_MaxNbObjects)
		Resize(false);
}

void clutl::ObjectGroup::RemoveHashEntry(unsigned int hash)
{
	// Linear probe from the natural hash location for matching hash
	const unsigned int index_mask = m_MaxNbObjects - 1;
	unsigned int index = hash & index_mask;
	while (m_NamedObjects[index].hash && m_NamedObjects[index].hash != hash)
		index = (index + 1) & index_mask;

	// Leave the has key in-place, clearing the object pointer, marking the object as a dummy object
	HashEntry& he = m_NamedObjects[index];
	he.object = 0;
	m_NbObjects--;
}

void clutl::ObjectGroup::Resize(bool increase)
{
	// Backup existing table
	unsigned int old_max_nb_objects = m_MaxNbObjects;
	HashEntry* old_named_objects = m_NamedObjects;

	// Either make the table bigger or leave it the same size to flush all dummy objects
	if (increase)
	{
		if (m_MaxNbObjects < 8192 * 4)
			m_MaxNbObjects *= 4;
		else
			m_MaxNbObjects *= 2;
	}
	m_NamedObjects = new HashEntry[m_MaxNbObjects];

	// Reinsert all objects into the new hash table
	m_NbObjects = 0;
	m_NbOccupiedEntries = 0;
	for (unsigned int i = 0; i < old_max_nb_objects; i++)
	{
		HashEntry& he = old_named_objects[i];
		if (he.object != 0)
			AddHashEntry(he.object);
	}

	delete [] old_named_objects;
}

clutl::ObjectDatabase::ObjectDatabase()
	: m_RootGroup(0)
{
	m_RootGroup = new ObjectGroup;
}

clutl::ObjectDatabase::~ObjectDatabase()
{
	delete m_RootGroup;
}

clutl::ObjectIterator::ObjectIterator(const ObjectGroup* object_group)
	: m_ObjectGroup(object_group)
	, m_Position(0)
{
	// Search for the first non-empty slot
	ScanForEntry();
}

clutl::Object* clutl::ObjectIterator::GetObject() const
{
	clcpp::internal::Assert(IsValid());
	return m_ObjectGroup->m_NamedObjects[m_Position].object;
}

void clutl::ObjectIterator::MoveNext()
{
	m_Position++;
	ScanForEntry();
}

bool clutl::ObjectIterator::IsValid() const
{
	return m_Position < m_ObjectGroup->m_MaxNbObjects;
}

void clutl::ObjectIterator::ScanForEntry()
{
	// Search for the next non-empty slot
	while (m_Position < m_ObjectGroup->m_MaxNbObjects &&
		m_ObjectGroup->m_NamedObjects[m_Position].object == 0)
		m_Position++;
}
<|endoftext|>


<commit_before>#pragma once

#include <pegtl.hh>

#include "command_base.hxx"
#include "grammar.hxx"
#include "positional_argument.hxx"
#include "parser_state.hxx"

namespace cl
{
	namespace internal
	{
		template< typename T >
		struct parser_action
			:	pegtl::nothing<T>
		{};

		template< >
		struct parser_action <R_ShortName>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.names().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_LongName>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.names().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_Value>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.values().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_Long>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.is_short() = false;
			}
		};

		template< >
		struct parser_action <R_Short>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.is_short() = true;
			}
		};

		template< >
		struct parser_action <R_Assignment>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if(p_state.is_short())
					p_hndlr.get(p_state.name().at(0))->read(p_state.values(), true);
				else p_hndlr.get(p_state.name())->read(p_state.values(), true);
			}
		};

		template< >
		struct parser_action <R_Multi_Switch>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				for (auto& t_name : p_state.names())
					p_hndlr.get(t_name.at(0))->read(p_state.values(), false);
			}
		};

		template< >
		struct parser_action <R_NonAssignment>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if(p_state.is_short())
					p_hndlr.get(p_state.name().at(0))->read(p_state.values(), false);
				else p_hndlr.get(p_state.name())->read(p_state.values(), false);
			}
		};

		template< >
		struct parser_action <R_Argument>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if (!p_state.values().empty())
				{
					// Try to add values to free_arguments. TODO solve without try catch
					try
					{
						p_hndlr.get("")->read(p_state.values(), false);
					}
					catch (...)
					{
						// If no free_arguments was added, just ignore the values
					}
				}

				// Clean up state for next match
				p_state.cleanup();
			}
		};
	}
}<commit_msg>Reworked some code to avoid using try-catch as control structure.<commit_after>#pragma once

#include <pegtl.hh>

#include "command_base.hxx"
#include "grammar.hxx"
#include "positional_argument.hxx"
#include "parser_state.hxx"

namespace cl
{
	namespace internal
	{
		template< typename T >
		struct parser_action
			:	pegtl::nothing<T>
		{};

		template< >
		struct parser_action <R_ShortName>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.names().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_LongName>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.names().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_Value>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.values().push_back(p_in.string());
			}
		};

		template< >
		struct parser_action <R_Long>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.is_short() = false;
			}
		};

		template< >
		struct parser_action <R_Short>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				p_state.is_short() = true;
			}
		};

		template< >
		struct parser_action <R_Assignment>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if(p_state.is_short())
					p_hndlr.get(p_state.name().at(0))->read(p_state.values(), true);
				else p_hndlr.get(p_state.name())->read(p_state.values(), true);
			}
		};

		template< >
		struct parser_action <R_Multi_Switch>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				for (auto& t_name : p_state.names())
					p_hndlr.get(t_name.at(0))->read(p_state.values(), false);
			}
		};

		template< >
		struct parser_action <R_NonAssignment>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if(p_state.is_short())
					p_hndlr.get(p_state.name().at(0))->read(p_state.values(), false);
				else p_hndlr.get(p_state.name())->read(p_state.values(), false);
			}
		};

		template< >
		struct parser_action <R_Argument>
		{
			static void apply(const pegtl::input& p_in, command_base& p_hndlr, parser_state& p_state)
			{
				if (!p_state.values().empty())
				{
					/*// Try to add values to free_arguments. TODO solve without try catch
					try
					{
						p_hndlr.get("")->read(p_state.values(), false);
					}
					catch (...)
					{
						// If no free_arguments was added, just ignore the values
					}*/
					// The use might not have supplied a handler for free arguments
					if(p_hndlr.has(""))
						p_hndlr.get("")->read(p_state.values(), false);
				}

				// Clean up state for next match
				p_state.cleanup();
			}
		};
	}
}
<|endoftext|>


<commit_before>#include "pch.h"
#include "Servo.h"
#include <EGL/egl.h>

namespace winrt::servo {

void on_load_started() { sServo->Delegate().OnServoLoadStarted(); }

void on_load_ended() { sServo->Delegate().OnServoLoadEnded(); }

void on_history_changed(bool back, bool forward) {
  sServo->Delegate().OnServoHistoryChanged(back, forward);
}

void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); }

void on_title_changed(const char *title) {
  sServo->Delegate().OnServoTitleChanged(char2hstring(title));
}

void on_url_changed(const char *url) {
  sServo->Delegate().OnServoURLChanged(char2hstring(url));
}

void wakeup() { sServo->Delegate().WakeUp(); }

bool on_allow_navigation(const char *url) {
  return sServo->Delegate().OnServoAllowNavigation(char2hstring(url));
};

void on_animating_changed(bool aAnimating) {
  sServo->Delegate().OnServoAnimatingChanged(aAnimating);
}

void on_panic(const char *backtrace) {
  if (sLogHandle != INVALID_HANDLE_VALUE) {
    CloseHandle(sLogHandle);
    sLogHandle = INVALID_HANDLE_VALUE;
  }
  throw hresult_error(E_FAIL, char2hstring(backtrace));
}

void on_ime_state_changed(bool aShow) {
  sServo->Delegate().OnServoIMEStateChanged(aShow);
}

void set_clipboard_contents(const char *) {
  // FIXME
}

const char *get_clipboard_contents() {
  // FIXME
  return nullptr;
}

void on_media_session_metadata(const char *title, const char *album,
                               const char *artist) {
  return sServo->Delegate().OnServoMediaSessionMetadata(
      char2hstring(title), char2hstring(album), char2hstring(artist));
}

void on_media_session_playback_state_change(
    const capi::CMediaSessionPlaybackState state) {
  return sServo->Delegate().OnServoMediaSessionPlaybackStateChange(state);
}

void prompt_alert(const char *message, bool trusted) {
  sServo->Delegate().OnServoPromptAlert(char2hstring(message), trusted);
}

void show_context_menu(const char *title, const char *const *items_list,
                       uint32_t items_size) {
  std::optional<hstring> opt_title = {};
  if (title != nullptr) {
    opt_title = char2hstring(title);
  }
  std::vector<winrt::hstring> items;
  for (uint32_t i = 0; i < items_size; i++) {
    items.push_back(char2hstring(items_list[i]));
  }
  sServo->Delegate().OnServoShowContextMenu(opt_title, items);
}

void on_devtools_started(Servo::DevtoolsServerState result,
                         const unsigned int port) {
  sServo->Delegate().OnServoDevtoolsStarted(
      result == Servo::DevtoolsServerState::Started, port);
}

void on_log_output(const char *buffer, uint32_t buffer_length) {
  OutputDebugStringA(buffer);

  if (sLogHandle == INVALID_HANDLE_VALUE) {
    return;
  }

  DWORD bytesWritten;
  auto writeResult =
      WriteFile(sLogHandle, buffer, buffer_length, &bytesWritten, nullptr);

  if (writeResult == FALSE || bytesWritten != buffer_length)
    throw std::runtime_error(
        "Failed to write log message to the log file: error code " +
        std::to_string(GetLastError()));
}

Servo::PromptResult prompt_ok_cancel(const char *message, bool trusted) {
  return sServo->Delegate().OnServoPromptOkCancel(char2hstring(message),
                                                  trusted);
}

Servo::PromptResult prompt_yes_no(const char *message, bool trusted) {
  return sServo->Delegate().OnServoPromptYesNo(char2hstring(message), trusted);
}

const char *prompt_input(const char *message, const char *default,
                         bool trusted) {
  auto input = sServo->Delegate().OnServoPromptInput(
      char2hstring(message), char2hstring(default), trusted);
  if (input.has_value()) {
    return *hstring2char(*input);
  } else {
    return nullptr;
  }
}

Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
             EGLNativeWindowType eglNativeWindow, float dpi,
             ServoDelegate &aDelegate)
    : mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) {
  SetEnvironmentVariableA("PreviewRuntimeEnabled", "1");

  capi::CInitOptions o;
  hstring defaultPrefs = L" --pref dom.webxr.enabled --devtools";
  o.args = *hstring2char(args + defaultPrefs);
  o.url = *hstring2char(url);
  o.width = mWindowWidth;
  o.height = mWindowHeight;
  o.density = dpi;
  o.enable_subpixel_text_antialiasing = false;
  o.native_widget = eglNativeWindow;

  // Note about logs:
  // By default: all modules are enabled. Only warn level-logs are displayed.
  // To change the log level, add "--vslogger-level debug" to o.args.
  // To only print logs from specific modules, add their names to pfilters.
  // For example:
  // static char *pfilters[] = {
  //   "servo",
  //   "simpleservo",
  //   "script::dom::bindings::error", // Show JS errors by default.
  //   "canvas::webgl_thread", // Show GL errors by default.
  //   "compositing",
  //   "constellation",
  // };
  // o.vslogger_mod_list = pfilters;
  // o.vslogger_mod_size = sizeof(pfilters) / sizeof(pfilters[0]);

  o.vslogger_mod_list = NULL;
  o.vslogger_mod_size = 0;

  sServo = this; // FIXME;

#ifdef _DEBUG
  auto current = winrt::Windows::Storage::ApplicationData::Current();
  auto filePath = std::wstring(current.LocalFolder().Path()) + L"\\stdout.txt";
  sLogHandle =
      CreateFile2(filePath.c_str(), GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr);
  if (sLogHandle == INVALID_HANDLE_VALUE)
    throw std::runtime_error("Failed to open the log file: error code " +
                             std::to_string(GetLastError()));

  if (SetFilePointer(sLogHandle, 0, nullptr, FILE_END) ==
      INVALID_SET_FILE_POINTER)
    throw std::runtime_error(
        "Failed to set file pointer to the end of file: error code " +
        std::to_string(GetLastError()));
#endif

  capi::CHostCallbacks c;
  c.on_load_started = &on_load_started;
  c.on_load_ended = &on_load_ended;
  c.on_title_changed = &on_title_changed;
  c.on_url_changed = &on_url_changed;
  c.on_history_changed = &on_history_changed;
  c.on_animating_changed = &on_animating_changed;
  c.on_shutdown_complete = &on_shutdown_complete;
  c.on_allow_navigation = &on_allow_navigation;
  c.on_ime_state_changed = &on_ime_state_changed;
  c.get_clipboard_contents = &get_clipboard_contents;
  c.set_clipboard_contents = &set_clipboard_contents;
  c.on_media_session_metadata = &on_media_session_metadata;
  c.on_media_session_playback_state_change =
      &on_media_session_playback_state_change;
  c.prompt_alert = &prompt_alert;
  c.prompt_ok_cancel = &prompt_ok_cancel;
  c.prompt_yes_no = &prompt_yes_no;
  c.prompt_input = &prompt_input;
  c.on_devtools_started = &on_devtools_started;
  c.show_context_menu = &show_context_menu;
  c.on_log_output = &on_log_output;

  capi::register_panic_handler(&on_panic);

  capi::init_with_egl(o, &wakeup, c);
}

Servo::~Servo() {
  sServo = nullptr;
  if (sLogHandle != INVALID_HANDLE_VALUE)
    CloseHandle(sLogHandle);
}

winrt::hstring char2hstring(const char *c_str) {
  // FIXME: any better way of doing this?
  auto str = std::string(c_str);
  int size_needed =
      MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
  std::wstring str2(size_needed, 0);
  MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0],
                      size_needed);
  winrt::hstring str3{str2};
  return str3;
}

std::unique_ptr<char *> hstring2char(hstring hstr) {
  const wchar_t *wc = hstr.c_str();
  size_t size = hstr.size() + 1;
  char *str = new char[size];
  size_t converted = 0;
  wcstombs_s(&converted, str, size, wc, hstr.size());
  return std::make_unique<char *>(str);
}

} // namespace winrt::servo
<commit_msg>Enable UWP logging in release builds with FirefoxRealityLogStdout env var.<commit_after>#include "pch.h"
#include "Servo.h"
#include <EGL/egl.h>

namespace winrt::servo {

void on_load_started() { sServo->Delegate().OnServoLoadStarted(); }

void on_load_ended() { sServo->Delegate().OnServoLoadEnded(); }

void on_history_changed(bool back, bool forward) {
  sServo->Delegate().OnServoHistoryChanged(back, forward);
}

void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); }

void on_title_changed(const char *title) {
  sServo->Delegate().OnServoTitleChanged(char2hstring(title));
}

void on_url_changed(const char *url) {
  sServo->Delegate().OnServoURLChanged(char2hstring(url));
}

void wakeup() { sServo->Delegate().WakeUp(); }

bool on_allow_navigation(const char *url) {
  return sServo->Delegate().OnServoAllowNavigation(char2hstring(url));
};

void on_animating_changed(bool aAnimating) {
  sServo->Delegate().OnServoAnimatingChanged(aAnimating);
}

void on_panic(const char *backtrace) {
  if (sLogHandle != INVALID_HANDLE_VALUE) {
    CloseHandle(sLogHandle);
    sLogHandle = INVALID_HANDLE_VALUE;
  }
  throw hresult_error(E_FAIL, char2hstring(backtrace));
}

void on_ime_state_changed(bool aShow) {
  sServo->Delegate().OnServoIMEStateChanged(aShow);
}

void set_clipboard_contents(const char *) {
  // FIXME
}

const char *get_clipboard_contents() {
  // FIXME
  return nullptr;
}

void on_media_session_metadata(const char *title, const char *album,
                               const char *artist) {
  return sServo->Delegate().OnServoMediaSessionMetadata(
      char2hstring(title), char2hstring(album), char2hstring(artist));
}

void on_media_session_playback_state_change(
    const capi::CMediaSessionPlaybackState state) {
  return sServo->Delegate().OnServoMediaSessionPlaybackStateChange(state);
}

void prompt_alert(const char *message, bool trusted) {
  sServo->Delegate().OnServoPromptAlert(char2hstring(message), trusted);
}

void show_context_menu(const char *title, const char *const *items_list,
                       uint32_t items_size) {
  std::optional<hstring> opt_title = {};
  if (title != nullptr) {
    opt_title = char2hstring(title);
  }
  std::vector<winrt::hstring> items;
  for (uint32_t i = 0; i < items_size; i++) {
    items.push_back(char2hstring(items_list[i]));
  }
  sServo->Delegate().OnServoShowContextMenu(opt_title, items);
}

void on_devtools_started(Servo::DevtoolsServerState result,
                         const unsigned int port) {
  sServo->Delegate().OnServoDevtoolsStarted(
      result == Servo::DevtoolsServerState::Started, port);
}

void on_log_output(const char *buffer, uint32_t buffer_length) {
  OutputDebugStringA(buffer);

  if (sLogHandle == INVALID_HANDLE_VALUE) {
    return;
  }

  DWORD bytesWritten;
  auto writeResult =
      WriteFile(sLogHandle, buffer, buffer_length, &bytesWritten, nullptr);

  if (writeResult == FALSE || bytesWritten != buffer_length)
    throw std::runtime_error(
        "Failed to write log message to the log file: error code " +
        std::to_string(GetLastError()));
}

Servo::PromptResult prompt_ok_cancel(const char *message, bool trusted) {
  return sServo->Delegate().OnServoPromptOkCancel(char2hstring(message),
                                                  trusted);
}

Servo::PromptResult prompt_yes_no(const char *message, bool trusted) {
  return sServo->Delegate().OnServoPromptYesNo(char2hstring(message), trusted);
}

const char *prompt_input(const char *message, const char *default,
                         bool trusted) {
  auto input = sServo->Delegate().OnServoPromptInput(
      char2hstring(message), char2hstring(default), trusted);
  if (input.has_value()) {
    return *hstring2char(*input);
  } else {
    return nullptr;
  }
}

Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
             EGLNativeWindowType eglNativeWindow, float dpi,
             ServoDelegate &aDelegate)
    : mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) {
  SetEnvironmentVariableA("PreviewRuntimeEnabled", "1");

  capi::CInitOptions o;
  hstring defaultPrefs = L" --pref dom.webxr.enabled --devtools";
  o.args = *hstring2char(args + defaultPrefs);
  o.url = *hstring2char(url);
  o.width = mWindowWidth;
  o.height = mWindowHeight;
  o.density = dpi;
  o.enable_subpixel_text_antialiasing = false;
  o.native_widget = eglNativeWindow;

  // Note about logs:
  // By default: all modules are enabled. Only warn level-logs are displayed.
  // To change the log level, add "--vslogger-level debug" to o.args.
  // To only print logs from specific modules, add their names to pfilters.
  // For example:
  // static char *pfilters[] = {
  //   "servo",
  //   "simpleservo",
  //   "script::dom::bindings::error", // Show JS errors by default.
  //   "canvas::webgl_thread", // Show GL errors by default.
  //   "compositing",
  //   "constellation",
  // };
  // o.vslogger_mod_list = pfilters;
  // o.vslogger_mod_size = sizeof(pfilters) / sizeof(pfilters[0]);

  o.vslogger_mod_list = NULL;
  o.vslogger_mod_size = 0;

  sServo = this; // FIXME;

#ifndef _DEBUG
  char buffer[1024];
  bool logToFile = GetEnvironmentVariableA("FirefoxRealityLogStdout", buffer,
                                           sizeof(buffer)) != 0;
#else
  bool logToFile = true;
#endif
  if (logToFile) {
    auto current = winrt::Windows::Storage::ApplicationData::Current();
    auto filePath =
        std::wstring(current.LocalFolder().Path()) + L"\\stdout.txt";
    sLogHandle =
        CreateFile2(filePath.c_str(), GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr);
    if (sLogHandle == INVALID_HANDLE_VALUE) {
      throw std::runtime_error("Failed to open the log file: error code " +
                               std::to_string(GetLastError()));
    }

    if (SetFilePointer(sLogHandle, 0, nullptr, FILE_END) ==
        INVALID_SET_FILE_POINTER) {
      throw std::runtime_error(
          "Failed to set file pointer to the end of file: error code " +
          std::to_string(GetLastError()));
    }
  }

  capi::CHostCallbacks c;
  c.on_load_started = &on_load_started;
  c.on_load_ended = &on_load_ended;
  c.on_title_changed = &on_title_changed;
  c.on_url_changed = &on_url_changed;
  c.on_history_changed = &on_history_changed;
  c.on_animating_changed = &on_animating_changed;
  c.on_shutdown_complete = &on_shutdown_complete;
  c.on_allow_navigation = &on_allow_navigation;
  c.on_ime_state_changed = &on_ime_state_changed;
  c.get_clipboard_contents = &get_clipboard_contents;
  c.set_clipboard_contents = &set_clipboard_contents;
  c.on_media_session_metadata = &on_media_session_metadata;
  c.on_media_session_playback_state_change =
      &on_media_session_playback_state_change;
  c.prompt_alert = &prompt_alert;
  c.prompt_ok_cancel = &prompt_ok_cancel;
  c.prompt_yes_no = &prompt_yes_no;
  c.prompt_input = &prompt_input;
  c.on_devtools_started = &on_devtools_started;
  c.show_context_menu = &show_context_menu;
  c.on_log_output = &on_log_output;

  capi::register_panic_handler(&on_panic);

  capi::init_with_egl(o, &wakeup, c);
}

Servo::~Servo() {
  sServo = nullptr;
  if (sLogHandle != INVALID_HANDLE_VALUE)
    CloseHandle(sLogHandle);
}

winrt::hstring char2hstring(const char *c_str) {
  // FIXME: any better way of doing this?
  auto str = std::string(c_str);
  int size_needed =
      MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
  std::wstring str2(size_needed, 0);
  MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0],
                      size_needed);
  winrt::hstring str3{str2};
  return str3;
}

std::unique_ptr<char *> hstring2char(hstring hstr) {
  const wchar_t *wc = hstr.c_str();
  size_t size = hstr.size() + 1;
  char *str = new char[size];
  size_t converted = 0;
  wcstombs_s(&converted, str, size, wc, hstr.size());
  return std::make_unique<char *>(str);
}

} // namespace winrt::servo
<|endoftext|>


<commit_before>#ifndef CTRE__RETURN_TYPE__HPP
#define CTRE__RETURN_TYPE__HPP

#include "id.hpp"
#include <type_traits>
#include <tuple>
#include <string_view>
#include <string>

namespace ctre {

struct not_matched_tag_t { };

static constexpr inline auto not_matched = not_matched_tag_t{};

template <size_t Id, typename Name = void> struct captured_content {
	template <typename Iterator> class storage {
		Iterator _begin{};
		Iterator _end{};

		bool _matched{false};
	public:
		using char_type = typename std::iterator_traits<Iterator>::value_type;

		using name = Name;

		constexpr CTRE_FORCE_INLINE storage() noexcept {}

		constexpr CTRE_FORCE_INLINE void matched() noexcept {
			_matched = true;
		}
		constexpr CTRE_FORCE_INLINE void unmatch() noexcept {
			_matched = false;
		}
		constexpr CTRE_FORCE_INLINE void set_start(Iterator pos) noexcept {
			_begin = pos;
		}
		constexpr CTRE_FORCE_INLINE storage & set_end(Iterator pos) noexcept {
			_end = pos;
			return *this;
		}
		constexpr CTRE_FORCE_INLINE Iterator get_end() const noexcept {
			return _end;
		}

		constexpr auto begin() const noexcept {
			return _begin;
		}
		constexpr auto end() const noexcept {
			return _end;
		}

		constexpr CTRE_FORCE_INLINE operator bool() const noexcept {
			return _matched;
		}

		constexpr CTRE_FORCE_INLINE const auto * data() const noexcept {
			return &*_begin;
		}

		constexpr CTRE_FORCE_INLINE auto size() const noexcept {
			return static_cast<size_t>(std::distance(_begin, _end));
		}

		constexpr CTRE_FORCE_INLINE auto to_view() const noexcept {
			return std::basic_string_view<char_type>(&*_begin, static_cast<size_t>(std::distance(_begin, _end)));
		}

		constexpr CTRE_FORCE_INLINE auto to_string() const noexcept {
			return std::basic_string<char_type>(begin(), end());
		}

		constexpr CTRE_FORCE_INLINE auto view() const noexcept {
			return std::basic_string_view<char_type>(&*_begin, static_cast<size_t>(std::distance(_begin, _end)));
		}

		constexpr CTRE_FORCE_INLINE auto str() const noexcept {
			return std::basic_string<char_type>(begin(), end());
		}

		constexpr CTRE_FORCE_INLINE operator std::basic_string_view<char_type>() const noexcept {
			return to_view();
		}

		constexpr CTRE_FORCE_INLINE explicit operator std::basic_string<char_type>() const noexcept {
			return to_string();
		}

		constexpr CTRE_FORCE_INLINE static size_t get_id() noexcept {
			return Id;
		}

		friend CTRE_FORCE_INLINE constexpr bool operator==(const storage & lhs, std::basic_string_view<char_type> rhs) noexcept {
			return lhs.view() == rhs;
		}
		friend CTRE_FORCE_INLINE constexpr bool operator!=(const storage & lhs, std::basic_string_view<char_type> rhs) noexcept {
			return lhs.view() != rhs;
		}
		friend CTRE_FORCE_INLINE constexpr bool operator==(std::basic_string_view<char_type> lhs, const storage & rhs) noexcept {
			return lhs == rhs.view();
		}
		friend CTRE_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<char_type> lhs, const storage & rhs) noexcept {
			return lhs != rhs.view();
		}
	};
};

struct capture_not_exists_tag { };

static constexpr inline auto capture_not_exists = capture_not_exists_tag{};

template <typename... Captures> struct captures;

template <typename Head, typename... Tail> struct captures<Head, Tail...>: captures<Tail...> {
	Head head{};
	constexpr CTRE_FORCE_INLINE captures() noexcept { }
	template <size_t id> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		if constexpr (id == Head::get_id()) {
			return true;
		} else {
			return captures<Tail...>::template exists<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return true;
		} else {
			return captures<Tail...>::template exists<Name>();
		}
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#else
	template <const auto & Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#endif
		if constexpr (std::is_same_v<typename Head::name, void>) {
			return captures<Tail...>::template exists<Name>();
		} else {
			if constexpr (Head::name::name.is_same_as(Name)) {
				return true;
			} else {
				return captures<Tail...>::template exists<Name>();
			}
		}
	}
	template <size_t id> CTRE_FORCE_INLINE constexpr auto & select() noexcept {
		if constexpr (id == Head::get_id()) {
			return head;
		} else {
			return captures<Tail...>::template select<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE constexpr auto & select() noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return head;
		} else {
			return captures<Tail...>::template select<Name>();
		}
	}
	template <size_t id> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		if constexpr (id == Head::get_id()) {
			return head;
		} else {
			return captures<Tail...>::template select<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return head;
		} else {
			return captures<Tail...>::template select<Name>();
		}
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#else
	template <const auto & Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#endif
		if constexpr (std::is_same_v<typename Head::name, void>) {
			return captures<Tail...>::template select<Name>();
		} else {
			if constexpr (Head::name::name.is_same_as(Name)) {
				return head;
			} else {
				return captures<Tail...>::template select<Name>();
			}
		}
	}
};

template <> struct captures<> {
	constexpr CTRE_FORCE_INLINE captures() noexcept { }
	template <size_t> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		return false;
	}
	template <typename> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		return false;
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#else
	template <const auto &> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#endif
		return false;
	}
	template <size_t> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		return capture_not_exists;
	}
	template <typename> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		return capture_not_exists;
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#else
	template <const auto &> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#endif
		return capture_not_exists;
	}
};

template <typename Iterator, typename... Captures> class regex_results {
	captures<captured_content<0>::template storage<Iterator>, typename Captures::template storage<Iterator>...> _captures{};
public:
	using char_type = typename std::iterator_traits<Iterator>::value_type;

	constexpr CTRE_FORCE_INLINE regex_results() noexcept { }
	constexpr CTRE_FORCE_INLINE regex_results(not_matched_tag_t) noexcept { }

	// special constructor for deducting
	constexpr CTRE_FORCE_INLINE regex_results(Iterator, ctll::list<Captures...>) noexcept { }

	template <size_t Id, typename = std::enable_if_t<decltype(_captures)::template exists<Id>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
		return _captures.template select<Id>();
	}
	template <typename Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
		return _captures.template select<Name>();
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
#else
	template <const auto & Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
#endif
		return _captures.template select<Name>();
	}
	static constexpr size_t count() noexcept {
		return sizeof...(Captures) + 1;
	}
	constexpr CTRE_FORCE_INLINE regex_results & matched() noexcept {
		_captures.template select<0>().matched();
		return *this;
	}
	constexpr CTRE_FORCE_INLINE regex_results & unmatch() noexcept {
		_captures.template select<0>().unmatch();
		return *this;
	}
	constexpr CTRE_FORCE_INLINE operator bool() const noexcept {
		return bool(_captures.template select<0>());
	}

	constexpr CTRE_FORCE_INLINE operator std::basic_string_view<char_type>() const noexcept {
		return to_view();
	}

	constexpr CTRE_FORCE_INLINE explicit operator std::basic_string<char_type>() const noexcept {
		return to_string();
	}

	constexpr CTRE_FORCE_INLINE auto to_view() const noexcept {
		return _captures.template select<0>().to_view();
	}

	constexpr CTRE_FORCE_INLINE auto to_string() const noexcept {
		return _captures.template select<0>().to_string();
	}

	constexpr CTRE_FORCE_INLINE auto view() const noexcept {
		return _captures.template select<0>().view();
	}

	constexpr CTRE_FORCE_INLINE auto str() const noexcept {
		return _captures.template select<0>().to_string();
	}

	constexpr CTRE_FORCE_INLINE size_t size() const noexcept {
		return _captures.template select<0>().size();
	}

	constexpr CTRE_FORCE_INLINE const auto * data() const noexcept {
		return _captures.template select<0>().data();
	}

	constexpr CTRE_FORCE_INLINE regex_results & set_start_mark(Iterator pos) noexcept {
		_captures.template select<0>().set_start(pos);
		return *this;
	}
	constexpr CTRE_FORCE_INLINE regex_results & set_end_mark(Iterator pos) noexcept {
		_captures.template select<0>().set_end(pos);
		return *this;
	}
	constexpr CTRE_FORCE_INLINE Iterator get_end_position() const noexcept {
		return _captures.template select<0>().get_end();
	}
	template <size_t Id> CTRE_FORCE_INLINE constexpr regex_results & start_capture(Iterator pos) noexcept {
		_captures.template select<Id>().set_start(pos);
		return *this;
	}
	template <size_t Id> CTRE_FORCE_INLINE constexpr regex_results & end_capture(Iterator pos) noexcept {
		_captures.template select<Id>().set_end(pos).matched();
		return *this;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator==(const regex_results & lhs, std::basic_string_view<char_type> rhs) noexcept {
		return lhs.view() == rhs;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator!=(const regex_results & lhs, std::basic_string_view<char_type> rhs) noexcept {
		return lhs.view() != rhs;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator==(std::basic_string_view<char_type> lhs, const regex_results & rhs) noexcept {
		return lhs == rhs.view();
	}
	friend CTRE_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<char_type> lhs, const regex_results & rhs) noexcept {
		return lhs != rhs.view();
	}
};

template <typename Iterator, typename... Captures> regex_results(Iterator, ctll::list<Captures...>) -> regex_results<Iterator, Captures...>;

}

// support for structured bindings

#ifndef __EDG__
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif

namespace std {
	template <typename... Captures> struct tuple_size<ctre::regex_results<Captures...>> : public std::integral_constant<size_t, ctre::regex_results<Captures...>::count()> { };

	template <size_t N, typename... Captures> struct tuple_element<N, ctre::regex_results<Captures...>> {
	public:
		using type = decltype(
			std::declval<const ctre::regex_results<Captures...> &>().template get<N>()
		);
	};
}

#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif

#endif
<commit_msg>convinience comparison shouldn't have a UB in case if the capture is empty<commit_after>#ifndef CTRE__RETURN_TYPE__HPP
#define CTRE__RETURN_TYPE__HPP

#include "id.hpp"
#include <type_traits>
#include <tuple>
#include <string_view>
#include <string>

namespace ctre {

struct not_matched_tag_t { };

static constexpr inline auto not_matched = not_matched_tag_t{};

template <size_t Id, typename Name = void> struct captured_content {
	template <typename Iterator> class storage {
		Iterator _begin{};
		Iterator _end{};

		bool _matched{false};
	public:
		using char_type = typename std::iterator_traits<Iterator>::value_type;

		using name = Name;

		constexpr CTRE_FORCE_INLINE storage() noexcept {}

		constexpr CTRE_FORCE_INLINE void matched() noexcept {
			_matched = true;
		}
		constexpr CTRE_FORCE_INLINE void unmatch() noexcept {
			_matched = false;
		}
		constexpr CTRE_FORCE_INLINE void set_start(Iterator pos) noexcept {
			_begin = pos;
		}
		constexpr CTRE_FORCE_INLINE storage & set_end(Iterator pos) noexcept {
			_end = pos;
			return *this;
		}
		constexpr CTRE_FORCE_INLINE Iterator get_end() const noexcept {
			return _end;
		}

		constexpr auto begin() const noexcept {
			return _begin;
		}
		constexpr auto end() const noexcept {
			return _end;
		}

		constexpr CTRE_FORCE_INLINE operator bool() const noexcept {
			return _matched;
		}

		constexpr CTRE_FORCE_INLINE const auto * data() const noexcept {
			return &*_begin;
		}

		constexpr CTRE_FORCE_INLINE auto size() const noexcept {
			return static_cast<size_t>(std::distance(_begin, _end));
		}

		constexpr CTRE_FORCE_INLINE auto to_view() const noexcept {
			return std::basic_string_view<char_type>(&*_begin, static_cast<size_t>(std::distance(_begin, _end)));
		}

		constexpr CTRE_FORCE_INLINE auto to_string() const noexcept {
			return std::basic_string<char_type>(begin(), end());
		}

		constexpr CTRE_FORCE_INLINE auto view() const noexcept {
			return std::basic_string_view<char_type>(&*_begin, static_cast<size_t>(std::distance(_begin, _end)));
		}

		constexpr CTRE_FORCE_INLINE auto str() const noexcept {
			return std::basic_string<char_type>(begin(), end());
		}

		constexpr CTRE_FORCE_INLINE operator std::basic_string_view<char_type>() const noexcept {
			return to_view();
		}

		constexpr CTRE_FORCE_INLINE explicit operator std::basic_string<char_type>() const noexcept {
			return to_string();
		}

		constexpr CTRE_FORCE_INLINE static size_t get_id() noexcept {
			return Id;
		}

		friend CTRE_FORCE_INLINE constexpr bool operator==(const storage & lhs, std::basic_string_view<char_type> rhs) noexcept {
			return bool(lhs) ? lhs.view() == rhs : false;
		}
		friend CTRE_FORCE_INLINE constexpr bool operator!=(const storage & lhs, std::basic_string_view<char_type> rhs) noexcept {
			return bool(lhs) ? lhs.view() != rhs : false;
		}
		friend CTRE_FORCE_INLINE constexpr bool operator==(std::basic_string_view<char_type> lhs, const storage & rhs) noexcept {
			return bool(rhs) ? lhs == rhs.view() : false;
		}
		friend CTRE_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<char_type> lhs, const storage & rhs) noexcept {
			return bool(rhs) ? lhs != rhs.view() : false;
		}
	};
};

struct capture_not_exists_tag { };

static constexpr inline auto capture_not_exists = capture_not_exists_tag{};

template <typename... Captures> struct captures;

template <typename Head, typename... Tail> struct captures<Head, Tail...>: captures<Tail...> {
	Head head{};
	constexpr CTRE_FORCE_INLINE captures() noexcept { }
	template <size_t id> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		if constexpr (id == Head::get_id()) {
			return true;
		} else {
			return captures<Tail...>::template exists<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return true;
		} else {
			return captures<Tail...>::template exists<Name>();
		}
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#else
	template <const auto & Name> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#endif
		if constexpr (std::is_same_v<typename Head::name, void>) {
			return captures<Tail...>::template exists<Name>();
		} else {
			if constexpr (Head::name::name.is_same_as(Name)) {
				return true;
			} else {
				return captures<Tail...>::template exists<Name>();
			}
		}
	}
	template <size_t id> CTRE_FORCE_INLINE constexpr auto & select() noexcept {
		if constexpr (id == Head::get_id()) {
			return head;
		} else {
			return captures<Tail...>::template select<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE constexpr auto & select() noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return head;
		} else {
			return captures<Tail...>::template select<Name>();
		}
	}
	template <size_t id> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		if constexpr (id == Head::get_id()) {
			return head;
		} else {
			return captures<Tail...>::template select<id>();
		}
	}
	template <typename Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		if constexpr (std::is_same_v<Name, typename Head::name>) {
			return head;
		} else {
			return captures<Tail...>::template select<Name>();
		}
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#else
	template <const auto & Name> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#endif
		if constexpr (std::is_same_v<typename Head::name, void>) {
			return captures<Tail...>::template select<Name>();
		} else {
			if constexpr (Head::name::name.is_same_as(Name)) {
				return head;
			} else {
				return captures<Tail...>::template select<Name>();
			}
		}
	}
};

template <> struct captures<> {
	constexpr CTRE_FORCE_INLINE captures() noexcept { }
	template <size_t> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		return false;
	}
	template <typename> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
		return false;
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#else
	template <const auto &> CTRE_FORCE_INLINE static constexpr bool exists() noexcept {
#endif
		return false;
	}
	template <size_t> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		return capture_not_exists;
	}
	template <typename> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
		return capture_not_exists;
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#else
	template <const auto &> CTRE_FORCE_INLINE constexpr auto & select() const noexcept {
#endif
		return capture_not_exists;
	}
};

template <typename Iterator, typename... Captures> class regex_results {
	captures<captured_content<0>::template storage<Iterator>, typename Captures::template storage<Iterator>...> _captures{};
public:
	using char_type = typename std::iterator_traits<Iterator>::value_type;

	constexpr CTRE_FORCE_INLINE regex_results() noexcept { }
	constexpr CTRE_FORCE_INLINE regex_results(not_matched_tag_t) noexcept { }

	// special constructor for deducting
	constexpr CTRE_FORCE_INLINE regex_results(Iterator, ctll::list<Captures...>) noexcept { }

	template <size_t Id, typename = std::enable_if_t<decltype(_captures)::template exists<Id>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
		return _captures.template select<Id>();
	}
	template <typename Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
		return _captures.template select<Name>();
	}
#if (__cpp_nontype_template_parameter_class || (__cpp_nontype_template_args >= 201911L))
	template <ctll::fixed_string Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
#else
	template <const auto & Name, typename = std::enable_if_t<decltype(_captures)::template exists<Name>()>> CTRE_FORCE_INLINE constexpr auto get() const noexcept {
#endif
		return _captures.template select<Name>();
	}
	static constexpr size_t count() noexcept {
		return sizeof...(Captures) + 1;
	}
	constexpr CTRE_FORCE_INLINE regex_results & matched() noexcept {
		_captures.template select<0>().matched();
		return *this;
	}
	constexpr CTRE_FORCE_INLINE regex_results & unmatch() noexcept {
		_captures.template select<0>().unmatch();
		return *this;
	}
	constexpr CTRE_FORCE_INLINE operator bool() const noexcept {
		return bool(_captures.template select<0>());
	}

	constexpr CTRE_FORCE_INLINE operator std::basic_string_view<char_type>() const noexcept {
		return to_view();
	}

	constexpr CTRE_FORCE_INLINE explicit operator std::basic_string<char_type>() const noexcept {
		return to_string();
	}

	constexpr CTRE_FORCE_INLINE auto to_view() const noexcept {
		return _captures.template select<0>().to_view();
	}

	constexpr CTRE_FORCE_INLINE auto to_string() const noexcept {
		return _captures.template select<0>().to_string();
	}

	constexpr CTRE_FORCE_INLINE auto view() const noexcept {
		return _captures.template select<0>().view();
	}

	constexpr CTRE_FORCE_INLINE auto str() const noexcept {
		return _captures.template select<0>().to_string();
	}

	constexpr CTRE_FORCE_INLINE size_t size() const noexcept {
		return _captures.template select<0>().size();
	}

	constexpr CTRE_FORCE_INLINE const auto * data() const noexcept {
		return _captures.template select<0>().data();
	}

	constexpr CTRE_FORCE_INLINE regex_results & set_start_mark(Iterator pos) noexcept {
		_captures.template select<0>().set_start(pos);
		return *this;
	}
	constexpr CTRE_FORCE_INLINE regex_results & set_end_mark(Iterator pos) noexcept {
		_captures.template select<0>().set_end(pos);
		return *this;
	}
	constexpr CTRE_FORCE_INLINE Iterator get_end_position() const noexcept {
		return _captures.template select<0>().get_end();
	}
	template <size_t Id> CTRE_FORCE_INLINE constexpr regex_results & start_capture(Iterator pos) noexcept {
		_captures.template select<Id>().set_start(pos);
		return *this;
	}
	template <size_t Id> CTRE_FORCE_INLINE constexpr regex_results & end_capture(Iterator pos) noexcept {
		_captures.template select<Id>().set_end(pos).matched();
		return *this;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator==(const regex_results & lhs, std::basic_string_view<char_type> rhs) noexcept {
		return bool(lhs) ? lhs.view() == rhs : false;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator!=(const regex_results & lhs, std::basic_string_view<char_type> rhs) noexcept {
		return bool(lhs) ? lhs.view() != rhs : true;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator==(std::basic_string_view<char_type> lhs, const regex_results & rhs) noexcept {
		return bool(rhs) ? lhs == rhs.view() : false;
	}
	friend CTRE_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<char_type> lhs, const regex_results & rhs) noexcept {
		return bool(rhs) ? lhs != rhs.view() : true;
	}
};

template <typename Iterator, typename... Captures> regex_results(Iterator, ctll::list<Captures...>) -> regex_results<Iterator, Captures...>;

}

// support for structured bindings

#ifndef __EDG__
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif

namespace std {
	template <typename... Captures> struct tuple_size<ctre::regex_results<Captures...>> : public std::integral_constant<size_t, ctre::regex_results<Captures...>::count()> { };

	template <size_t N, typename... Captures> struct tuple_element<N, ctre::regex_results<Captures...>> {
	public:
		using type = decltype(
			std::declval<const ctre::regex_results<Captures...> &>().template get<N>()
		);
	};
}

#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif

#endif
<|endoftext|>


<commit_before>#include "servercontroller.hpp"
#include "serversocket.hpp"
#include <iostream>

namespace mirrors {

const std::string boardLocatingInstructions = "Position the camera to view the entire board.";

ServerController::ServerController(QObject *parent)
    : QObject(parent),
      sock(new ServerSocket(this)),
      trackerManager(nullptr),
      detectorTimer(new QTimer(this)),
      serverState(Idle),
      currentLevel(-1)
{

    connect(this, SIGNAL(markersUpdated(vector<MarkerUpdate>)),
            this, SLOT(broadcastPositions(vector<MarkerUpdate>)));
    connect(sock, SIGNAL(errorOccurred(QString)),
            this, SIGNAL(socketError(QString)));
    connect(this, SIGNAL(markersUpdated(vector<MarkerUpdate>)),
            sock, SLOT(processUpdates()));
    connect(sock, SIGNAL(levelChanged(int)),
            this, SLOT(changeLevel(int)));

    // A single-shot Timer with an interval of 0 will
    // directly fire the timeout when control goes back
    // to the event thread. We use this structure to
    // allow signals events to be processed while
    // keeping the detector active as much as possible.
    detectorTimer->setInterval(0);
    detectorTimer->setSingleShot(true);
}

ServerController::~ServerController() {
    if (trackerManager != nullptr) {
        delete trackerManager;
    }
}

void ServerController::changeState(ServerState state) {
    this->serverState = state;
    emit stateChanged(state);
}

void ServerController::fatalError(const QString &message) {
    stopServer();
    emit fatalErrorOccurred(message);
}

void ServerController::startServer(quint16 port, int cameraDevice, cv::Size camSize, BoardDetectionApproach::Type boardDetectionApproach) {
    Q_ASSERT(serverState == Idle);
    Q_ASSERT(trackerManager == nullptr); // Otherwise we get a memory leak.
    changeState(Starting);

    // (Re)Initialize tracking
    trackerManager = new TrackerManager(cameraDevice, camSize, boardDetectionApproach);
    trackerManager->loadPatterns(MARKER_DIRECTORY, MARKER_COUNT);

    sock->setPortNumber(port);
    sock->start();
    detectorTimer->start();

    // Start detecting board
    connect(detectorTimer, SIGNAL(timeout()),
            this,          SLOT(detectBoard()));
}

void ServerController::stopServer() {
    Q_ASSERT(serverState == Started || serverState == Starting);
    changeState(Stopping);
    sock->stop();
    currentLevel = -1;
}

void ServerController::changeLevel(int nextLevel) {
    if (nextLevel != currentLevel) {
        cv::Size2f boardSize(trackerManager->scaledBoardSize());
        sock->broadcastLevelUpdate(nextLevel, boardSize);
        currentLevel = nextLevel;

        std::cout << boardSize.width << ", " << boardSize.height << std::endl;
        emit levelChanged(nextLevel);
    }
}

void ServerController::detectBoard() {
    Q_ASSERT(trackerManager != nullptr);
    if (state() == Stopping) {
        delete trackerManager;
        trackerManager = nullptr;
        changeState(Idle);

        disconnect(detectorTimer, SIGNAL(timeout()),
                   this,          SLOT(detectBoard()));

        return;
    }
    Q_ASSERT(serverState == Starting);

    QPixmap result;
    bool boardLocated = trackerManager->locateBoard(result, true);

    // Show image to user
    emit imageReady(result);

    if (boardLocated) {
        // When the board is found, we stop trying to locate
        // the board and start detecting markers instead.
        disconnect(detectorTimer, SIGNAL(timeout()),
                   this,          SLOT(detectBoard()));
        connect(detectorTimer,    SIGNAL(timeout()),
                this,             SLOT(detectFrame()));
        changeState(Started);
    }
    detectorTimer->start();
}

void ServerController::setDebugOverlay(bool enable) {
    showDebugOverlay = enable;
}

void ServerController::detectFrame() {
    // detectFrame should never be called when the server is not running.
    Q_ASSERT(serverState != Idle);
    Q_ASSERT(trackerManager != nullptr);
    if (state() == Starting) {
        changeState(Started);
    }

    if (state() == Started) {
        QPixmap result;
        vector<MarkerUpdate> markers = trackerManager->getMarkerUpdates(result, showDebugOverlay);

        emit markersUpdated(markers);
        emit imageReady(result);

        detectorTimer->start();

        emit fpsChanged(trackerManager->getUpdateRate());
    } else {
        changeState(Idle);
        delete trackerManager;
        trackerManager = nullptr;

        disconnect(detectorTimer,    SIGNAL(timeout()),
                this,             SLOT(detectFrame()));
    }
}

void ServerController::broadcastPositions(vector<MarkerUpdate> markers) {
    for(MarkerUpdate marker : markers) {
        broadcastPosition(marker);
    }
}

void ServerController::broadcastPosition(const MarkerUpdate& marker) {
    if (marker.type == MarkerUpdateType::REMOVE) {
        sock->broadcastDelete(marker.id);
    } else {
        // Scale marker positions based on their size for Meta 1 tracking
        sock->broadcastPositionUpdate(
                    marker.id,
                    trackerManager->scaledMarkerCoordinate(marker.position),
                    marker.rotation);
    }
}

} // namespace mirrors

<commit_msg>Add HTTP server for board image and fix TrackerManager bug #2<commit_after>#include "servercontroller.hpp"
#include "serversocket.hpp"
#include <QBuffer>
#include <iostream>

namespace mirrors {

const std::string boardLocatingInstructions = "Position the camera to view the entire board.";

ServerController::ServerController(QObject *parent)
    : QObject(parent),
      sock(new ServerSocket(this)),
      trackerManager(nullptr),
      detectorTimer(new QTimer(this)),
      serverState(Idle),
      currentLevel(-1),
      server(new QHttpServer)
{

    connect(this, SIGNAL(markersUpdated(vector<MarkerUpdate>)),
            this, SLOT(broadcastPositions(vector<MarkerUpdate>)));
    connect(sock, SIGNAL(errorOccurred(QString)),
            this, SIGNAL(socketError(QString)));
    connect(this, SIGNAL(markersUpdated(vector<MarkerUpdate>)),
            sock, SLOT(processUpdates()));
    connect(sock, SIGNAL(levelChanged(int)),
            this, SLOT(changeLevel(int)));
    connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
            this, SLOT(sendBoard(QHttpRequest*, QHttpResponse*)));

    // A single-shot Timer with an interval of 0 will
    // directly fire the timeout when control goes back
    // to the event thread. We use this structure to
    // allow signals events to be processed while
    // keeping the detector active as much as possible.
    detectorTimer->setInterval(0);
    detectorTimer->setSingleShot(true);
}

void ServerController::sendBoard(QHttpRequest* req, QHttpResponse* resp) {
    resp->setHeader("Content-Type", "image/jpeg");
    resp->setHeader("Content-Length", QString::number(boardImageBytes.size()));
    resp->writeHead(200);
    resp->write(boardImageBytes);

    resp->end();
}

ServerController::~ServerController() {
    if (trackerManager != nullptr) {
        delete trackerManager;
    }
}

void ServerController::changeState(ServerState state) {
    this->serverState = state;
    emit stateChanged(state);
}

void ServerController::fatalError(const QString &message) {
    stopServer();
    emit fatalErrorOccurred(message);
}

void ServerController::startServer(quint16 port, int cameraDevice, cv::Size camSize, BoardDetectionApproach::Type boardDetectionApproach) {
    Q_ASSERT(serverState == Idle);
    Q_ASSERT(trackerManager == nullptr); // Otherwise we get a memory leak.
    changeState(Starting);

    // (Re)Initialize tracking
    trackerManager = new TrackerManager(cameraDevice, camSize, boardDetectionApproach);
    trackerManager->loadPatterns(MARKER_DIRECTORY, MARKER_COUNT);

    sock->setPortNumber(port);
    sock->start();
    detectorTimer->start();

    // Start detecting board
    connect(detectorTimer, SIGNAL(timeout()),
            this,          SLOT(detectBoard()));

    // Start server that broadcasts board image
    server->listen(port + 1);
}

void ServerController::stopServer() {
    Q_ASSERT(serverState == Started || serverState == Starting);
    changeState(Stopping);
    sock->stop();
    currentLevel = -1;
}

void ServerController::changeLevel(int nextLevel) {
    if (nextLevel != currentLevel) {
        cv::Size2f boardSize(trackerManager->scaledBoardSize());
        sock->broadcastLevelUpdate(nextLevel, boardSize);
        currentLevel = nextLevel;

        std::cout << boardSize.width << ", " << boardSize.height << std::endl;
        emit levelChanged(nextLevel);
    }
}

void ServerController::detectBoard() {
    Q_ASSERT(trackerManager != nullptr);
    if (state() == Stopping) {
        delete trackerManager;
        trackerManager = nullptr;
        changeState(Idle);

        disconnect(detectorTimer, SIGNAL(timeout()),
                   this,          SLOT(detectBoard()));

        return;
    }
    Q_ASSERT(serverState == Starting);

    QPixmap result;
    bool boardLocated = trackerManager->locateBoard(result, true);

    // Show image to user
    emit imageReady(result);

    if (boardLocated) {
        // Save an image of the board without text overlay
        QPixmap board;
        trackerManager->getMarkerUpdates(board, false);

        boardImageBytes.clear();
        QBuffer buffer(&boardImageBytes);
        buffer.open(QIODevice::WriteOnly);
        board.save(&buffer, "JPG");

        // When the board is found, we stop trying to locate
        // the board and start detecting markers instead.
        disconnect(detectorTimer, SIGNAL(timeout()),
                   this,          SLOT(detectBoard()));
        connect(detectorTimer,    SIGNAL(timeout()),
                this,             SLOT(detectFrame()));
        changeState(Started);
    }
    detectorTimer->start();
}

void ServerController::setDebugOverlay(bool enable) {
    showDebugOverlay = enable;
}

void ServerController::detectFrame() {
    // detectFrame should never be called when the server is not running.
    Q_ASSERT(serverState != Idle);
    Q_ASSERT(trackerManager != nullptr);
    if (state() == Starting) {
        changeState(Started);
    }

    if (state() == Started) {
        QPixmap result;
        vector<MarkerUpdate> markers = trackerManager->getMarkerUpdates(result, showDebugOverlay);

        emit markersUpdated(markers);
        emit imageReady(result);

        detectorTimer->start();

        emit fpsChanged(trackerManager->getUpdateRate());
    } else {
        changeState(Idle);
        delete trackerManager;
        trackerManager = nullptr;

        disconnect(detectorTimer,    SIGNAL(timeout()),
                this,             SLOT(detectFrame()));
    }
}

void ServerController::broadcastPositions(vector<MarkerUpdate> markers) {
    for(MarkerUpdate marker : markers) {
        broadcastPosition(marker);
    }
}

void ServerController::broadcastPosition(const MarkerUpdate& marker) {
    if (marker.type == MarkerUpdateType::REMOVE) {
        sock->broadcastDelete(marker.id);
    } else {
        // Scale marker positions based on their size for Meta 1 tracking
        sock->broadcastPositionUpdate(
                    marker.id,
                    trackerManager->scaledMarkerCoordinate(marker.position),
                    marker.rotation);
    }
}

} // namespace mirrors

<|endoftext|>