<commit_before>/**
 * \file
 * \brief DynamicThreadBase class header
 *
 * \author Copyright (C) 2015-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_

#include "distortos/DynamicSignalsReceiver.hpp"
#include "distortos/DynamicThreadParameters.hpp"
#include "distortos/ThreadCommon.hpp"

#include "distortos/internal/memory/storageDeleter.hpp"

namespace distortos
{

#ifdef CONFIG_THREAD_DETACH_ENABLE

class DynamicThread;

#endif	// def CONFIG_THREAD_DETACH_ENABLE

namespace internal
{

/**
 * \brief DynamicThreadBase class is a type-erased interface for thread that has dynamic storage for bound function,
 * stack and internal DynamicSignalsReceiver object.
 *
 * If thread detachment is enabled (CONFIG_THREAD_DETACH_ENABLE is defined) then this class is dynamically allocated by
 * DynamicThread - which allows it to be "detached". Otherwise - if thread detachment is disabled
 * (CONFIG_THREAD_DETACH_ENABLE is not defined) - DynamicThread just inherits from this class.
 */

class DynamicThreadBase : public ThreadCommon
{
public:

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this
	 * thread
	 * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread
	 * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable catching of signals for this thread
	 * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest
	 * \param [in] schedulingPolicy is the scheduling policy of the thread
	 * \param [in] owner is a reference to owner DynamicThread object
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions,
			uint8_t priority, SchedulingPolicy schedulingPolicy, DynamicThread& owner, Function&& function,
			Args&&... args);

#else	// !def CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this
	 * thread
	 * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread
	 * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable catching of signals for this thread
	 * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest
	 * \param [in] schedulingPolicy is the scheduling policy of the thread
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions,
			uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args);

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] parameters is a DynamicThreadParameters struct with thread parameters
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(const DynamicThreadParameters parameters, Function&& function, Args&&... args) :
			DynamicThreadBase{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals,
					parameters.signalActions, parameters.priority, parameters.schedulingPolicy,
					std::forward<Function>(function), std::forward<Args>(args)...}
	{

	}

#endif	// !def CONFIG_THREAD_DETACH_ENABLE

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Detaches the thread.
	 *
	 * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach
	 * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html
	 *
	 * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources
	 * allocated for the thread will be deallocated when the thread terminates.
	 *
	 * \return 0 on success, error code otherwise:
	 * - EINVAL - this thread is already detached;
	 */

	int detach() override;

#endif	// def CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Starts the thread.
	 *
	 * This operation can be performed on threads in "New" state only.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by ThreadCommon::startInternal();
	 */

	int start()
	{
#ifdef CONFIG_THREAD_DETACH_ENABLE
		return ThreadCommon::startInternal(run, preTerminationHook, terminationHook);
#else	// !def CONFIG_THREAD_DETACH_ENABLE
		return ThreadCommon::startInternal(run, nullptr, terminationHook);
#endif	// !def CONFIG_THREAD_DETACH_ENABLE
	}

	DynamicThreadBase(const DynamicThreadBase&) = delete;
	DynamicThreadBase(DynamicThreadBase&&) = default;
	const DynamicThreadBase& operator=(const DynamicThreadBase&) = delete;
	DynamicThreadBase& operator=(DynamicThreadBase&&) = delete;

protected:

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Pre-termination hook function of thread
	 *
	 * If thread is detached, locks object used for deferred deletion.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void preTerminationHook(Thread& thread);

	/**
	 * \brief Termination hook function of thread
	 *
	 * Calls ThreadCommon::terminationHook() and - if thread is detached - schedules itself for deferred deletion.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void terminationHook(Thread& thread);

#endif	// def CONFIG_THREAD_DETACH_ENABLE

private:

	/**
	 * \brief Helper function to make stack with size adjusted to alignment requirements
	 *
	 * Size of "stack guard" is added to function argument.
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 *
	 * \return Stack object with size adjusted to alignment requirements
	 */

	static Stack makeStack(const size_t stackSize)
	{
		static_assert(alignof(max_align_t) >= CONFIG_ARCHITECTURE_STACK_ALIGNMENT,
				"Alignment of dynamically allocated memory is too low!");

		const auto adjustedStackSize = (stackSize + CONFIG_ARCHITECTURE_STACK_ALIGNMENT - 1) /
				CONFIG_ARCHITECTURE_STACK_ALIGNMENT * CONFIG_ARCHITECTURE_STACK_ALIGNMENT;
		return {{new uint8_t[adjustedStackSize + stackGuardSize], storageDeleter<uint8_t>},
				adjustedStackSize + stackGuardSize};
	}

	/**
	 * \brief Thread's "run" function.
	 *
	 * Executes bound function object.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void run(Thread& thread);

	/// internal DynamicSignalsReceiver object
	DynamicSignalsReceiver dynamicSignalsReceiver_;

	/// bound function object
	std::function<void()> boundFunction_;

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/// pointer to owner DynamicThread object, nullptr if thread is detached
	DynamicThread* owner_;

#endif	// def CONFIG_THREAD_DETACH_ENABLE
};

#ifdef CONFIG_THREAD_DETACH_ENABLE

template<typename Function, typename... Args>
DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals,
		const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy,
		DynamicThread& owner, Function&& function, Args&&... args) :
				ThreadCommon{makeStack(stackSize), priority, schedulingPolicy, nullptr,
						canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr},
				dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0,
						canReceiveSignals == true ? signalActions : 0},
				boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)},
				owner_{&owner}
{

}

#else	// !def CONFIG_THREAD_DETACH_ENABLE

template<typename Function, typename... Args>
DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals,
		const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy,
		Function&& function, Args&&... args) :
				ThreadCommon{makeStack(stackSize), priority, schedulingPolicy, nullptr,
						canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr},
				dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0,
						canReceiveSignals == true ? signalActions : 0},
				boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)}
{

}

#endif	// !def CONFIG_THREAD_DETACH_ENABLE

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_
<commit_msg>Add "#include <functional>" in DynamicThreadBase.hpp<commit_after>/**
 * \file
 * \brief DynamicThreadBase class header
 *
 * \author Copyright (C) 2015-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_

#include "distortos/DynamicSignalsReceiver.hpp"
#include "distortos/DynamicThreadParameters.hpp"
#include "distortos/ThreadCommon.hpp"

#include "distortos/internal/memory/storageDeleter.hpp"

#include <functional>

namespace distortos
{

#ifdef CONFIG_THREAD_DETACH_ENABLE

class DynamicThread;

#endif	// def CONFIG_THREAD_DETACH_ENABLE

namespace internal
{

/**
 * \brief DynamicThreadBase class is a type-erased interface for thread that has dynamic storage for bound function,
 * stack and internal DynamicSignalsReceiver object.
 *
 * If thread detachment is enabled (CONFIG_THREAD_DETACH_ENABLE is defined) then this class is dynamically allocated by
 * DynamicThread - which allows it to be "detached". Otherwise - if thread detachment is disabled
 * (CONFIG_THREAD_DETACH_ENABLE is not defined) - DynamicThread just inherits from this class.
 */

class DynamicThreadBase : public ThreadCommon
{
public:

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this
	 * thread
	 * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread
	 * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable catching of signals for this thread
	 * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest
	 * \param [in] schedulingPolicy is the scheduling policy of the thread
	 * \param [in] owner is a reference to owner DynamicThread object
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions,
			uint8_t priority, SchedulingPolicy schedulingPolicy, DynamicThread& owner, Function&& function,
			Args&&... args);

#else	// !def CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this
	 * thread
	 * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread
	 * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if
	 * \a canReceiveSignals == true, 0 to disable catching of signals for this thread
	 * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest
	 * \param [in] schedulingPolicy is the scheduling policy of the thread
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions,
			uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args);

	/**
	 * \brief DynamicThreadBase's constructor
	 *
	 * \tparam Function is the function that will be executed in separate thread
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] parameters is a DynamicThreadParameters struct with thread parameters
	 * \param [in] function is a function that will be executed in separate thread
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	DynamicThreadBase(const DynamicThreadParameters parameters, Function&& function, Args&&... args) :
			DynamicThreadBase{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals,
					parameters.signalActions, parameters.priority, parameters.schedulingPolicy,
					std::forward<Function>(function), std::forward<Args>(args)...}
	{

	}

#endif	// !def CONFIG_THREAD_DETACH_ENABLE

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Detaches the thread.
	 *
	 * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach
	 * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html
	 *
	 * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources
	 * allocated for the thread will be deallocated when the thread terminates.
	 *
	 * \return 0 on success, error code otherwise:
	 * - EINVAL - this thread is already detached;
	 */

	int detach() override;

#endif	// def CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Starts the thread.
	 *
	 * This operation can be performed on threads in "New" state only.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by ThreadCommon::startInternal();
	 */

	int start()
	{
#ifdef CONFIG_THREAD_DETACH_ENABLE
		return ThreadCommon::startInternal(run, preTerminationHook, terminationHook);
#else	// !def CONFIG_THREAD_DETACH_ENABLE
		return ThreadCommon::startInternal(run, nullptr, terminationHook);
#endif	// !def CONFIG_THREAD_DETACH_ENABLE
	}

	DynamicThreadBase(const DynamicThreadBase&) = delete;
	DynamicThreadBase(DynamicThreadBase&&) = default;
	const DynamicThreadBase& operator=(const DynamicThreadBase&) = delete;
	DynamicThreadBase& operator=(DynamicThreadBase&&) = delete;

protected:

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/**
	 * \brief Pre-termination hook function of thread
	 *
	 * If thread is detached, locks object used for deferred deletion.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void preTerminationHook(Thread& thread);

	/**
	 * \brief Termination hook function of thread
	 *
	 * Calls ThreadCommon::terminationHook() and - if thread is detached - schedules itself for deferred deletion.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void terminationHook(Thread& thread);

#endif	// def CONFIG_THREAD_DETACH_ENABLE

private:

	/**
	 * \brief Helper function to make stack with size adjusted to alignment requirements
	 *
	 * Size of "stack guard" is added to function argument.
	 *
	 * \param [in] stackSize is the size of stack, bytes
	 *
	 * \return Stack object with size adjusted to alignment requirements
	 */

	static Stack makeStack(const size_t stackSize)
	{
		static_assert(alignof(max_align_t) >= CONFIG_ARCHITECTURE_STACK_ALIGNMENT,
				"Alignment of dynamically allocated memory is too low!");

		const auto adjustedStackSize = (stackSize + CONFIG_ARCHITECTURE_STACK_ALIGNMENT - 1) /
				CONFIG_ARCHITECTURE_STACK_ALIGNMENT * CONFIG_ARCHITECTURE_STACK_ALIGNMENT;
		return {{new uint8_t[adjustedStackSize + stackGuardSize], storageDeleter<uint8_t>},
				adjustedStackSize + stackGuardSize};
	}

	/**
	 * \brief Thread's "run" function.
	 *
	 * Executes bound function object.
	 *
	 * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase!
	 */

	static void run(Thread& thread);

	/// internal DynamicSignalsReceiver object
	DynamicSignalsReceiver dynamicSignalsReceiver_;

	/// bound function object
	std::function<void()> boundFunction_;

#ifdef CONFIG_THREAD_DETACH_ENABLE

	/// pointer to owner DynamicThread object, nullptr if thread is detached
	DynamicThread* owner_;

#endif	// def CONFIG_THREAD_DETACH_ENABLE
};

#ifdef CONFIG_THREAD_DETACH_ENABLE

template<typename Function, typename... Args>
DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals,
		const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy,
		DynamicThread& owner, Function&& function, Args&&... args) :
				ThreadCommon{makeStack(stackSize), priority, schedulingPolicy, nullptr,
						canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr},
				dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0,
						canReceiveSignals == true ? signalActions : 0},
				boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)},
				owner_{&owner}
{

}

#else	// !def CONFIG_THREAD_DETACH_ENABLE

template<typename Function, typename... Args>
DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals,
		const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy,
		Function&& function, Args&&... args) :
				ThreadCommon{makeStack(stackSize), priority, schedulingPolicy, nullptr,
						canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr},
				dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0,
						canReceiveSignals == true ? signalActions : 0},
				boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)}
{

}

#endif	// !def CONFIG_THREAD_DETACH_ENABLE

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_
<|endoftext|>


<commit_before>/**
 * \file
 * \brief CallOnceControlBlock class header
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-07-15
 */

#ifndef INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_
#define INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_

#include "distortos/estd/TypeErasedFunctor.hpp"

#include <utility>

#include <sys/features.h>

namespace distortos
{

/// GCC 4.9 is needed for CallOnceControlBlock::operator()() function - and thus for OnceFlag and callOnce() - earlier
/// versions don't support parameter pack expansion in lambdas
#define DISTORTOS_CALLONCE_SUPPORTED	__GNUC_PREREQ(4, 9)

#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

namespace scheduler
{

class ThreadControlBlockList;

}	// namespace scheduler

namespace synchronization
{

/// CallOnceControlBlock class implements functionality of OnceFlag class and callOnce()
/// \note This class requires GCC 4.9.
class CallOnceControlBlock
{
public:

	/**
	 * \brief CallOnceControlBlock's constructor
	 */

	constexpr CallOnceControlBlock() :
			blockedList_{},
			done_{}
	{

	}

private:

	/// Functor is a type-erased interface for functors which execute bounded function with bounded arguments
	class Functor : public estd::TypeErasedFunctor<void()>
	{

	};

	/**
	 * \brief BoundedFunctor is a type-erased Functor which calls its bounded functor
	 *
	 * \param F is the type of bounded functor
	 */

	template<typename F>
	class BoundedFunctor : public Functor
	{
	public:

		/**
		 * \brief BoundedFunctor's constructor
		 *
		 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct
		 * internal bounded functor
		 */

		constexpr explicit BoundedFunctor(F&& boundedFunctor) :
				boundedFunctor_{std::move(boundedFunctor)}
		{

		}

		/**
		 * \brief BoundedFunctor's function call operator
		 *
		 * Calls the bounded functor.
		 */

		virtual void operator()() const override
		{
			boundedFunctor_();
		}

	private:

		/// bounded functor
		F boundedFunctor_;
	};

	/**
	 * \brief Helper factory function to make BoundedFunctor object with deduced template arguments
	 *
	 * \param F is the type of bounded functor
	 *
	 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct internal
	 * bounded functor
	 *
	 * \return BoundedFunctor object with deduced template arguments
	 */

	template<typename F>
	constexpr static BoundedFunctor<F> makeBoundedFunctor(F&& boundedFunctor)
	{
		return BoundedFunctor<F>{std::move(boundedFunctor)};
	}

	/**
	 * \brief Implements callOnce() using type-erased functor.
	 *
	 * Does nothing if any function was already called for this object. If the function is currently being executed, but
	 * not yet done, then the calling thread is blocked. In other case the function is executed and - after it is done -
	 * all blocked threads are unblocked.
	 *
	 * \param [in] functor is a reference to functor which will execute bounded function with bounded arguments
	 */

	void callOnceImplementation(const Functor& functor);

	/// pointer to stack-allocated list of ThreadControlBlock objects blocked on associated OnceFlag
	scheduler::ThreadControlBlockList* blockedList_;

	/// tells whether any function was already called for this object (true) or not (false)
	bool done_;
};

}	// namespace synchronization

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_
<commit_msg>CallOnceControlBlock: add function call operator<commit_after>/**
 * \file
 * \brief CallOnceControlBlock class header
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-07-15
 */

#ifndef INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_
#define INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_

#include "distortos/estd/invoke.hpp"
#include "distortos/estd/TypeErasedFunctor.hpp"

#include <sys/features.h>

namespace distortos
{

/// GCC 4.9 is needed for CallOnceControlBlock::operator()() function - and thus for OnceFlag and callOnce() - earlier
/// versions don't support parameter pack expansion in lambdas
#define DISTORTOS_CALLONCE_SUPPORTED	__GNUC_PREREQ(4, 9)

#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

namespace scheduler
{

class ThreadControlBlockList;

}	// namespace scheduler

namespace synchronization
{

/// CallOnceControlBlock class implements functionality of OnceFlag class and callOnce()
/// \note This class requires GCC 4.9.
class CallOnceControlBlock
{
public:

	/**
	 * \brief CallOnceControlBlock's constructor
	 */

	constexpr CallOnceControlBlock() :
			blockedList_{},
			done_{}
	{

	}

	/**
	 * \brief CallOnceControlBlock's function call operator
	 *
	 * Does nothing if any function was already called for this object. In other case provided function and arguments
	 * are wrapped in a type-erased functor and passed to callOnceImplementation().
	 *
	 * \param Function is the function object that will be executed
	 * \param Args are the arguments for \a Function
	 *
	 * \param [in] function is the function object that will be executed
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	void operator()(Function&& function, Args&&... args)
	{
		if (done_ == true)	// function already executed?
			return;

		const auto functor = makeBoundedFunctor(
				[&function, &args...]()
				{
					estd::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
				});
		callOnceImplementation(functor);
	}

private:

	/// Functor is a type-erased interface for functors which execute bounded function with bounded arguments
	class Functor : public estd::TypeErasedFunctor<void()>
	{

	};

	/**
	 * \brief BoundedFunctor is a type-erased Functor which calls its bounded functor
	 *
	 * \param F is the type of bounded functor
	 */

	template<typename F>
	class BoundedFunctor : public Functor
	{
	public:

		/**
		 * \brief BoundedFunctor's constructor
		 *
		 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct
		 * internal bounded functor
		 */

		constexpr explicit BoundedFunctor(F&& boundedFunctor) :
				boundedFunctor_{std::move(boundedFunctor)}
		{

		}

		/**
		 * \brief BoundedFunctor's function call operator
		 *
		 * Calls the bounded functor.
		 */

		virtual void operator()() const override
		{
			boundedFunctor_();
		}

	private:

		/// bounded functor
		F boundedFunctor_;
	};

	/**
	 * \brief Helper factory function to make BoundedFunctor object with deduced template arguments
	 *
	 * \param F is the type of bounded functor
	 *
	 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct internal
	 * bounded functor
	 *
	 * \return BoundedFunctor object with deduced template arguments
	 */

	template<typename F>
	constexpr static BoundedFunctor<F> makeBoundedFunctor(F&& boundedFunctor)
	{
		return BoundedFunctor<F>{std::move(boundedFunctor)};
	}

	/**
	 * \brief Implements callOnce() using type-erased functor.
	 *
	 * Does nothing if any function was already called for this object. If the function is currently being executed, but
	 * not yet done, then the calling thread is blocked. In other case the function is executed and - after it is done -
	 * all blocked threads are unblocked.
	 *
	 * \param [in] functor is a reference to functor which will execute bounded function with bounded arguments
	 */

	void callOnceImplementation(const Functor& functor);

	/// pointer to stack-allocated list of ThreadControlBlock objects blocked on associated OnceFlag
	scheduler::ThreadControlBlockList* blockedList_;

	/// tells whether any function was already called for this object (true) or not (false)
	bool done_;
};

}	// namespace synchronization

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_
<|endoftext|>


<commit_before>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_BOX_HPP
#define OUZEL_MATH_BOX_HPP

#include <cstdint>
#include <limits>
#include "Size.hpp"
#include "Vector.hpp"

namespace ouzel
{
    template <typename T, std::size_t N> class Box final
    {
    public:
        Vector<T, N> min;
        Vector<T, N> max;

        Box() noexcept
        {
            for (T& v : min.v)
                v = std::numeric_limits<T>::max();
            for (T& v : max.v)
                v = std::numeric_limits<T>::lowest();
        }

        constexpr Box(const Vector<T, N>& initMin, const Vector<T, N>& initMax) noexcept:
            min(initMin), max(initMax)
        {
        }

        template <std::size_t N2>
        explicit Box(const Box<T, N2>& box) noexcept:
            min{Vector<T, N>{box.min}}, max{Vector<T, N>{box.max}}
        {
        }

        auto getCenter() const noexcept
        {
            return (min + max) / T(2);
        }

        auto intersects(const Box& aabb) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (aabb.min.v[0] > max.v[0]) return false;
            for (std::size_t i = 0; i < N; ++i)
                if (aabb.max.v[0] < min.v[0]) return false;
            return true;
        }

        auto containsPoint(const Vector<T, N>& point) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] < min.v[i]) return false;
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] > max.v[i]) return false;
            return true;
        }

        void merge(const Box& box) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                min.v[i] = std::min(min.v[i], box.min.v[i]);
            for (std::size_t i = 0; i < N; ++i)
                max.v[i] = std::max(max.v[i], box.max.v[i]);
        }

        void reset() noexcept
        {
            for (T& v : min.v)
                v = std::numeric_limits<T>::max();
            for (T& v : max.v)
                v = std::numeric_limits<T>::lowest();
        }

        auto isEmpty() const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (min.v[i] > max.v[i])
                    return true;
            return false;
        }

        void insertPoint(const Vector<T, N>& point) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] < min.v[i]) min.v[i] = point.v[i];
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] > max.v[i]) max.v[i] = point.v[i];
        }

        constexpr auto operator+(const Vector<T, N>& v) const noexcept
        {
            return Box(min + v, max + v);
        }

        auto& operator+=(const Vector<T, N>& v) noexcept
        {
            min += v;
            max += v;
            return *this;
        }

        constexpr auto operator-(const Vector<T, N>& v) const noexcept
        {
            return Box(min - v, max - v);
        }

        auto& operator-=(const Vector<T, N>& v) noexcept
        {
            min -= v;
            max -= v;
            return *this;
        }

        auto getSize() const noexcept
        {
            return Size<T, N>{max - min};
        }
    };
}

#endif // OUZEL_MATH_BOX_HPP
<commit_msg>Do point insertion in a single loop<commit_after>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_BOX_HPP
#define OUZEL_MATH_BOX_HPP

#include <cstdint>
#include <limits>
#include "Size.hpp"
#include "Vector.hpp"

namespace ouzel
{
    template <typename T, std::size_t N> class Box final
    {
    public:
        Vector<T, N> min;
        Vector<T, N> max;

        Box() noexcept
        {
            for (T& v : min.v)
                v = std::numeric_limits<T>::max();
            for (T& v : max.v)
                v = std::numeric_limits<T>::lowest();
        }

        constexpr Box(const Vector<T, N>& initMin, const Vector<T, N>& initMax) noexcept:
            min(initMin), max(initMax)
        {
        }

        template <std::size_t N2>
        explicit Box(const Box<T, N2>& box) noexcept:
            min{Vector<T, N>{box.min}}, max{Vector<T, N>{box.max}}
        {
        }

        auto getCenter() const noexcept
        {
            return (min + max) / T(2);
        }

        auto intersects(const Box& aabb) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (aabb.min.v[0] > max.v[0]) return false;
            for (std::size_t i = 0; i < N; ++i)
                if (aabb.max.v[0] < min.v[0]) return false;
            return true;
        }

        auto containsPoint(const Vector<T, N>& point) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] < min.v[i]) return false;
            for (std::size_t i = 0; i < N; ++i)
                if (point.v[i] > max.v[i]) return false;
            return true;
        }

        void merge(const Box& box) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                min.v[i] = std::min(min.v[i], box.min.v[i]);
            for (std::size_t i = 0; i < N; ++i)
                max.v[i] = std::max(max.v[i], box.max.v[i]);
        }

        void reset() noexcept
        {
            for (T& v : min.v)
                v = std::numeric_limits<T>::max();
            for (T& v : max.v)
                v = std::numeric_limits<T>::lowest();
        }

        auto isEmpty() const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (min.v[i] > max.v[i])
                    return true;
            return false;
        }

        void insertPoint(const Vector<T, N>& point) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
            {
                if (point.v[i] < min.v[i]) min.v[i] = point.v[i];
                if (point.v[i] > max.v[i]) max.v[i] = point.v[i];
            }
        }

        constexpr auto operator+(const Vector<T, N>& v) const noexcept
        {
            return Box(min + v, max + v);
        }

        auto& operator+=(const Vector<T, N>& v) noexcept
        {
            min += v;
            max += v;
            return *this;
        }

        constexpr auto operator-(const Vector<T, N>& v) const noexcept
        {
            return Box(min - v, max - v);
        }

        auto& operator-=(const Vector<T, N>& v) noexcept
        {
            min -= v;
            max -= v;
            return *this;
        }

        auto getSize() const noexcept
        {
            return Size<T, N>{max - min};
        }
    };
}

#endif // OUZEL_MATH_BOX_HPP
<|endoftext|>


<commit_before>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_GRAPHICS_RASTERIZERSTATE_HPP
#define OUZEL_GRAPHICS_RASTERIZERSTATE_HPP

namespace ouzel
{
    namespace graphics
    {
        enum class CullMode
        {
            NONE,
            FRONT,
            BACK
        };

        enum class FillMode
        {
            SOLID,
            WIREFRAME
        };
    } // namespace graphics
} // namespace ouzel

#endif // OUZEL_GRAPHICS_RASTERIZERSTATE_HPP
<commit_msg>Add enum for face direction<commit_after>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_GRAPHICS_RASTERIZERSTATE_HPP
#define OUZEL_GRAPHICS_RASTERIZERSTATE_HPP

namespace ouzel
{
    namespace graphics
    {
        enum class CullMode
        {
            NONE,
            FRONT,
            BACK
        };

        enum class FillMode
        {
            SOLID,
            WIREFRAME
        };

        enum class Face
        {
            CLOCK_WISE,
            COUNTER_CLOCK_WISE
        };
    } // namespace graphics
} // namespace ouzel

#endif // OUZEL_GRAPHICS_RASTERIZERSTATE_HPP
<|endoftext|>


<commit_before>//Sign B Config

#include "FastLED.h"

#define CHIPSET     WS2812B
#define COLOR_ORDER GRB

#define BRIGHTNESS  64

#define N_LEDS_WING_LEFT  266
#define N_LEDS_WING_RIGHT 266
#define N_LEDS_HEAD       45
#define N_LEDS_BODY_UPPER 275
#define N_LEDS_BODY_LOWER 145
#define N_LEDS_TAIL       30

#define MAX_STRIP_LENGTH  275
#define N_STRIPS          6

#define PIN_WING_LEFT   1
#define PIN_WING_RIGHT  2
#define PIN_HEAD        3
#define PIN_BODY_UPPER  4
#define PIN_BODY_LOWER  5
#define PIN_TAIL        6

#define INDEX_WING_LEFT  0
#define INDEX_WING_RIGHT 1
#define INDEX_HEAD       2
#define INDEX_BODY_UPPER 3
#define INDEX_BODY_LOWER 4
#define INDEX_TAIL       5

/*
CRGB signWingLeft[N_LEDS_WING_LEFT];
CRGB signWingRight[N_LEDS_WING_RIGHT];
CRGB signHead[N_LEDS_HEAD];
CRGB signBodyUpper[N_LEDS_BODY_UPPER];
CRGB signBodyLower[N_LEDS_BODY_LOWER];
CRGB signTail[N_LEDS_TAIL];
*/

CRGB leds[N_STRIPS][MAX_STRIP_LENGTH]

void setup() {
  /*
  FastLED.addLeds<CHIPSET, PIN_WING_LEFT,   COLOR_ORDER>(signWingLeft,  N_LEDS_WING_LEFT);
  FastLED.addLeds<CHIPSET, PIN_WING_RIGHT,  COLOR_ORDER>(signWingRight, N_LEDS_WING_RIGHT);
  FastLED.addLeds<CHIPSET, PIN_HEAD,        COLOR_ORDER>(signHead,      N_LEDS_HEAD);
  FastLED.addLeds<CHIPSET, PIN_BODY_UPPER,  COLOR_ORDER>(signBodyUpper, N_LEDS_BODY_UPPER);
  FastLED.addLeds<CHIPSET, PIN_BODY_LOWER,  COLOR_ORDER>(signBodyLower, N_LEDS_BODY_LOWER);
  FastLED.addLeds<CHIPSET, PIN_TAIL,        COLOR_ORDER>(signTail,      N_LEDS_TAIL);
  */

  FastLED.addLeds<CHIPSET, PIN_WING_LEFT,   COLOR_ORDER>(leds[INDEX_WING_LEFT],  N_LEDS_WING_LEFT);
  FastLED.addLeds<CHIPSET, PIN_WING_RIGHT,  COLOR_ORDER>(leds[INDEX_WING_RIGHT], N_LEDS_WING_RIGHT);
  FastLED.addLeds<CHIPSET, PIN_HEAD,        COLOR_ORDER>(leds[INDEX_HEAD],       N_LEDS_HEAD);
  FastLED.addLeds<CHIPSET, PIN_BODY_UPPER,  COLOR_ORDER>(leds[INDEX_BODY_UPPER], N_LEDS_BODY_UPPER);
  FastLED.addLeds<CHIPSET, PIN_BODY_LOWER,  COLOR_ORDER>(leds[INDEX_BODY_LOWER], N_LEDS_BODY_LOWER);
  FastLED.addLeds<CHIPSET, PIN_TAIL,        COLOR_ORDER>(leds[INDEX_TAIL],       N_LEDS_TAIL);
}

void loop() {

}

int XY(int x, int y) {
  if(y > HEIGHT) {
    y = HEIGHT;
  }
  if(y < 0) {
    y = 0;
  }
  if(x > WIDTH) {
    x = WIDTH;
  }
  if(x < 0) {
    x = 0;
  }
  // for a serpentine layout reverse every 2nd row:
  if(x % 2 == 1) {
    return (x * (WIDTH) + (HEIGHT - y -1));
  }
  else {
    // use that line only, if you have all rows beginning at the same side
    return (x * (WIDTH) + y);
  }
}

uint8_t getStripFromPixelIndex(uint16_t i) {
  if(i >= 0 && i < N_LEDS_WING_LEFT) {
    return INDEX_WING_LEFT;
  }
  if(i >= N_LEDS_WING_LEFT && i < N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT) {
    return INDEX_WING_RIGHT;
  }
  if(i >= N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT && i < N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD) {
    return INDEX_HEAD;
  }
  if(i >= N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD && i < N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD) {
    return INDEX_HEAD;
  }
}
<commit_msg>pixel finder functions<commit_after>//Sign B basic functions

#include "FastLED.h"

#define CHIPSET           WS2812B
#define COLOR_ORDER       GRB

#define BRIGHTNESS        64

//The number of pixels in each section:
#define N_LEDS_WING_LEFT  266
#define N_LEDS_WING_RIGHT 266
#define N_LEDS_HEAD       45
#define N_LEDS_BODY_UPPER 275
#define N_LEDS_BODY_LOWER 145
#define N_LEDS_TAIL       30

#define MAX_STRIP_LENGTH  275
#define N_STRIPS          6

//The data pins
#define PIN_WING_LEFT     1
#define PIN_WING_RIGHT    2
#define PIN_HEAD          3
#define PIN_BODY_UPPER    4
#define PIN_BODY_LOWER    5
#define PIN_TAIL          6

//The order in which the strips are wired
#define INDEX_WING_LEFT   0
#define INDEX_WING_RIGHT  1
#define INDEX_HEAD        2
#define INDEX_BODY_UPPER  3
#define INDEX_BODY_LOWER  4
#define INDEX_TAIL        5

//The dimensions of the virtual pixelMap to be rendered
#define MAP_WIDTH         111
#define MAP_HEIGHT        25

//An integer to indicate a non-existent pixel
#define DNE               -1

/*
CRGB signWingLeft[N_LEDS_WING_LEFT];
CRGB signWingRight[N_LEDS_WING_RIGHT];
CRGB signHead[N_LEDS_HEAD];
CRGB signBodyUpper[N_LEDS_BODY_UPPER];
CRGB signBodyLower[N_LEDS_BODY_LOWER];
CRGB signTail[N_LEDS_TAIL];
*/

int16_t pixelMap[MAP_HEIGHT][MAP_WIDTH] = {
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},

  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},

  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},

  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},

  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,},
  {,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,,,,,  ,}
};

CRGB leds[N_STRIPS][MAX_STRIP_LENGTH];

uint16_t offsetWingLeft = N_LEDS_WING_LEFT;
uint16_t offsetWingRight = N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT;
uint16_t offsetHead = N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD;
uint16_t offsetBodyUpper = N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD + N_LEDS_BODY_UPPER;
uint16_t offsetBodyLower = N_LEDS_WING_LEFT + N_LEDS_WING_RIGHT + N_LEDS_HEAD + N_LEDS_BODY_UPPER + N_LEDS_BODY_LOWER;

void setup() {
  /*
  FastLED.addLeds<CHIPSET, PIN_WING_LEFT,   COLOR_ORDER>(signWingLeft,  N_LEDS_WING_LEFT);
  FastLED.addLeds<CHIPSET, PIN_WING_RIGHT,  COLOR_ORDER>(signWingRight, N_LEDS_WING_RIGHT);
  FastLED.addLeds<CHIPSET, PIN_HEAD,        COLOR_ORDER>(signHead,      N_LEDS_HEAD);
  FastLED.addLeds<CHIPSET, PIN_BODY_UPPER,  COLOR_ORDER>(signBodyUpper, N_LEDS_BODY_UPPER);
  FastLED.addLeds<CHIPSET, PIN_BODY_LOWER,  COLOR_ORDER>(signBodyLower, N_LEDS_BODY_LOWER);
  FastLED.addLeds<CHIPSET, PIN_TAIL,        COLOR_ORDER>(signTail,      N_LEDS_TAIL);
  */

  FastLED.addLeds<CHIPSET, PIN_WING_LEFT,   COLOR_ORDER>(leds[INDEX_WING_LEFT],  N_LEDS_WING_LEFT);
  FastLED.addLeds<CHIPSET, PIN_WING_RIGHT,  COLOR_ORDER>(leds[INDEX_WING_RIGHT], N_LEDS_WING_RIGHT);
  FastLED.addLeds<CHIPSET, PIN_HEAD,        COLOR_ORDER>(leds[INDEX_HEAD],       N_LEDS_HEAD);
  FastLED.addLeds<CHIPSET, PIN_BODY_UPPER,  COLOR_ORDER>(leds[INDEX_BODY_UPPER], N_LEDS_BODY_UPPER);
  FastLED.addLeds<CHIPSET, PIN_BODY_LOWER,  COLOR_ORDER>(leds[INDEX_BODY_LOWER], N_LEDS_BODY_LOWER);
  FastLED.addLeds<CHIPSET, PIN_TAIL,        COLOR_ORDER>(leds[INDEX_TAIL],       N_LEDS_TAIL);
}

void loop() {

}

int XY(int x, int y) {
  return pixelMap[y][x];
}

/*
**  Use the following two functions together to get strip index and pixel index
**  E.G. leds[getStripFromPixelIndex(i)][getPixelIndexInStrip(i)]
*/

uint8_t getStripFromPixelIndex(uint16_t i) {
  if(i >= 0 && i < offsetWingLeft) {
    return INDEX_WING_LEFT;
  }
  if(i >= offsetWingLeft && i < offsetWingRight) {
    return INDEX_WING_RIGHT;
  }
  if(i >= offsetWingRight && i < offsetHead) {
    return INDEX_HEAD;
  }
  if(i >= offsetHead && i < offsetBodyUpper) {
    return INDEX_BODY_UPPER;
  }
  if(i >= offsetBodyUpper && i < offsetBodyLower) {
    return INDEX_BODY_LOWER;
  }
  if(i >= offsetBodyLower && i < offsetTail) {
    return INDEX_TAIL;
  }
}

uint16_t getPixelIndexInStrip(uint16_t i) {
  if(i >= 0 && i < offsetWingLeft) {
    return i;
  }
  if(i >= offsetWingLeft && i < offsetWingRight) {
    return i - offsetWingLeft;
  }
  if(i >= offsetWingRight && i < offsetHead) {
    return i - offsetWingRight;
  }
  if(i >= offsetHead && i < offsetBodyUpper) {
    return i - offsetHead;
  }
  if(i >= offsetBodyUpper && i < offsetBodyLower) {
    return i - offsetBodyUpper;
  }
  if(i >= offsetBodyLower && i < offsetTail) {
    return i - offsetBodyLower;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010 .SE, The Internet Infrastructure Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 SimpleConfigLoader.cpp

 Loads the configuration from the configuration file.
 *****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <limits.h>
#include <unistd.h>
#include "config.h"
#if defined(HAVE_GETPWUID_R)
# include <sys/types.h>
# include <pwd.h>
#endif
#include "SimpleConfigLoader.h"
#include "log.h"
#include "Configuration.h"

// Initialise the one-and-only instance
std::auto_ptr<SimpleConfigLoader> SimpleConfigLoader::instance(NULL);

// Return the one-and-only instance
SimpleConfigLoader* SimpleConfigLoader::i()
{
	if (instance.get() == NULL)
	{
		instance = std::auto_ptr<SimpleConfigLoader>(new SimpleConfigLoader());
	}

	return instance.get();
}

// Constructor
SimpleConfigLoader::SimpleConfigLoader()
{
}

// Load the configuration
bool SimpleConfigLoader::loadConfiguration()
{
	char* configPath = getConfigPath();

	FILE* fp = fopen(configPath,"r");

	if (fp == NULL)
	{
		ERROR_MSG("Could not open the config file: %s", configPath);
		free(configPath);
		return false;
	}
	free(configPath);

	char fileBuf[1024];

	// Format in config file
	//
	// <name> = <value>
	// # Line is ignored

	while (fgets(fileBuf, sizeof(fileBuf), fp) != NULL)
	{
		// End the string at the first comment or newline
		fileBuf[strcspn(fileBuf, "#\n\r")] = '\0';

		// Get the first part of the line
		char* name = strtok(fileBuf, "=");
		if (name == NULL)
		{
			continue;
		}

		// Trim the name
		char* trimmedName = trimString(name);
		if (trimmedName == NULL)
		{
			continue;
		}

		// Get the second part of the line
		char* value = strtok(NULL, "=");
		if(value == NULL) {
			free(trimmedName);
			continue;
		}

		// Trim the value
		char* trimmedValue = trimString(value);
		if (trimmedValue == NULL)
		{
			free(trimmedName);
			continue;
		}

		// Save name,value
		std::string stringName(trimmedName);
		std::string stringValue(trimmedValue);
		free(trimmedName);
		free(trimmedValue);

		switch (Configuration::i()->getType(stringName))
		{
			case CONFIG_TYPE_STRING:
				Configuration::i()->setString(stringName, stringValue);
				break;
			case CONFIG_TYPE_INT:
				Configuration::i()->setInt(stringName, atoi(stringValue.c_str()));
				break;
			case CONFIG_TYPE_BOOL:
				bool boolValue;
				if (string2bool(stringValue, &boolValue))
				{
					Configuration::i()->setBool(stringName, boolValue);
				}
				else
				{
					WARNING_MSG("The value %s is not a boolean", stringValue.c_str());
				}
				break;
			case CONFIG_TYPE_UNSUPPORTED:
			default:
				WARNING_MSG("The following configuration is not supported: %s = %s",
					stringName.c_str(), stringValue.c_str());
				break;
		}
	}

	fclose(fp);

	return true;
}

// Get the boolean value from a string
bool SimpleConfigLoader::string2bool(std::string stringValue, bool* boolValue)
{
	// Convert to lowercase
	std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), tolower);

	if (stringValue.compare("true") == 0)
	{
		*boolValue = true;
		return true;
	}

	if (stringValue.compare("false") == 0)
	{
		*boolValue = false;
		return true;
	}

	return false;
}

#define CONFIG_FILE ".config/softhsm2/softhsm2.conf"

/* Returns a user-specific path for configuration.
 */
static char *get_user_path(void)
{
	char path[_POSIX_PATH_MAX];
	const char *home_dir = getenv("HOME");

	if (home_dir != NULL && home_dir[0] != 0) {
		snprintf(path, sizeof(path), "%s/" CONFIG_FILE, home_dir);
		if (access(path, R_OK) == 0)
			return strdup(path);
		else
			goto fail;
	}

#if defined(HAVE_GETPWUID_R)
	if (home_dir == NULL || home_dir[0] == '\0') {
		struct passwd *pwd;
		struct passwd _pwd;
		int ret;
		char tmp[512];

		ret = getpwuid_r(getuid(), &_pwd, tmp, sizeof(tmp), &pwd);
		if (ret == 0 && pwd != NULL) {
			snprintf(path, sizeof(path), "%s/" CONFIG_FILE, pwd->pw_dir);
			if (access(path, R_OK) == 0)
				return strdup(path);
			else
				goto fail;
		}
	}
#endif

 fail:
	return NULL;
}

char* SimpleConfigLoader::getConfigPath()
{
	const char* configPath = getenv("SOFTHSM2_CONF");
	char *tpath;

	if (configPath == NULL) {
		tpath = get_user_path();
		if (tpath != NULL) {
			return tpath;
		}
		configPath = DEFAULT_SOFTHSM2_CONF;
	}

	return strdup(configPath);
}

char* SimpleConfigLoader::trimString(char* text)
{
	if (text == NULL)
	{
		return NULL;
	}

	int startPos = 0;
	int endPos = strlen(text) - 1;

	// Find the first position without a space
	while (startPos <= endPos && isspace((int)*(text + startPos)))
	{
		startPos++;
	}
	// Find the last position without a space
	while (startPos <= endPos && isspace((int)*(text + endPos)))
	{
		endPos--;
	}

	// We must have a valid string
	int length = endPos - startPos + 1;
	if (length <= 0)
	{
		return NULL;
	}

	// Create the trimmed text
	char* trimmedText = (char*)malloc(length + 1);
	if (trimmedText == NULL)
	{
		return NULL;
	}
	trimmedText[length] = '\0';
	memcpy(trimmedText, text + startPos, length);

	return trimmedText;
}
<commit_msg>Allow reading configuration file in windows systems as well<commit_after>/*
 * Copyright (c) 2010 .SE, The Internet Infrastructure Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 SimpleConfigLoader.cpp

 Loads the configuration from the configuration file.
 *****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <limits.h>
#ifdef _WIN32
# include <io.h>
#else
# include <unistd.h>
#endif
#include "config.h"
#if defined(HAVE_GETPWUID_R)
# include <sys/types.h>
# include <pwd.h>
#endif
#include "SimpleConfigLoader.h"
#include "log.h"
#include "Configuration.h"

// Initialise the one-and-only instance
std::auto_ptr<SimpleConfigLoader> SimpleConfigLoader::instance(NULL);

// Return the one-and-only instance
SimpleConfigLoader* SimpleConfigLoader::i()
{
	if (instance.get() == NULL)
	{
		instance = std::auto_ptr<SimpleConfigLoader>(new SimpleConfigLoader());
	}

	return instance.get();
}

// Constructor
SimpleConfigLoader::SimpleConfigLoader()
{
}

// Load the configuration
bool SimpleConfigLoader::loadConfiguration()
{
	char* configPath = getConfigPath();

	FILE* fp = fopen(configPath,"r");

	if (fp == NULL)
	{
		ERROR_MSG("Could not open the config file: %s", configPath);
		free(configPath);
		return false;
	}
	free(configPath);

	char fileBuf[1024];

	// Format in config file
	//
	// <name> = <value>
	// # Line is ignored

	while (fgets(fileBuf, sizeof(fileBuf), fp) != NULL)
	{
		// End the string at the first comment or newline
		fileBuf[strcspn(fileBuf, "#\n\r")] = '\0';

		// Get the first part of the line
		char* name = strtok(fileBuf, "=");
		if (name == NULL)
		{
			continue;
		}

		// Trim the name
		char* trimmedName = trimString(name);
		if (trimmedName == NULL)
		{
			continue;
		}

		// Get the second part of the line
		char* value = strtok(NULL, "=");
		if(value == NULL) {
			free(trimmedName);
			continue;
		}

		// Trim the value
		char* trimmedValue = trimString(value);
		if (trimmedValue == NULL)
		{
			free(trimmedName);
			continue;
		}

		// Save name,value
		std::string stringName(trimmedName);
		std::string stringValue(trimmedValue);
		free(trimmedName);
		free(trimmedValue);

		switch (Configuration::i()->getType(stringName))
		{
			case CONFIG_TYPE_STRING:
				Configuration::i()->setString(stringName, stringValue);
				break;
			case CONFIG_TYPE_INT:
				Configuration::i()->setInt(stringName, atoi(stringValue.c_str()));
				break;
			case CONFIG_TYPE_BOOL:
				bool boolValue;
				if (string2bool(stringValue, &boolValue))
				{
					Configuration::i()->setBool(stringName, boolValue);
				}
				else
				{
					WARNING_MSG("The value %s is not a boolean", stringValue.c_str());
				}
				break;
			case CONFIG_TYPE_UNSUPPORTED:
			default:
				WARNING_MSG("The following configuration is not supported: %s = %s",
					stringName.c_str(), stringValue.c_str());
				break;
		}
	}

	fclose(fp);

	return true;
}

// Get the boolean value from a string
bool SimpleConfigLoader::string2bool(std::string stringValue, bool* boolValue)
{
	// Convert to lowercase
	std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), tolower);

	if (stringValue.compare("true") == 0)
	{
		*boolValue = true;
		return true;
	}

	if (stringValue.compare("false") == 0)
	{
		*boolValue = false;
		return true;
	}

	return false;
}

#define CONFIG_FILE ".config/softhsm2/softhsm2.conf"

/* Returns a user-specific path for configuration.
 */
static char *get_user_path(void)
{
#ifdef _WIN32
	char path[512];
	const char *home_drive = getenv("HOMEDRIVE");
	const char *home_path = getenv("HOMEPATH");

	if (home_drive && home_path) {
		snprintf(path, sizeof(path), "%s%s\\softhsm2.conf", home_drive, home_path);

		if (_access(path, 0) == 0)
			return strdup(path);
	}
	goto fail;
#else
	char path[_POSIX_PATH_MAX];
	const char *home_dir = getenv("HOME");

	if (home_dir != NULL && home_dir[0] != 0) {
		snprintf(path, sizeof(path), "%s/" CONFIG_FILE, home_dir);
		if (access(path, R_OK) == 0)
			return strdup(path);
		else
			goto fail;
	}

# if defined(HAVE_GETPWUID_R)
	if (home_dir == NULL || home_dir[0] == '\0') {
		struct passwd *pwd;
		struct passwd _pwd;
		int ret;
		char tmp[512];

		ret = getpwuid_r(getuid(), &_pwd, tmp, sizeof(tmp), &pwd);
		if (ret == 0 && pwd != NULL) {
			snprintf(path, sizeof(path), "%s/" CONFIG_FILE, pwd->pw_dir);
			if (access(path, R_OK) == 0)
				return strdup(path);
			else
				goto fail;
		}
	}
# endif
#endif

 fail:
	return NULL;
}

char* SimpleConfigLoader::getConfigPath()
{
	const char* configPath = getenv("SOFTHSM2_CONF");
	char *tpath;

	if (configPath == NULL) {
		tpath = get_user_path();
		if (tpath != NULL) {
			return tpath;
		}
		configPath = DEFAULT_SOFTHSM2_CONF;
	}

	return strdup(configPath);
}

char* SimpleConfigLoader::trimString(char* text)
{
	if (text == NULL)
	{
		return NULL;
	}

	int startPos = 0;
	int endPos = strlen(text) - 1;

	// Find the first position without a space
	while (startPos <= endPos && isspace((int)*(text + startPos)))
	{
		startPos++;
	}
	// Find the last position without a space
	while (startPos <= endPos && isspace((int)*(text + endPos)))
	{
		endPos--;
	}

	// We must have a valid string
	int length = endPos - startPos + 1;
	if (length <= 0)
	{
		return NULL;
	}

	// Create the trimmed text
	char* trimmedText = (char*)malloc(length + 1);
	if (trimmedText == NULL)
	{
		return NULL;
	}
	trimmedText[length] = '\0';
	memcpy(trimmedText, text + startPos, length);

	return trimmedText;
}
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2017 Roman Lebedev

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; withexpected even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "common/Common.h" // for uchar8, clampBits, isIn, isPower...
#include <algorithm>       // for fill, min, equal
#include <cassert>         // for assert
#include <cstddef>         // for size_t
#include <gtest/gtest.h>   // for make_tuple, get, IsNullLiteralHe...
#include <limits>          // for numeric_limits
#include <memory>          // for unique_ptr
#include <string>          // for basic_string, string, allocator
#include <vector>          // for vector

using namespace std;
using namespace RawSpeed;

using powerOfTwoType = std::tr1::tuple<int, bool>;
class PowerOfTwoTest : public ::testing::TestWithParam<powerOfTwoType> {
protected:
  PowerOfTwoTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    expected = std::tr1::get<1>(GetParam());
  }

  int in;        // input
  bool expected; // expected output
};
static const powerOfTwoType powerOfTwoValues[] = {
    make_tuple(0, true),  make_tuple(1, true),   make_tuple(2, true),
    make_tuple(3, false), make_tuple(4, true),   make_tuple(5, false),
    make_tuple(6, false), make_tuple(7, false),  make_tuple(8, true),
    make_tuple(9, false), make_tuple(10, false), make_tuple(11, false),

};
INSTANTIATE_TEST_CASE_P(PowerOfTwoTest, PowerOfTwoTest,
                        ::testing::ValuesIn(powerOfTwoValues));
TEST_P(PowerOfTwoTest, PowerOfTwoTest) {
  ASSERT_EQ(isPowerOfTwo(in), expected);
}

using RoundUpType = std::tr1::tuple<size_t, size_t, size_t>;
class RoundUpTest : public ::testing::TestWithParam<RoundUpType> {
protected:
  RoundUpTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    multiple = std::tr1::get<1>(GetParam());
    expected = std::tr1::get<2>(GetParam());
  }

  size_t in; // input
  size_t multiple;
  size_t expected; // expected output
};
static const RoundUpType RoundUpValues[] = {
    make_tuple(0, 0, 0),    make_tuple(0, 10, 0),   make_tuple(10, 0, 10),
    make_tuple(10, 10, 10), make_tuple(10, 1, 10),  make_tuple(10, 2, 10),
    make_tuple(10, 3, 12),  make_tuple(10, 4, 12),  make_tuple(10, 5, 10),
    make_tuple(10, 6, 12),  make_tuple(10, 7, 14),  make_tuple(10, 8, 16),
    make_tuple(10, 9, 18),  make_tuple(10, 11, 11), make_tuple(10, 12, 12),

};
INSTANTIATE_TEST_CASE_P(RoundUpTest, RoundUpTest,
                        ::testing::ValuesIn(RoundUpValues));
TEST_P(RoundUpTest, RoundUpTest) { ASSERT_EQ(roundUp(in, multiple), expected); }

using IsInType = std::tr1::tuple<string, bool>;
class IsInTest : public ::testing::TestWithParam<IsInType> {
protected:
  IsInTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    expected = std::tr1::get<1>(GetParam());
  }

  string in;     // input
  bool expected; // expected output
};

static const IsInType IsInValues[] = {
    make_tuple("foo", true),    make_tuple("foo2", true),
    make_tuple("bar", true),    make_tuple("baz", true),
    make_tuple("foo1", false),  make_tuple("bar2", false),
    make_tuple("baz-1", false), make_tuple("quz", false),

};
INSTANTIATE_TEST_CASE_P(IsInTest, IsInTest, ::testing::ValuesIn(IsInValues));
TEST_P(IsInTest, IsInTest) {
  ASSERT_EQ(isIn(in, {"foo", "foo2", "bar", "baz"}), expected);
}

using ClampBitsType =
    std::tr1::tuple<int, int, ushort16>;
class ClampBitsTest : public ::testing::TestWithParam<ClampBitsType> {
protected:
  ClampBitsTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    n = std::tr1::get<1>(GetParam());
    expected = std::tr1::get<2>(GetParam());
  }

  int in; // input
  int n;
  ushort16 expected; // expected output
};

#define ROWS(v, p, pv) make_tuple((v), (p), ((v) <= (pv)) ? (v) : (pv)),

#define THREEROWS(v, p)                                                        \
  ROWS(((1 << (v)) - 1), (p), ((1 << (p)) - 1))                                \
  ROWS(((1 << (v)) - 0), (p), ((1 << (p)) - 1))                                \
  ROWS(((1 << (v)) + 1), (p), ((1 << (p)) - 1))

#define MOREROWS(v)                                                            \
  THREEROWS(v, 0)                                                              \
  THREEROWS(v, 1)                                                              \
  THREEROWS(v, 2)                                                              \
  THREEROWS(v, 4)                                                              \
  THREEROWS(v, 8) THREEROWS(v, 16)

#define GENERATE()                                                             \
  MOREROWS(0)                                                                  \
  MOREROWS(1)                                                                  \
  MOREROWS(2) MOREROWS(4) MOREROWS(8) MOREROWS(16) MOREROWS(24) MOREROWS(30)

static const ClampBitsType ClampBitsValues[] = {
    make_tuple(0, 0, 0),    make_tuple(0, 16, 0),
    make_tuple(32, 0, 0),   make_tuple(32, 16, 32),
    make_tuple(32, 2, 3),   make_tuple(-32, 0, 0),
    make_tuple(-32, 16, 0), GENERATE()};
INSTANTIATE_TEST_CASE_P(ClampBitsTest, ClampBitsTest,
                        ::testing::ValuesIn(ClampBitsValues));
TEST_P(ClampBitsTest, ClampBitsTest) { ASSERT_EQ(clampBits(in, n), expected); }

using TrimSpacesType = std::tr1::tuple<string, string>;
class TrimSpacesTest : public ::testing::TestWithParam<TrimSpacesType> {
protected:
  TrimSpacesTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    out = std::tr1::get<1>(GetParam());
  }

  string in;  // input
  string out; // expected output
};

static const TrimSpacesType TrimSpacesValues[] = {
#define STR "fo2o 3,24 b5a#r"
    make_tuple("foo", "foo"),
    make_tuple(STR, STR),
    make_tuple("  " STR, STR),
    make_tuple("\t" STR, STR),
    make_tuple(" \t " STR, STR),
    make_tuple(STR "  ", STR),
    make_tuple(STR "\t", STR),
    make_tuple(STR "  \t  ", STR),
    make_tuple("  " STR "  ", STR),
    make_tuple("\t" STR "\t", STR),
    make_tuple("  \t  " STR "  \t  ", STR),
    make_tuple("    ", ""),
    make_tuple("  \t", ""),
    make_tuple("  \t  ", ""),
    make_tuple("\t  ", ""),
#undef STR
};
INSTANTIATE_TEST_CASE_P(TrimSpacesTest, TrimSpacesTest,
                        ::testing::ValuesIn(TrimSpacesValues));
TEST_P(TrimSpacesTest, TrimSpacesTest) { ASSERT_EQ(trimSpaces(in), out); }

using splitStringType = std::tr1::tuple<string, char, vector<string>>;
class SplitStringTest : public ::testing::TestWithParam<splitStringType> {
protected:
  SplitStringTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    sep = std::tr1::get<1>(GetParam());
    out = std::tr1::get<2>(GetParam());
  }

  string in;          // input
  char sep;           // the separator
  vector<string> out; // expected output
};
static const splitStringType splitStringValues[] = {
    make_tuple(" ini mi,ni  moe ", ' ',
               vector<string>({"ini", "mi,ni", "moe"})),
    make_tuple(" 412, 542,732 , ", ',',
               vector<string>({" 412", " 542", "732 ", " "})),

};
INSTANTIATE_TEST_CASE_P(SplitStringTest, SplitStringTest,
                        ::testing::ValuesIn(splitStringValues));
TEST_P(SplitStringTest, SplitStringTest) {
  auto split = splitString(in, sep);
  ASSERT_EQ(split.size(), out.size());
  ASSERT_TRUE(std::equal(split.begin(), split.end(), out.begin()));
}

TEST(UnrollLoopTest, Test) {
  ASSERT_NO_THROW({
    int cnt = 0;
    unroll_loop<0>([&](int i) { cnt++; });
    ASSERT_EQ(cnt, 0);
  });

  ASSERT_NO_THROW({
    int cnt = 0;
    unroll_loop<3>([&](int i) { cnt++; });
    ASSERT_EQ(cnt, 3);
  });
}

TEST(GetThreadCountTest, Test) {
  ASSERT_NO_THROW({ ASSERT_GE(getThreadCount(), 1); });
}

TEST(MakeUniqueTest, Test) {
  ASSERT_NO_THROW({
    auto s = make_unique<int>(0);
    ASSERT_EQ(*s, 0);
  });
  ASSERT_NO_THROW({
    auto s = make_unique<int>(314);
    ASSERT_EQ(*s, 314);
  });
}

using copyPixelsType = std::tr1::tuple<int, int, int, int>;
class CopyPixelsTest : public ::testing::TestWithParam<copyPixelsType> {
protected:
  CopyPixelsTest() = default;
  virtual void SetUp() {
    dstPitch = std::tr1::get<0>(GetParam());
    srcPitch = std::tr1::get<1>(GetParam());
    rowSize = min(min(std::tr1::get<2>(GetParam()), srcPitch), dstPitch);
    height = std::tr1::get<3>(GetParam());

    assert(srcPitch * height < numeric_limits<uchar8>::max());
    assert(dstPitch * height < numeric_limits<uchar8>::max());

    src.resize((size_t)srcPitch * height);
    dst.resize((size_t)dstPitch * height);

    fill(src.begin(), src.end(), 0);
    fill(dst.begin(), dst.end(), -1);
  }
  void generate() {
    uchar8 v = 0;

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < rowSize; x++, v++) {
        src[y * srcPitch + x] = v;
      }
    }
  }
  void copy() {
    if (src.empty() || dst.empty())
      return;

    copyPixels(&(dst[0]), dstPitch, &(src[0]), srcPitch, rowSize, height);
  }
  void compare() {
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < rowSize; x++) {
        ASSERT_EQ(dst[y * dstPitch + x], src[y * srcPitch + x]);
      }
    }
  }

  vector<uchar8> src;
  vector<uchar8> dst;
  int dstPitch;
  int srcPitch;
  int rowSize;
  int height;
};
INSTANTIATE_TEST_CASE_P(CopyPixelsTest, CopyPixelsTest,
                        testing::Combine(testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1)));
TEST_P(CopyPixelsTest, CopyPixelsTest) {
  generate();
  copy();
  compare();
}
<commit_msg>ClampBitsTest: add deathtest for assert, also check negatives<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2017 Roman Lebedev

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; withexpected even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "common/Common.h" // for uchar8, clampBits, isIn, isPower...
#include <algorithm>       // for fill, min, equal
#include <cassert>         // for assert
#include <cstddef>         // for size_t
#include <gtest/gtest.h>   // for make_tuple, get, IsNullLiteralHe...
#include <limits>          // for numeric_limits
#include <memory>          // for unique_ptr
#include <string>          // for basic_string, string, allocator
#include <vector>          // for vector

using namespace std;
using namespace RawSpeed;

using powerOfTwoType = std::tr1::tuple<int, bool>;
class PowerOfTwoTest : public ::testing::TestWithParam<powerOfTwoType> {
protected:
  PowerOfTwoTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    expected = std::tr1::get<1>(GetParam());
  }

  int in;        // input
  bool expected; // expected output
};
static const powerOfTwoType powerOfTwoValues[] = {
    make_tuple(0, true),  make_tuple(1, true),   make_tuple(2, true),
    make_tuple(3, false), make_tuple(4, true),   make_tuple(5, false),
    make_tuple(6, false), make_tuple(7, false),  make_tuple(8, true),
    make_tuple(9, false), make_tuple(10, false), make_tuple(11, false),

};
INSTANTIATE_TEST_CASE_P(PowerOfTwoTest, PowerOfTwoTest,
                        ::testing::ValuesIn(powerOfTwoValues));
TEST_P(PowerOfTwoTest, PowerOfTwoTest) {
  ASSERT_EQ(isPowerOfTwo(in), expected);
}

using RoundUpType = std::tr1::tuple<size_t, size_t, size_t>;
class RoundUpTest : public ::testing::TestWithParam<RoundUpType> {
protected:
  RoundUpTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    multiple = std::tr1::get<1>(GetParam());
    expected = std::tr1::get<2>(GetParam());
  }

  size_t in; // input
  size_t multiple;
  size_t expected; // expected output
};
static const RoundUpType RoundUpValues[] = {
    make_tuple(0, 0, 0),    make_tuple(0, 10, 0),   make_tuple(10, 0, 10),
    make_tuple(10, 10, 10), make_tuple(10, 1, 10),  make_tuple(10, 2, 10),
    make_tuple(10, 3, 12),  make_tuple(10, 4, 12),  make_tuple(10, 5, 10),
    make_tuple(10, 6, 12),  make_tuple(10, 7, 14),  make_tuple(10, 8, 16),
    make_tuple(10, 9, 18),  make_tuple(10, 11, 11), make_tuple(10, 12, 12),

};
INSTANTIATE_TEST_CASE_P(RoundUpTest, RoundUpTest,
                        ::testing::ValuesIn(RoundUpValues));
TEST_P(RoundUpTest, RoundUpTest) { ASSERT_EQ(roundUp(in, multiple), expected); }

using IsInType = std::tr1::tuple<string, bool>;
class IsInTest : public ::testing::TestWithParam<IsInType> {
protected:
  IsInTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    expected = std::tr1::get<1>(GetParam());
  }

  string in;     // input
  bool expected; // expected output
};

static const IsInType IsInValues[] = {
    make_tuple("foo", true),    make_tuple("foo2", true),
    make_tuple("bar", true),    make_tuple("baz", true),
    make_tuple("foo1", false),  make_tuple("bar2", false),
    make_tuple("baz-1", false), make_tuple("quz", false),

};
INSTANTIATE_TEST_CASE_P(IsInTest, IsInTest, ::testing::ValuesIn(IsInValues));
TEST_P(IsInTest, IsInTest) {
  ASSERT_EQ(isIn(in, {"foo", "foo2", "bar", "baz"}), expected);
}

using ClampBitsType =
    std::tr1::tuple<int, int, ushort16>;
class ClampBitsTest : public ::testing::TestWithParam<ClampBitsType> {
protected:
  ClampBitsTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    n = std::tr1::get<1>(GetParam());
    expected = std::tr1::get<2>(GetParam());
  }

  int in; // input
  int n;
  ushort16 expected; // expected output
};

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

#define ROW(v, p, pv) make_tuple((v), (p), MIN(pv, MAX(v, 0))),

#define ROWS(v, p, pv) ROW(-v, p, 0) ROW(v, p, pv)

#define THREEROWS(v, p)                                                        \
  ROWS(((1 << (v)) - 1), (p), ((1 << (p)) - 1))                                \
  ROWS(((1 << (v)) - 0), (p), ((1 << (p)) - 1))                                \
  ROWS(((1 << (v)) + 1), (p), ((1 << (p)) - 1))

#define MOREROWS(v)                                                            \
  THREEROWS(v, 0)                                                              \
  THREEROWS(v, 1)                                                              \
  THREEROWS(v, 2)                                                              \
  THREEROWS(v, 4)                                                              \
  THREEROWS(v, 8) THREEROWS(v, 16)

#define GENERATE()                                                             \
  MOREROWS(0)                                                                  \
  MOREROWS(1)                                                                  \
  MOREROWS(2) MOREROWS(4) MOREROWS(8) MOREROWS(16) MOREROWS(24) MOREROWS(30)

static const ClampBitsType ClampBitsValues[] = {
    make_tuple(0, 0, 0),    make_tuple(0, 16, 0),
    make_tuple(32, 0, 0),   make_tuple(32, 16, 32),
    make_tuple(32, 2, 3),   make_tuple(-32, 0, 0),
    make_tuple(-32, 16, 0), GENERATE()};
INSTANTIATE_TEST_CASE_P(ClampBitsTest, ClampBitsTest,
                        ::testing::ValuesIn(ClampBitsValues));
TEST_P(ClampBitsTest, ClampBitsTest) { ASSERT_EQ(clampBits(in, n), expected); }
TEST(ClampBitsDeathTest, Only16Bit) {
#ifndef NDEBUG
  ASSERT_DEATH({ ASSERT_EQ(clampBits(0, 17), 0); }, "n <= 16");
#endif
}

using TrimSpacesType = std::tr1::tuple<string, string>;
class TrimSpacesTest : public ::testing::TestWithParam<TrimSpacesType> {
protected:
  TrimSpacesTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    out = std::tr1::get<1>(GetParam());
  }

  string in;  // input
  string out; // expected output
};

static const TrimSpacesType TrimSpacesValues[] = {
#define STR "fo2o 3,24 b5a#r"
    make_tuple("foo", "foo"),
    make_tuple(STR, STR),
    make_tuple("  " STR, STR),
    make_tuple("\t" STR, STR),
    make_tuple(" \t " STR, STR),
    make_tuple(STR "  ", STR),
    make_tuple(STR "\t", STR),
    make_tuple(STR "  \t  ", STR),
    make_tuple("  " STR "  ", STR),
    make_tuple("\t" STR "\t", STR),
    make_tuple("  \t  " STR "  \t  ", STR),
    make_tuple("    ", ""),
    make_tuple("  \t", ""),
    make_tuple("  \t  ", ""),
    make_tuple("\t  ", ""),
#undef STR
};
INSTANTIATE_TEST_CASE_P(TrimSpacesTest, TrimSpacesTest,
                        ::testing::ValuesIn(TrimSpacesValues));
TEST_P(TrimSpacesTest, TrimSpacesTest) { ASSERT_EQ(trimSpaces(in), out); }

using splitStringType = std::tr1::tuple<string, char, vector<string>>;
class SplitStringTest : public ::testing::TestWithParam<splitStringType> {
protected:
  SplitStringTest() = default;
  virtual void SetUp() {
    in = std::tr1::get<0>(GetParam());
    sep = std::tr1::get<1>(GetParam());
    out = std::tr1::get<2>(GetParam());
  }

  string in;          // input
  char sep;           // the separator
  vector<string> out; // expected output
};
static const splitStringType splitStringValues[] = {
    make_tuple(" ini mi,ni  moe ", ' ',
               vector<string>({"ini", "mi,ni", "moe"})),
    make_tuple(" 412, 542,732 , ", ',',
               vector<string>({" 412", " 542", "732 ", " "})),

};
INSTANTIATE_TEST_CASE_P(SplitStringTest, SplitStringTest,
                        ::testing::ValuesIn(splitStringValues));
TEST_P(SplitStringTest, SplitStringTest) {
  auto split = splitString(in, sep);
  ASSERT_EQ(split.size(), out.size());
  ASSERT_TRUE(std::equal(split.begin(), split.end(), out.begin()));
}

TEST(UnrollLoopTest, Test) {
  ASSERT_NO_THROW({
    int cnt = 0;
    unroll_loop<0>([&](int i) { cnt++; });
    ASSERT_EQ(cnt, 0);
  });

  ASSERT_NO_THROW({
    int cnt = 0;
    unroll_loop<3>([&](int i) { cnt++; });
    ASSERT_EQ(cnt, 3);
  });
}

TEST(GetThreadCountTest, Test) {
  ASSERT_NO_THROW({ ASSERT_GE(getThreadCount(), 1); });
}

TEST(MakeUniqueTest, Test) {
  ASSERT_NO_THROW({
    auto s = make_unique<int>(0);
    ASSERT_EQ(*s, 0);
  });
  ASSERT_NO_THROW({
    auto s = make_unique<int>(314);
    ASSERT_EQ(*s, 314);
  });
}

using copyPixelsType = std::tr1::tuple<int, int, int, int>;
class CopyPixelsTest : public ::testing::TestWithParam<copyPixelsType> {
protected:
  CopyPixelsTest() = default;
  virtual void SetUp() {
    dstPitch = std::tr1::get<0>(GetParam());
    srcPitch = std::tr1::get<1>(GetParam());
    rowSize = min(min(std::tr1::get<2>(GetParam()), srcPitch), dstPitch);
    height = std::tr1::get<3>(GetParam());

    assert(srcPitch * height < numeric_limits<uchar8>::max());
    assert(dstPitch * height < numeric_limits<uchar8>::max());

    src.resize((size_t)srcPitch * height);
    dst.resize((size_t)dstPitch * height);

    fill(src.begin(), src.end(), 0);
    fill(dst.begin(), dst.end(), -1);
  }
  void generate() {
    uchar8 v = 0;

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < rowSize; x++, v++) {
        src[y * srcPitch + x] = v;
      }
    }
  }
  void copy() {
    if (src.empty() || dst.empty())
      return;

    copyPixels(&(dst[0]), dstPitch, &(src[0]), srcPitch, rowSize, height);
  }
  void compare() {
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < rowSize; x++) {
        ASSERT_EQ(dst[y * dstPitch + x], src[y * srcPitch + x]);
      }
    }
  }

  vector<uchar8> src;
  vector<uchar8> dst;
  int dstPitch;
  int srcPitch;
  int rowSize;
  int height;
};
INSTANTIATE_TEST_CASE_P(CopyPixelsTest, CopyPixelsTest,
                        testing::Combine(testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1),
                                         testing::Range(0, 4, 1)));
TEST_P(CopyPixelsTest, CopyPixelsTest) {
  generate();
  copy();
  compare();
}
<|endoftext|>