<commit_before>#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
#include <boost/asio/error.hpp>

namespace i2p
{
namespace client
{
	static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;

	I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
		m_LocalDestination (localDestination ? localDestination :
			i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)),
			m_ReadyTimer(m_LocalDestination->GetService()),
			m_ConnectTimeout(0),
			isUpdated (true)
	{
		m_LocalDestination->Acquire ();
	}

	I2PService::I2PService (i2p::data::SigningKeyType kt):
		m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)),
		m_ReadyTimer(m_LocalDestination->GetService()),
		m_ConnectTimeout(0),
		isUpdated (true)
	{
		m_LocalDestination->Acquire ();
	}

	I2PService::~I2PService ()
	{
		ClearHandlers ();
		if (m_LocalDestination) m_LocalDestination->Release ();
	}

	void I2PService::ClearHandlers ()
	{
		if(m_ConnectTimeout)
			m_ReadyTimer.cancel();
		std::unique_lock<std::mutex> l(m_HandlersMutex);
		for (auto it: m_Handlers)
			it->Terminate ();
		m_Handlers.clear();
	}

	void I2PService::SetConnectTimeout(uint32_t timeout)
	{
		if(timeout && !m_ConnectTimeout)
		{
			TriggerReadyCheckTimer();
		}
		else if (m_ConnectTimeout && !timeout)
		{
			m_ReadyTimer.cancel();
		}
		m_ConnectTimeout = timeout;
	}

	void I2PService::AddReadyCallback(ReadyCallback cb)
	{
		uint32_t now = i2p::util::GetSecondsSinceEpoch();
		uint32_t tm = now + m_ConnectTimeout;
		LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now);
		m_ReadyCallbacks.push_back({cb, tm});
	}

	void I2PService::TriggerReadyCheckTimer()
	{
		m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1));
		m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, this, std::placeholders::_1));
	}

	void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec)
	{
		if(ec || m_LocalDestination->IsReady())
		{
			for(auto & itr : m_ReadyCallbacks)
				itr.first(ec);
			m_ReadyCallbacks.clear();
		}
		else if(!m_LocalDestination->IsReady())
		{
			// expire timed out requests
			uint32_t now = i2p::util::GetSecondsSinceEpoch ();
			auto itr = m_ReadyCallbacks.begin();
			while(itr != m_ReadyCallbacks.end())
			{
				if(itr->second >= now)
				{
					itr->first(boost::asio::error::timed_out);
					itr = m_ReadyCallbacks.erase(itr);
				}
				else
					++itr;
			}
		}
		if(!ec)
			TriggerReadyCheckTimer();
	}

	void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
		assert(streamRequestComplete);
		i2p::data::IdentHash identHash;
		if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
			CreateStream(streamRequestComplete, identHash, port);
		else
		{
			LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
			streamRequestComplete (nullptr);
		}
	}

	void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash & identHash, int port)
	{
		if(m_ConnectTimeout)
		{
			if(m_LocalDestination->IsReady())
				m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
			else
			{
				AddReadyCallback([this, streamRequestComplete, identHash, port] (const boost::system::error_code & ec) {
						if(ec)
						{
							LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message());
							streamRequestComplete(nullptr);
						}
						else
							this->m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
					});
			}
		}
		else
			m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
	}

	TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
	{
		boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
		upstream->set_option(option);
		downstream->set_option(option);
	}

	TCPIPPipe::~TCPIPPipe()
	{
		Terminate();
	}

	void TCPIPPipe::Start()
	{
		AsyncReceiveUpstream();
		AsyncReceiveDownstream();
	}

	void TCPIPPipe::Terminate()
	{
		if(Kill()) return;
		if (m_up)
		{
			if (m_up->is_open())
				m_up->close();
			m_up = nullptr;
		}
		if (m_down)
		{
			if (m_down->is_open())
				m_down->close();
			m_down = nullptr;
		}
		Done(shared_from_this());
	}

	void TCPIPPipe::AsyncReceiveUpstream()
	{
		if (m_up)
		{
			m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
				std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
				std::placeholders::_1, std::placeholders::_2));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
	}

	void TCPIPPipe::AsyncReceiveDownstream()
	{
		if (m_down) {
			m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
				std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
				std::placeholders::_1, std::placeholders::_2));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
	}

	void TCPIPPipe::UpstreamWrite(size_t len)
	{
		if (m_up)
		{
			LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
			boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
				boost::asio::transfer_all(),
				std::bind(&TCPIPPipe::HandleUpstreamWrite,
				shared_from_this(),
				std::placeholders::_1));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
	}

	void TCPIPPipe::DownstreamWrite(size_t len)
	{
		if (m_down)
		{
			LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
			boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
				boost::asio::transfer_all(),
				std::bind(&TCPIPPipe::HandleDownstreamWrite,
				shared_from_this(),
				std::placeholders::_1));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
	}

	void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
	{
		LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		} else {
			if (bytes_transfered > 0 )
				memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
			UpstreamWrite(bytes_transfered);
		}
	}

	void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		}
		else
			AsyncReceiveUpstream();
	}

	void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		}
		else
			AsyncReceiveDownstream();
	}

	void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
	{
		LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		} else {
			if (bytes_transfered > 0 )
				memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
			DownstreamWrite(bytes_transfered);
		}
	}

	void TCPIPAcceptor::Start ()
	{
		m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint));
		m_Acceptor->listen ();
		Accept ();
	}

	void TCPIPAcceptor::Stop ()
	{
		if (m_Acceptor)
		{
			m_Acceptor->close();
			m_Acceptor.reset (nullptr);
		}
		m_Timer.cancel ();
		ClearHandlers();
	}

	void TCPIPAcceptor::Accept ()
	{
		auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
		m_Acceptor->async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
			std::placeholders::_1, newSocket));
	}

	void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
	{
		if (!ecode)
		{
			LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
			auto handler = CreateHandler(socket);
			if (handler)
			{
				AddHandler(handler);
				handler->Handle();
			}
			else
				socket->close();
			Accept();
		}
		else
		{
			if (ecode != boost::asio::error::operation_aborted)
				LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
		}
	}
}
}
<commit_msg>the change allows when an TCPIPAcceptor is constructed by setting port = 0, the random port chosen by asio can be retrieved using TCPIPAcceptor::GetLocalEndpoint().port()<commit_after>#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
#include <boost/asio/error.hpp>

namespace i2p
{
namespace client
{
	static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;

	I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
		m_LocalDestination (localDestination ? localDestination :
			i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)),
			m_ReadyTimer(m_LocalDestination->GetService()),
			m_ConnectTimeout(0),
			isUpdated (true)
	{
		m_LocalDestination->Acquire ();
	}

	I2PService::I2PService (i2p::data::SigningKeyType kt):
		m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)),
		m_ReadyTimer(m_LocalDestination->GetService()),
		m_ConnectTimeout(0),
		isUpdated (true)
	{
		m_LocalDestination->Acquire ();
	}

	I2PService::~I2PService ()
	{
		ClearHandlers ();
		if (m_LocalDestination) m_LocalDestination->Release ();
	}

	void I2PService::ClearHandlers ()
	{
		if(m_ConnectTimeout)
			m_ReadyTimer.cancel();
		std::unique_lock<std::mutex> l(m_HandlersMutex);
		for (auto it: m_Handlers)
			it->Terminate ();
		m_Handlers.clear();
	}

	void I2PService::SetConnectTimeout(uint32_t timeout)
	{
		if(timeout && !m_ConnectTimeout)
		{
			TriggerReadyCheckTimer();
		}
		else if (m_ConnectTimeout && !timeout)
		{
			m_ReadyTimer.cancel();
		}
		m_ConnectTimeout = timeout;
	}

	void I2PService::AddReadyCallback(ReadyCallback cb)
	{
		uint32_t now = i2p::util::GetSecondsSinceEpoch();
		uint32_t tm = now + m_ConnectTimeout;
		LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now);
		m_ReadyCallbacks.push_back({cb, tm});
	}

	void I2PService::TriggerReadyCheckTimer()
	{
		m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1));
		m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, this, std::placeholders::_1));
	}

	void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec)
	{
		if(ec || m_LocalDestination->IsReady())
		{
			for(auto & itr : m_ReadyCallbacks)
				itr.first(ec);
			m_ReadyCallbacks.clear();
		}
		else if(!m_LocalDestination->IsReady())
		{
			// expire timed out requests
			uint32_t now = i2p::util::GetSecondsSinceEpoch ();
			auto itr = m_ReadyCallbacks.begin();
			while(itr != m_ReadyCallbacks.end())
			{
				if(itr->second >= now)
				{
					itr->first(boost::asio::error::timed_out);
					itr = m_ReadyCallbacks.erase(itr);
				}
				else
					++itr;
			}
		}
		if(!ec)
			TriggerReadyCheckTimer();
	}

	void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
		assert(streamRequestComplete);
		i2p::data::IdentHash identHash;
		if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
			CreateStream(streamRequestComplete, identHash, port);
		else
		{
			LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
			streamRequestComplete (nullptr);
		}
	}

	void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash & identHash, int port)
	{
		if(m_ConnectTimeout)
		{
			if(m_LocalDestination->IsReady())
				m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
			else
			{
				AddReadyCallback([this, streamRequestComplete, identHash, port] (const boost::system::error_code & ec) {
						if(ec)
						{
							LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message());
							streamRequestComplete(nullptr);
						}
						else
							this->m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
					});
			}
		}
		else
			m_LocalDestination->CreateStream(streamRequestComplete, identHash, port);
	}

	TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
	{
		boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
		upstream->set_option(option);
		downstream->set_option(option);
	}

	TCPIPPipe::~TCPIPPipe()
	{
		Terminate();
	}

	void TCPIPPipe::Start()
	{
		AsyncReceiveUpstream();
		AsyncReceiveDownstream();
	}

	void TCPIPPipe::Terminate()
	{
		if(Kill()) return;
		if (m_up)
		{
			if (m_up->is_open())
				m_up->close();
			m_up = nullptr;
		}
		if (m_down)
		{
			if (m_down->is_open())
				m_down->close();
			m_down = nullptr;
		}
		Done(shared_from_this());
	}

	void TCPIPPipe::AsyncReceiveUpstream()
	{
		if (m_up)
		{
			m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
				std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
				std::placeholders::_1, std::placeholders::_2));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
	}

	void TCPIPPipe::AsyncReceiveDownstream()
	{
		if (m_down) {
			m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
				std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
				std::placeholders::_1, std::placeholders::_2));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
	}

	void TCPIPPipe::UpstreamWrite(size_t len)
	{
		if (m_up)
		{
			LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
			boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
				boost::asio::transfer_all(),
				std::bind(&TCPIPPipe::HandleUpstreamWrite,
				shared_from_this(),
				std::placeholders::_1));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
	}

	void TCPIPPipe::DownstreamWrite(size_t len)
	{
		if (m_down)
		{
			LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
			boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
				boost::asio::transfer_all(),
				std::bind(&TCPIPPipe::HandleDownstreamWrite,
				shared_from_this(),
				std::placeholders::_1));
		}
		else
			LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
	}

	void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
	{
		LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		} else {
			if (bytes_transfered > 0 )
				memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
			UpstreamWrite(bytes_transfered);
		}
	}

	void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		}
		else
			AsyncReceiveUpstream();
	}

	void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		}
		else
			AsyncReceiveDownstream();
	}

	void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
	{
		LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
		if (ecode)
		{
			LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate();
		} else {
			if (bytes_transfered > 0 )
				memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
			DownstreamWrite(bytes_transfered);
		}
	}

	void TCPIPAcceptor::Start ()
	{
		m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint));
		//update the local end point in case port has been set zero and got updated now
		m_LocalEndpoint = m_Acceptor->local_endpoint();
		m_Acceptor->listen ();
		Accept ();
	}

	void TCPIPAcceptor::Stop ()
	{
		if (m_Acceptor)
		{
			m_Acceptor->close();
			m_Acceptor.reset (nullptr);
		}
		m_Timer.cancel ();
		ClearHandlers();
	}

	void TCPIPAcceptor::Accept ()
	{
		auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
		m_Acceptor->async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
			std::placeholders::_1, newSocket));
	}

	void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
	{
		if (!ecode)
		{
			LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
			auto handler = CreateHandler(socket);
			if (handler)
			{
				AddHandler(handler);
				handler->Handle();
			}
			else
				socket->close();
			Accept();
		}
		else
		{
			if (ecode != boost::asio::error::operation_aborted)
				LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
		}
	}
}
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/log/Logger.h>

#include <stingraykit/collection/iterators.h>
#include <stingraykit/diagnostics/Backtrace.h>
#include <stingraykit/log/SystemLogger.h>
#include <stingraykit/string/StringFormat.h>
#include <stingraykit/time/TimeEngine.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/PhoenixSingleton.h>
#include <stingraykit/SafeSingleton.h>

#include <cstdio>
#include <cstring>

#include <map>
#include <sstream>

namespace stingray
{

	namespace Detail {
	namespace LoggerDetail
	{
		NullPtrType		s_logger; // Static logger fallback
	}}

	struct NamedLoggerSettings
	{
	private:
		optional<LogLevel>	_logLevel;
		bool				_backtrace;
		bool				_highlight;

	public:
		NamedLoggerSettings() :
			_backtrace(false), _highlight(false)
		{ }

		optional<LogLevel> GetLogLevel() const			{ return _logLevel; }
		void SetLogLevel(optional<LogLevel> logLevel)	{ _logLevel = logLevel; }

		bool BacktraceEnabled() const					{ return _backtrace; }
		void EnableBacktrace(bool enable)				{ _backtrace = enable; }

		bool HighlightEnabled() const					{ return _highlight; }
		void EnableHighlight(bool enable)				{ _highlight = enable; }

		bool IsEmpty() const							{ return GetLogLevel() || BacktraceEnabled() || HighlightEnabled(); }
	};

	class NamedLoggerRegistry
	{
		struct StrLess
		{
			bool operator() (const char* l, const char* r) const { return strcmp(l, r) < 0; }
		};

		typedef std::map<std::string, NamedLoggerSettings>			SettingsRegistry;
		typedef std::multimap<const char*, NamedLogger*, StrLess>	ObjectsRegistry;

	private:
		Mutex				_mutex;
		SettingsRegistry	_settings;
		ObjectsRegistry		_objects;

	public:
		NamedLoggerRegistry()
		{ }

		ObjectsRegistry::iterator Register(const char* loggerName, NamedLogger* logger)
		{
			MutexLock l(_mutex);
			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it != _settings.end())
			{
				logger->SetLogLevel(it->second.GetLogLevel());
				logger->EnableBacktrace(it->second.BacktraceEnabled());
				logger->EnableHighlight(it->second.HighlightEnabled());
			}
			return _objects.insert(std::make_pair(loggerName, logger));
		}

		void Unregister(ObjectsRegistry::iterator it)
		{
			MutexLock l(_mutex);
			_objects.erase(it);
		}

		void GetLoggerNames(std::set<std::string>& out) const
		{
			MutexLock l(_mutex);
			out.clear();
			std::copy(keys_iterator(_objects.begin()), keys_iterator(_objects.end()), std::inserter(out, out.begin()));
		}

		void SetLogLevel(const std::string& loggerName, optional<LogLevel> logLevel)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->SetLogLevel(logLevel);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.SetLogLevel(logLevel);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}

		void EnableBacktrace(const std::string& loggerName, bool enable)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->EnableBacktrace(enable);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.EnableBacktrace(enable);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}

		void EnableHighlight(const std::string& loggerName, bool enable)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->EnableHighlight(enable);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.EnableHighlight(enable);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}
	};
	STINGRAYKIT_DECLARE_PTR(NamedLoggerRegistry);

	class LoggerImpl
	{
		typedef std::vector<ILoggerSinkPtr>							SinksBundle;

	private:
		SinksBundle				_sinks;
		Mutex					_logMutex;
		NamedLoggerRegistry		_registry;

	public:
		LoggerImpl()
		{ }

		~LoggerImpl()
		{ }

		void AddSink(const ILoggerSinkPtr& sink)
		{
			MutexLock l(_logMutex);
			_sinks.push_back(sink);
		}

		void RemoveSink(const ILoggerSinkPtr& sink)
		{
			MutexLock l(_logMutex);
			SinksBundle::iterator it = std::find(_sinks.begin(), _sinks.end(), sink);
			if (it != _sinks.end())
				_sinks.erase(it);
		}

		void Log(const LoggerMessage& message) throw()
		{
			try
			{
				EnableInterruptionPoints eip(false);

				SinksBundle sinks;
				{
					MutexLock l(_logMutex);
					sinks = _sinks;
				}

				if (sinks.empty())
					SystemLogger::Log(message);
				else
					PutMessageToSinks(sinks, message);
			}
			catch (const std::exception&)
			{ }
		}

		NamedLoggerRegistry& GetRegistry()
		{ return _registry; }

	private:
		static void PutMessageToSinks(const SinksBundle& sinks, const LoggerMessage& message)
		{
			for (SinksBundle::const_iterator it = sinks.begin(); it != sinks.end(); ++it)
			{
				try
				{
					ILoggerSinkPtr sink = *it;
					sink->Log(message);
				}
				catch (const std::exception&)
				{ }
			}
		}
	};
	STINGRAYKIT_DECLARE_PTR(LoggerImpl);

	typedef SafeSingleton<LoggerImpl> LoggerSingleton;

	/////////////////////////////////////////////////////////////////

	NamedLogger::NamedLogger(const char* name) :
		_params(name),
		_logLevel(OptionalLogLevel::Null)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
		{
			NamedLoggerRegistryPtr r(logger, &(logger->GetRegistry()));
			_token = MakeToken<FunctionToken>(bind(&NamedLoggerRegistry::Unregister, r, r->Register(_params.GetName(), this)));
		}
	}

	NamedLogger::~NamedLogger()
	{ _token.Reset(); }

	/////////////////////////////////////////////////////////////////

	struct LogLevelHolder
	{
		static u32 s_logLevel;
	};

	u32 LogLevelHolder::s_logLevel = (u32)LogLevel::Info;

	void Logger::SetLogLevel(LogLevel logLevel)
	{
		AtomicU32::Store(LogLevelHolder::s_logLevel, (u32)logLevel.val(), MemoryOrderRelaxed);
		Stream(logLevel) << "Log level is " << logLevel;
	}

	LogLevel Logger::GetLogLevel()
	{ return (LogLevel::Enum)AtomicU32::Load(LogLevelHolder::s_logLevel, MemoryOrderRelaxed); }

	LoggerStream Logger::Stream(LogLevel logLevel, DuplicatingLogsFilter* duplicatingLogsFilter)
	{ return LoggerStream(null, GetLogLevel(), logLevel, duplicatingLogsFilter, &Logger::DoLog); }

	void Logger::SetLogLevel(const std::string& loggerName, optional<LogLevel> logLevel)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().SetLogLevel(loggerName, logLevel);
	}

	void Logger::EnableBacktrace(const std::string& loggerName, bool enable)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().EnableBacktrace(loggerName, enable);
	}

	void Logger::EnableHighlight(const std::string& loggerName, bool enable)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().EnableHighlight(loggerName, enable);
	}

	void Logger::GetLoggerNames(std::set<std::string>& out)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().GetLoggerNames(out);
	}

	Token Logger::AddSink(const ILoggerSinkPtr& sink)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (!logger)
			return null;

		logger->AddSink(sink);
		return MakeToken<FunctionToken>(bind(&LoggerImpl::RemoveSink, logger, sink));
	}

	void Logger::DoLog(const NamedLoggerParams* loggerParams, LogLevel logLevel, const std::string& text)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		LogLevel ll = logger ? GetLogLevel() : LogLevel(LogLevel::Debug);
		if (!loggerParams && logLevel < ll) // NamedLogger LoggerStream checks the log level in its destructor
			return;

		optional<LoggerMessage> msg;
		if (loggerParams)
			msg = LoggerMessage(loggerParams->GetName(), logLevel, text + (loggerParams->BacktraceEnabled() ? ": " + Backtrace().Get() : std::string()), loggerParams->HighlightEnabled());
		else
			msg = LoggerMessage(logLevel, text, false);

		if (logger)
			logger->Log(*msg);
		else
			SystemLogger::Log(*msg);
	}

	LogLevel NamedLogger::GetLogLevel() const
	{
		optional<LogLevel> logLevel = OptionalLogLevel::ToLogLevel(_logLevel.load(MemoryOrderRelaxed));
		return logLevel ? *logLevel : Logger::GetLogLevel();
	}

	void NamedLogger::SetLogLevel(optional<LogLevel> logLevel)
	{
		_logLevel.store(OptionalLogLevel::FromLogLevel(logLevel), MemoryOrderRelaxed);
		Stream(GetLogLevel()) << "Log level is " << logLevel;
	}

	bool NamedLogger::BacktraceEnabled() const
	{ return _params.BacktraceEnabled(); }

	void NamedLogger::EnableBacktrace(bool enable)
	{
		_params.EnableBacktrace(enable);
		Stream(GetLogLevel()) << "Backtrace " << (enable ? "enabled" : "disabled");
	}

	bool NamedLogger::HighlightEnabled() const
	{ return _params.HighlightEnabled(); }

	void NamedLogger::EnableHighlight(bool enable)
	{ _params.EnableHighlight(enable); }

	LoggerStream NamedLogger::Stream(LogLevel logLevel) const
	{ return LoggerStream(&_params, GetLogLevel(), logLevel, &_duplicatingLogsFilter, &Logger::DoLog); }

	void NamedLogger::Log(LogLevel logLevel, const std::string& message)
	{ Stream(logLevel) << message; }

	/////////////////////////////////////////////////////////////////

	ActionLogger::ActionLogger(const std::string& action)
		: _namedLogger(NULL), _action(action)
	{ Logger::Info() << _action << "..."; }

	ActionLogger::ActionLogger(NamedLogger& namedLogger, const std::string& action)
		: _namedLogger(&namedLogger), _action(action)
	{ _namedLogger->Info() << _action << "..."; }

	ActionLogger::~ActionLogger()
	{
		s64 ms = 0;
		int mms = 0;
		try { s64 e = _elapsedTime.ElapsedMicroseconds(); ms = e / 1000; mms = e % 1000; } catch(const std::exception&) { }
		if (std::uncaught_exception())
		{
			try
			{
				if (_namedLogger)
					_namedLogger->Info() << _action << " completed with exception in " << ms << "." << mms << " ms";
				else
					Logger::Info() << _action << " completed with exception in " << ms << "." << mms << " ms";
			}
			catch (const std::exception&)
			{ return; }
		}
		else
		{
			if (_namedLogger)
				_namedLogger->Info() << _action << " completed in " << ms << "." << mms << " ms";
			else
				Logger::Info() << _action << " completed in " << ms << "." << mms << " ms";
		}
	}

	/////////////////////////////////////////////////////////////////

	Tracer::Tracer(const Detail::NamedLoggerAccessor& logger, const char* funcName) :
		_logger(logger), _logLevel(logger.GetLogLevel()), _funcName(funcName)
	{
		if (_logLevel > LogLevel::Trace)
			return;

		_startTime = TimeEngine::GetMonotonicMicroseconds();
		_logger.Trace() << "TRACER: entering function '" << _funcName << "'";
	}

	Tracer::~Tracer()
	{
		if (_logLevel > LogLevel::Trace)
			return;

		try
		{
			s64 e = TimeEngine::GetMonotonicMicroseconds() - _startTime;
			s64 ms = e / 1000;
			int mms = e % 1000;

			if (std::uncaught_exception())
				_logger.Trace() << "TRACER: leaving function '" << _funcName << "' due to an exception (" << StringFormat("%1%.%2$3%", ms, mms) << " ms)";
			else
				_logger.Trace() << "TRACER: leaving function '" << _funcName << "' (" << StringFormat("%1%.%2$3%", ms, mms) << " ms)";
		}
		catch(const std::exception&)
		{ }
	}

	/////////////////////////////////////////////////////////////////

	void LogException(const std::exception& ex)
	{ Logger::Error() << ex; }

	void LogExceptionTo(NamedLogger& namedLogger, const std::exception& ex)
	{ namedLogger.Error() << ex; }

}
<commit_msg>ActionLogger: add helper for formatting elapsed time<commit_after>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/log/Logger.h>

#include <stingraykit/collection/iterators.h>
#include <stingraykit/diagnostics/Backtrace.h>
#include <stingraykit/log/SystemLogger.h>
#include <stingraykit/string/StringFormat.h>
#include <stingraykit/time/TimeEngine.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/PhoenixSingleton.h>
#include <stingraykit/SafeSingleton.h>

#include <cstdio>
#include <cstring>

#include <map>
#include <sstream>

namespace stingray
{

	namespace Detail {
	namespace LoggerDetail
	{
		NullPtrType		s_logger; // Static logger fallback
	}}

	struct NamedLoggerSettings
	{
	private:
		optional<LogLevel>	_logLevel;
		bool				_backtrace;
		bool				_highlight;

	public:
		NamedLoggerSettings() :
			_backtrace(false), _highlight(false)
		{ }

		optional<LogLevel> GetLogLevel() const			{ return _logLevel; }
		void SetLogLevel(optional<LogLevel> logLevel)	{ _logLevel = logLevel; }

		bool BacktraceEnabled() const					{ return _backtrace; }
		void EnableBacktrace(bool enable)				{ _backtrace = enable; }

		bool HighlightEnabled() const					{ return _highlight; }
		void EnableHighlight(bool enable)				{ _highlight = enable; }

		bool IsEmpty() const							{ return GetLogLevel() || BacktraceEnabled() || HighlightEnabled(); }
	};

	class NamedLoggerRegistry
	{
		struct StrLess
		{
			bool operator() (const char* l, const char* r) const { return strcmp(l, r) < 0; }
		};

		typedef std::map<std::string, NamedLoggerSettings>			SettingsRegistry;
		typedef std::multimap<const char*, NamedLogger*, StrLess>	ObjectsRegistry;

	private:
		Mutex				_mutex;
		SettingsRegistry	_settings;
		ObjectsRegistry		_objects;

	public:
		NamedLoggerRegistry()
		{ }

		ObjectsRegistry::iterator Register(const char* loggerName, NamedLogger* logger)
		{
			MutexLock l(_mutex);
			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it != _settings.end())
			{
				logger->SetLogLevel(it->second.GetLogLevel());
				logger->EnableBacktrace(it->second.BacktraceEnabled());
				logger->EnableHighlight(it->second.HighlightEnabled());
			}
			return _objects.insert(std::make_pair(loggerName, logger));
		}

		void Unregister(ObjectsRegistry::iterator it)
		{
			MutexLock l(_mutex);
			_objects.erase(it);
		}

		void GetLoggerNames(std::set<std::string>& out) const
		{
			MutexLock l(_mutex);
			out.clear();
			std::copy(keys_iterator(_objects.begin()), keys_iterator(_objects.end()), std::inserter(out, out.begin()));
		}

		void SetLogLevel(const std::string& loggerName, optional<LogLevel> logLevel)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->SetLogLevel(logLevel);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.SetLogLevel(logLevel);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}

		void EnableBacktrace(const std::string& loggerName, bool enable)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->EnableBacktrace(enable);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.EnableBacktrace(enable);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}

		void EnableHighlight(const std::string& loggerName, bool enable)
		{
			MutexLock l(_mutex);
			std::pair<ObjectsRegistry::iterator, ObjectsRegistry::iterator> range = _objects.equal_range(loggerName.c_str());
			for (ObjectsRegistry::iterator it = range.first; it != range.second; ++it)
				it->second->EnableHighlight(enable);

			SettingsRegistry::iterator it = _settings.find(loggerName);
			if (it == _settings.end())
				it = _settings.insert(std::make_pair(loggerName, NamedLoggerSettings())).first;
			it->second.EnableHighlight(enable);
			if (it->second.IsEmpty())
				_settings.erase(it);
		}
	};
	STINGRAYKIT_DECLARE_PTR(NamedLoggerRegistry);

	class LoggerImpl
	{
		typedef std::vector<ILoggerSinkPtr>							SinksBundle;

	private:
		SinksBundle				_sinks;
		Mutex					_logMutex;
		NamedLoggerRegistry		_registry;

	public:
		LoggerImpl()
		{ }

		~LoggerImpl()
		{ }

		void AddSink(const ILoggerSinkPtr& sink)
		{
			MutexLock l(_logMutex);
			_sinks.push_back(sink);
		}

		void RemoveSink(const ILoggerSinkPtr& sink)
		{
			MutexLock l(_logMutex);
			SinksBundle::iterator it = std::find(_sinks.begin(), _sinks.end(), sink);
			if (it != _sinks.end())
				_sinks.erase(it);
		}

		void Log(const LoggerMessage& message) throw()
		{
			try
			{
				EnableInterruptionPoints eip(false);

				SinksBundle sinks;
				{
					MutexLock l(_logMutex);
					sinks = _sinks;
				}

				if (sinks.empty())
					SystemLogger::Log(message);
				else
					PutMessageToSinks(sinks, message);
			}
			catch (const std::exception&)
			{ }
		}

		NamedLoggerRegistry& GetRegistry()
		{ return _registry; }

	private:
		static void PutMessageToSinks(const SinksBundle& sinks, const LoggerMessage& message)
		{
			for (SinksBundle::const_iterator it = sinks.begin(); it != sinks.end(); ++it)
			{
				try
				{
					ILoggerSinkPtr sink = *it;
					sink->Log(message);
				}
				catch (const std::exception&)
				{ }
			}
		}
	};
	STINGRAYKIT_DECLARE_PTR(LoggerImpl);

	typedef SafeSingleton<LoggerImpl> LoggerSingleton;

	/////////////////////////////////////////////////////////////////

	NamedLogger::NamedLogger(const char* name) :
		_params(name),
		_logLevel(OptionalLogLevel::Null)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
		{
			NamedLoggerRegistryPtr r(logger, &(logger->GetRegistry()));
			_token = MakeToken<FunctionToken>(bind(&NamedLoggerRegistry::Unregister, r, r->Register(_params.GetName(), this)));
		}
	}

	NamedLogger::~NamedLogger()
	{ _token.Reset(); }

	/////////////////////////////////////////////////////////////////

	struct LogLevelHolder
	{
		static u32 s_logLevel;
	};

	u32 LogLevelHolder::s_logLevel = (u32)LogLevel::Info;

	void Logger::SetLogLevel(LogLevel logLevel)
	{
		AtomicU32::Store(LogLevelHolder::s_logLevel, (u32)logLevel.val(), MemoryOrderRelaxed);
		Stream(logLevel) << "Log level is " << logLevel;
	}

	LogLevel Logger::GetLogLevel()
	{ return (LogLevel::Enum)AtomicU32::Load(LogLevelHolder::s_logLevel, MemoryOrderRelaxed); }

	LoggerStream Logger::Stream(LogLevel logLevel, DuplicatingLogsFilter* duplicatingLogsFilter)
	{ return LoggerStream(null, GetLogLevel(), logLevel, duplicatingLogsFilter, &Logger::DoLog); }

	void Logger::SetLogLevel(const std::string& loggerName, optional<LogLevel> logLevel)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().SetLogLevel(loggerName, logLevel);
	}

	void Logger::EnableBacktrace(const std::string& loggerName, bool enable)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().EnableBacktrace(loggerName, enable);
	}

	void Logger::EnableHighlight(const std::string& loggerName, bool enable)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().EnableHighlight(loggerName, enable);
	}

	void Logger::GetLoggerNames(std::set<std::string>& out)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (logger)
			logger->GetRegistry().GetLoggerNames(out);
	}

	Token Logger::AddSink(const ILoggerSinkPtr& sink)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		if (!logger)
			return null;

		logger->AddSink(sink);
		return MakeToken<FunctionToken>(bind(&LoggerImpl::RemoveSink, logger, sink));
	}

	void Logger::DoLog(const NamedLoggerParams* loggerParams, LogLevel logLevel, const std::string& text)
	{
		LoggerImplPtr logger = LoggerSingleton::Instance();
		LogLevel ll = logger ? GetLogLevel() : LogLevel(LogLevel::Debug);
		if (!loggerParams && logLevel < ll) // NamedLogger LoggerStream checks the log level in its destructor
			return;

		optional<LoggerMessage> msg;
		if (loggerParams)
			msg = LoggerMessage(loggerParams->GetName(), logLevel, text + (loggerParams->BacktraceEnabled() ? ": " + Backtrace().Get() : std::string()), loggerParams->HighlightEnabled());
		else
			msg = LoggerMessage(logLevel, text, false);

		if (logger)
			logger->Log(*msg);
		else
			SystemLogger::Log(*msg);
	}

	LogLevel NamedLogger::GetLogLevel() const
	{
		optional<LogLevel> logLevel = OptionalLogLevel::ToLogLevel(_logLevel.load(MemoryOrderRelaxed));
		return logLevel ? *logLevel : Logger::GetLogLevel();
	}

	void NamedLogger::SetLogLevel(optional<LogLevel> logLevel)
	{
		_logLevel.store(OptionalLogLevel::FromLogLevel(logLevel), MemoryOrderRelaxed);
		Stream(GetLogLevel()) << "Log level is " << logLevel;
	}

	bool NamedLogger::BacktraceEnabled() const
	{ return _params.BacktraceEnabled(); }

	void NamedLogger::EnableBacktrace(bool enable)
	{
		_params.EnableBacktrace(enable);
		Stream(GetLogLevel()) << "Backtrace " << (enable ? "enabled" : "disabled");
	}

	bool NamedLogger::HighlightEnabled() const
	{ return _params.HighlightEnabled(); }

	void NamedLogger::EnableHighlight(bool enable)
	{ _params.EnableHighlight(enable); }

	LoggerStream NamedLogger::Stream(LogLevel logLevel) const
	{ return LoggerStream(&_params, GetLogLevel(), logLevel, &_duplicatingLogsFilter, &Logger::DoLog); }

	void NamedLogger::Log(LogLevel logLevel, const std::string& message)
	{ Stream(logLevel) << message; }

	/////////////////////////////////////////////////////////////////

	namespace
	{

		class ElapsedMillisecondsToString
		{
		private:
			const ElapsedTime&	_elapsed;

		public:
			explicit ElapsedMillisecondsToString(const ElapsedTime& elapsed) : _elapsed(elapsed) { }

			std::string ToString() const
			{
				s64 ms = 0;
				int mms = 0;

				try { s64 e = _elapsed.ElapsedMicroseconds(); ms = e / 1000; mms = e % 1000; }
				catch (const std::exception&) { }

				return StringBuilder() % ms % "." % mms;
			}
		};

	}

	ActionLogger::ActionLogger(const std::string& action)
		: _namedLogger(NULL), _action(action)
	{ Logger::Info() << _action << "..."; }

	ActionLogger::ActionLogger(NamedLogger& namedLogger, const std::string& action)
		: _namedLogger(&namedLogger), _action(action)
	{ _namedLogger->Info() << _action << "..."; }

	ActionLogger::~ActionLogger()
	{
		if (std::uncaught_exception())
		{
			try
			{
				if (_namedLogger)
					_namedLogger->Info() << _action << " completed with exception in " << ElapsedMillisecondsToString(_elapsedTime) << " ms";
				else
					Logger::Info() << _action << " completed with exception in " << ElapsedMillisecondsToString(_elapsedTime) << " ms";
			}
			catch (const std::exception&)
			{ return; }
		}
		else
		{
			if (_namedLogger)
				_namedLogger->Info() << _action << " completed in " << ElapsedMillisecondsToString(_elapsedTime) << " ms";
			else
				Logger::Info() << _action << " completed in " << ElapsedMillisecondsToString(_elapsedTime) << " ms";
		}
	}

	/////////////////////////////////////////////////////////////////

	Tracer::Tracer(const Detail::NamedLoggerAccessor& logger, const char* funcName) :
		_logger(logger), _logLevel(logger.GetLogLevel()), _funcName(funcName)
	{
		if (_logLevel > LogLevel::Trace)
			return;

		_startTime = TimeEngine::GetMonotonicMicroseconds();
		_logger.Trace() << "TRACER: entering function '" << _funcName << "'";
	}

	Tracer::~Tracer()
	{
		if (_logLevel > LogLevel::Trace)
			return;

		try
		{
			s64 e = TimeEngine::GetMonotonicMicroseconds() - _startTime;
			s64 ms = e / 1000;
			int mms = e % 1000;

			if (std::uncaught_exception())
				_logger.Trace() << "TRACER: leaving function '" << _funcName << "' due to an exception (" << StringFormat("%1%.%2$3%", ms, mms) << " ms)";
			else
				_logger.Trace() << "TRACER: leaving function '" << _funcName << "' (" << StringFormat("%1%.%2$3%", ms, mms) << " ms)";
		}
		catch(const std::exception&)
		{ }
	}

	/////////////////////////////////////////////////////////////////

	void LogException(const std::exception& ex)
	{ Logger::Error() << ex; }

	void LogExceptionTo(NamedLogger& namedLogger, const std::exception& ex)
	{ namedLogger.Error() << ex; }

}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.

#include "browser/url_request_context_getter.h"

#include <algorithm>

#include "browser/network_delegate.h"

#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/worker_pool.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/common/content_switches.h"
#include "net/base/host_mapping_rules.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/dns/mapped_host_resolver.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_server_properties_impl.h"
#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_script_fetcher_impl.h"
#include "net/proxy/proxy_service.h"
#include "net/proxy/proxy_service_v8.h"
#include "net/ssl/channel_id_service.h"
#include "net/ssl/default_channel_id_store.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/file_protocol_handler.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_storage.h"
#include "net/url_request/url_request_intercepting_job_factory.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "url/url_constants.h"
#include "storage/browser/quota/special_storage_policy.h"

using content::BrowserThread;

namespace brightray {

namespace {

// Comma-separated list of rules that control how hostnames are mapped.
//
// For example:
//    "MAP * 127.0.0.1" --> Forces all hostnames to be mapped to 127.0.0.1
//    "MAP *.google.com proxy" --> Forces all google.com subdomains to be
//                                 resolved to "proxy".
//    "MAP test.com [::1]:77 --> Forces "test.com" to resolve to IPv6 loopback.
//                               Will also force the port of the resulting
//                               socket address to be 77.
//    "MAP * baz, EXCLUDE www.google.com" --> Remaps everything to "baz",
//                                            except for "www.google.com".
//
// These mappings apply to the endpoint host in a net::URLRequest (the TCP
// connect and host resolver in a direct connection, and the CONNECT in an http
// proxy connection, and the endpoint host in a SOCKS proxy connection).
const char kHostRules[] = "host-rules";

// Don't use a proxy server, always make direct connections. Overrides any
// other proxy server flags that are passed.
const char kNoProxyServer[] = "no-proxy-server";

// Uses a specified proxy server, overrides system settings. This switch only
// affects HTTP and HTTPS requests.
const char kProxyServer[] = "proxy-server";

}  // namespace

net::URLRequestJobFactory* URLRequestContextGetter::Delegate::CreateURLRequestJobFactory(
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector* protocol_interceptors) {
  scoped_ptr<net::URLRequestJobFactoryImpl> job_factory(new net::URLRequestJobFactoryImpl);

  for (auto it = protocol_handlers->begin(); it != protocol_handlers->end(); ++it)
    job_factory->SetProtocolHandler(it->first, it->second.release());
  protocol_handlers->clear();

  job_factory->SetProtocolHandler(url::kDataScheme, new net::DataProtocolHandler);
  job_factory->SetProtocolHandler(url::kFileScheme, new net::FileProtocolHandler(
      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));

  // Set up interceptors in the reverse order.
  scoped_ptr<net::URLRequestJobFactory> top_job_factory = job_factory.Pass();
  content::URLRequestInterceptorScopedVector::reverse_iterator i;
  for (i = protocol_interceptors->rbegin(); i != protocol_interceptors->rend(); ++i)
    top_job_factory.reset(new net::URLRequestInterceptingJobFactory(
        top_job_factory.Pass(), make_scoped_ptr(*i)));
  protocol_interceptors->weak_clear();

  return top_job_factory.release();
}

net::HttpCache::BackendFactory*
URLRequestContextGetter::Delegate::CreateHttpCacheBackendFactory(const base::FilePath& base_path) {
  base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache"));
  return new net::HttpCache::DefaultBackend(
      net::DISK_CACHE,
      net::CACHE_BACKEND_DEFAULT,
      cache_path,
      0,
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
}

URLRequestContextGetter::URLRequestContextGetter(
    Delegate* delegate,
    const base::FilePath& base_path,
    base::MessageLoop* io_loop,
    base::MessageLoop* file_loop,
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector protocol_interceptors)
    : delegate_(delegate),
      base_path_(base_path),
      io_loop_(io_loop),
      file_loop_(file_loop),
      protocol_interceptors_(protocol_interceptors.Pass()) {
  // Must first be created on the UI thread.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  std::swap(protocol_handlers_, *protocol_handlers);

  // We must create the proxy config service on the UI loop on Linux because it
  // must synchronously run on the glib message loop. This will be passed to
  // the URLRequestContextStorage on the IO thread in GetURLRequestContext().
  proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
      io_loop_->message_loop_proxy(), file_loop_->message_loop_proxy()));
}

URLRequestContextGetter::~URLRequestContextGetter() {
}

net::HostResolver* URLRequestContextGetter::host_resolver() {
  return url_request_context_->host_resolver();
}

net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (!url_request_context_.get()) {
    url_request_context_.reset(new net::URLRequestContext);
    network_delegate_.reset(delegate_->CreateNetworkDelegate());
    url_request_context_->set_network_delegate(network_delegate_.get());

    storage_.reset(new net::URLRequestContextStorage(url_request_context_.get()));
    auto cookie_config = content::CookieStoreConfig(
        base_path_.Append(FILE_PATH_LITERAL("Cookies")),
        content::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES,
        NULL, NULL);
    storage_->set_cookie_store(content::CreateCookieStore(cookie_config));
    storage_->set_channel_id_service(new net::ChannelIDService(
        new net::DefaultChannelIDStore(NULL),
        base::WorkerPool::GetTaskRunner(true)));
    storage_->set_http_user_agent_settings(new net::StaticHttpUserAgentSettings(
        "en-us,en", base::EmptyString()));

    scoped_ptr<net::HostResolver> host_resolver(net::HostResolver::CreateDefaultResolver(NULL));

    // --host-resolver-rules
    if (command_line.HasSwitch(switches::kHostResolverRules)) {
      scoped_ptr<net::MappedHostResolver> remapped_resolver(
          new net::MappedHostResolver(host_resolver.Pass()));
      remapped_resolver->SetRulesFromString(
          command_line.GetSwitchValueASCII(switches::kHostResolverRules));
      host_resolver = remapped_resolver.Pass();
    }

    // --proxy-server
    net::DhcpProxyScriptFetcherFactory dhcp_factory;
    if (command_line.HasSwitch(kNoProxyServer))
      storage_->set_proxy_service(net::ProxyService::CreateDirect());
    else if (command_line.HasSwitch(kProxyServer))
      storage_->set_proxy_service(net::ProxyService::CreateFixed(
          command_line.GetSwitchValueASCII(kProxyServer)));
    else
      storage_->set_proxy_service(
          net::CreateProxyServiceUsingV8ProxyResolver(
              proxy_config_service_.release(),
              new net::ProxyScriptFetcherImpl(url_request_context_.get()),
              dhcp_factory.Create(url_request_context_.get()),
              host_resolver.get(),
              NULL,
              url_request_context_->network_delegate()));

    storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
    storage_->set_transport_security_state(new net::TransportSecurityState);
    storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
    storage_->set_http_auth_handler_factory(
        net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
    scoped_ptr<net::HttpServerProperties> server_properties(
        new net::HttpServerPropertiesImpl);
    storage_->set_http_server_properties(server_properties.Pass());

    net::HttpNetworkSession::Params network_session_params;
    network_session_params.cert_verifier = url_request_context_->cert_verifier();
    network_session_params.proxy_service = url_request_context_->proxy_service();
    network_session_params.ssl_config_service = url_request_context_->ssl_config_service();
    network_session_params.network_delegate = url_request_context_->network_delegate();
    network_session_params.http_server_properties = url_request_context_->http_server_properties();
    network_session_params.ignore_certificate_errors = false;
    network_session_params.transport_security_state =
        url_request_context_->transport_security_state();
    network_session_params.channel_id_service =
        url_request_context_->channel_id_service();
    network_session_params.http_auth_handler_factory =
        url_request_context_->http_auth_handler_factory();

    // --ignore-certificate-errors
    if (command_line.HasSwitch(switches::kIgnoreCertificateErrors))
      network_session_params.ignore_certificate_errors = true;

    // --host-rules
    if (command_line.HasSwitch(kHostRules)) {
      host_mapping_rules_.reset(new net::HostMappingRules);
      host_mapping_rules_->SetRulesFromString(command_line.GetSwitchValueASCII(kHostRules));
      network_session_params.host_mapping_rules = host_mapping_rules_.get();
    }

    // Give |storage_| ownership at the end in case it's |mapped_host_resolver|.
    storage_->set_host_resolver(host_resolver.Pass());
    network_session_params.host_resolver = url_request_context_->host_resolver();

    net::HttpCache::BackendFactory* backend = delegate_->CreateHttpCacheBackendFactory(base_path_);
    storage_->set_http_transaction_factory(new net::HttpCache(network_session_params, backend));

    storage_->set_job_factory(delegate_->CreateURLRequestJobFactory(
        &protocol_handlers_, &protocol_interceptors_));
  }

  return url_request_context_.get();
}

scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetter::GetNetworkTaskRunner() const {
  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}

}  // namespace brightray
<commit_msg>Fix url_request_context_getter.cc<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.

#include "browser/url_request_context_getter.h"

#include <algorithm>

#include "browser/network_delegate.h"

#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/worker_pool.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/common/content_switches.h"
#include "net/base/host_mapping_rules.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/dns/mapped_host_resolver.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_server_properties_impl.h"
#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_script_fetcher_impl.h"
#include "net/proxy/proxy_service.h"
#include "net/proxy/proxy_service_v8.h"
#include "net/ssl/channel_id_service.h"
#include "net/ssl/default_channel_id_store.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/file_protocol_handler.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_storage.h"
#include "net/url_request/url_request_intercepting_job_factory.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "url/url_constants.h"
#include "storage/browser/quota/special_storage_policy.h"

using content::BrowserThread;

namespace brightray {

namespace {

// Comma-separated list of rules that control how hostnames are mapped.
//
// For example:
//    "MAP * 127.0.0.1" --> Forces all hostnames to be mapped to 127.0.0.1
//    "MAP *.google.com proxy" --> Forces all google.com subdomains to be
//                                 resolved to "proxy".
//    "MAP test.com [::1]:77 --> Forces "test.com" to resolve to IPv6 loopback.
//                               Will also force the port of the resulting
//                               socket address to be 77.
//    "MAP * baz, EXCLUDE www.google.com" --> Remaps everything to "baz",
//                                            except for "www.google.com".
//
// These mappings apply to the endpoint host in a net::URLRequest (the TCP
// connect and host resolver in a direct connection, and the CONNECT in an http
// proxy connection, and the endpoint host in a SOCKS proxy connection).
const char kHostRules[] = "host-rules";

// Don't use a proxy server, always make direct connections. Overrides any
// other proxy server flags that are passed.
const char kNoProxyServer[] = "no-proxy-server";

// Uses a specified proxy server, overrides system settings. This switch only
// affects HTTP and HTTPS requests.
const char kProxyServer[] = "proxy-server";

}  // namespace

net::URLRequestJobFactory* URLRequestContextGetter::Delegate::CreateURLRequestJobFactory(
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector* protocol_interceptors) {
  scoped_ptr<net::URLRequestJobFactoryImpl> job_factory(new net::URLRequestJobFactoryImpl);

  for (auto it = protocol_handlers->begin(); it != protocol_handlers->end(); ++it)
    job_factory->SetProtocolHandler(it->first, it->second.release());
  protocol_handlers->clear();

  job_factory->SetProtocolHandler(url::kDataScheme, new net::DataProtocolHandler);
  job_factory->SetProtocolHandler(url::kFileScheme, new net::FileProtocolHandler(
      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));

  // Set up interceptors in the reverse order.
  scoped_ptr<net::URLRequestJobFactory> top_job_factory = job_factory.Pass();
  content::URLRequestInterceptorScopedVector::reverse_iterator i;
  for (i = protocol_interceptors->rbegin(); i != protocol_interceptors->rend(); ++i)
    top_job_factory.reset(new net::URLRequestInterceptingJobFactory(
        top_job_factory.Pass(), make_scoped_ptr(*i)));
  protocol_interceptors->weak_clear();

  return top_job_factory.release();
}

net::HttpCache::BackendFactory*
URLRequestContextGetter::Delegate::CreateHttpCacheBackendFactory(const base::FilePath& base_path) {
  base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache"));
  return new net::HttpCache::DefaultBackend(
      net::DISK_CACHE,
      net::CACHE_BACKEND_DEFAULT,
      cache_path,
      0,
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
}

URLRequestContextGetter::URLRequestContextGetter(
    Delegate* delegate,
    const base::FilePath& base_path,
    base::MessageLoop* io_loop,
    base::MessageLoop* file_loop,
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector protocol_interceptors)
    : delegate_(delegate),
      base_path_(base_path),
      io_loop_(io_loop),
      file_loop_(file_loop),
      protocol_interceptors_(protocol_interceptors.Pass()) {
  // Must first be created on the UI thread.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  std::swap(protocol_handlers_, *protocol_handlers);

  // We must create the proxy config service on the UI loop on Linux because it
  // must synchronously run on the glib message loop. This will be passed to
  // the URLRequestContextStorage on the IO thread in GetURLRequestContext().
  proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
      io_loop_->message_loop_proxy(), file_loop_->message_loop_proxy()));
}

URLRequestContextGetter::~URLRequestContextGetter() {
}

net::HostResolver* URLRequestContextGetter::host_resolver() {
  return url_request_context_->host_resolver();
}

net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  auto& command_line = *base::CommandLine::ForCurrentProcess();
  if (!url_request_context_.get()) {
    url_request_context_.reset(new net::URLRequestContext);
    network_delegate_.reset(delegate_->CreateNetworkDelegate());
    url_request_context_->set_network_delegate(network_delegate_.get());

    storage_.reset(new net::URLRequestContextStorage(url_request_context_.get()));
    auto cookie_config = content::CookieStoreConfig(
        base_path_.Append(FILE_PATH_LITERAL("Cookies")),
        content::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES,
        NULL, NULL);
    storage_->set_cookie_store(content::CreateCookieStore(cookie_config));
    storage_->set_channel_id_service(new net::ChannelIDService(
        new net::DefaultChannelIDStore(NULL),
        base::WorkerPool::GetTaskRunner(true)));
    storage_->set_http_user_agent_settings(new net::StaticHttpUserAgentSettings(
        "en-us,en", base::EmptyString()));

    scoped_ptr<net::HostResolver> host_resolver(net::HostResolver::CreateDefaultResolver(NULL));

    // --host-resolver-rules
    if (command_line.HasSwitch(switches::kHostResolverRules)) {
      scoped_ptr<net::MappedHostResolver> remapped_resolver(
          new net::MappedHostResolver(host_resolver.Pass()));
      remapped_resolver->SetRulesFromString(
          command_line.GetSwitchValueASCII(switches::kHostResolverRules));
      host_resolver = remapped_resolver.Pass();
    }

    // --proxy-server
    net::DhcpProxyScriptFetcherFactory dhcp_factory;
    if (command_line.HasSwitch(kNoProxyServer))
      storage_->set_proxy_service(net::ProxyService::CreateDirect());
    else if (command_line.HasSwitch(kProxyServer))
      storage_->set_proxy_service(net::ProxyService::CreateFixed(
          command_line.GetSwitchValueASCII(kProxyServer)));
    else
      storage_->set_proxy_service(
          net::CreateProxyServiceUsingV8ProxyResolver(
              proxy_config_service_.release(),
              new net::ProxyScriptFetcherImpl(url_request_context_.get()),
              dhcp_factory.Create(url_request_context_.get()),
              host_resolver.get(),
              NULL,
              url_request_context_->network_delegate()));

    storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
    storage_->set_transport_security_state(new net::TransportSecurityState);
    storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
    storage_->set_http_auth_handler_factory(
        net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
    scoped_ptr<net::HttpServerProperties> server_properties(
        new net::HttpServerPropertiesImpl);
    storage_->set_http_server_properties(server_properties.Pass());

    net::HttpNetworkSession::Params network_session_params;
    network_session_params.cert_verifier = url_request_context_->cert_verifier();
    network_session_params.proxy_service = url_request_context_->proxy_service();
    network_session_params.ssl_config_service = url_request_context_->ssl_config_service();
    network_session_params.network_delegate = url_request_context_->network_delegate();
    network_session_params.http_server_properties = url_request_context_->http_server_properties();
    network_session_params.ignore_certificate_errors = false;
    network_session_params.transport_security_state =
        url_request_context_->transport_security_state();
    network_session_params.channel_id_service =
        url_request_context_->channel_id_service();
    network_session_params.http_auth_handler_factory =
        url_request_context_->http_auth_handler_factory();

    // --ignore-certificate-errors
    if (command_line.HasSwitch(switches::kIgnoreCertificateErrors))
      network_session_params.ignore_certificate_errors = true;

    // --host-rules
    if (command_line.HasSwitch(kHostRules)) {
      host_mapping_rules_.reset(new net::HostMappingRules);
      host_mapping_rules_->SetRulesFromString(command_line.GetSwitchValueASCII(kHostRules));
      network_session_params.host_mapping_rules = host_mapping_rules_.get();
    }

    // Give |storage_| ownership at the end in case it's |mapped_host_resolver|.
    storage_->set_host_resolver(host_resolver.Pass());
    network_session_params.host_resolver = url_request_context_->host_resolver();

    net::HttpCache::BackendFactory* backend = delegate_->CreateHttpCacheBackendFactory(base_path_);
    storage_->set_http_transaction_factory(new net::HttpCache(network_session_params, backend));

    storage_->set_job_factory(delegate_->CreateURLRequestJobFactory(
        &protocol_handlers_, &protocol_interceptors_));
  }

  return url_request_context_.get();
}

scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetter::GetNetworkTaskRunner() const {
  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}

}  // namespace brightray
<|endoftext|>


<commit_before>// Copyright Daniel Wallin 2008. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

# ifndef LUABIND_DEDUCE_SIGNATURE_080911_HPP
#  define LUABIND_DEDUCE_SIGNATURE_080911_HPP

#  include <luabind/detail/most_derived.hpp>

#  include <boost/function_types/components.hpp>
#  include <boost/type_traits/add_reference.hpp>

namespace luabind { namespace detail {

namespace mpl = boost::mpl;
namespace fty = boost::function_types;

template <class F>
fty::components<F> deduce_signature(F, ...)
{
    return fty::components<F>();
}

template <class F, class Wrapped>
fty::components<F, boost::add_reference<most_derived<mpl::_1, Wrapped> > >
deduce_signature(F, Wrapped*)
{
    return fty::components<F, boost::add_reference<most_derived<mpl::_1, Wrapped> > >();
}

} } // namespace luabind::detail

#endif  // LUABIND_DEDUCE_SIGNATURE_080911_HPP
<commit_msg>Use enable_if to keep tag_function working.<commit_after>// Copyright Daniel Wallin 2008. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

# ifndef LUABIND_DEDUCE_SIGNATURE_080911_HPP
#  define LUABIND_DEDUCE_SIGNATURE_080911_HPP

#  include <luabind/detail/most_derived.hpp>

#  include <boost/function_types/components.hpp>
#  include <boost/function_types/is_callable_builtin.hpp>
#  include <boost/function_types/is_member_function_pointer.hpp>
#  include <boost/type_traits/add_reference.hpp>
#  include <boost/utility/enable_if.hpp>

namespace luabind { namespace detail {

namespace mpl = boost::mpl;
namespace fty = boost::function_types;

template <class F>
struct signature_aux
{
    typedef F type;
};

template <class F>
typename boost::enable_if<
    fty::is_callable_builtin<typename signature_aux<F>::type>,
    fty::components<typename signature_aux<F>::type> >::type
deduce_signature(F const&, ...)
{
    return fty::components<typename signature_aux<F>::type>();
}

template <class F, class Wrapped>
typename boost::enable_if<
    fty::is_member_function_pointer<typename signature_aux<F>::type>,
    fty::components<
        typename signature_aux<F>::type,
        boost::add_reference<most_derived<mpl::_1, Wrapped> > > >::type
deduce_signature(F, Wrapped*)
{
    return fty::components<
        typename signature_aux<F>::type,
        boost::add_reference<most_derived<mpl::_1, Wrapped> > >();
}

} } // namespace luabind::detail

#endif  // LUABIND_DEDUCE_SIGNATURE_080911_HPP
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <cassert>
#include <map>
#include <memory>
#include <cstdint>
#include <cstddef>
#include <entt/entity/registry.hpp>
#include "duktape.h"

template<typename Type>
struct tag { using type = Type; };

struct Position {
    double x;
    double y;
};

struct Renderable {};

struct DuktapeRuntime {
    std::map<duk_uint_t, std::string> components;
};

template<typename Comp>
duk_ret_t set(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    registry.accomodate<Comp>(entity);
    return 0;
}

template<>
duk_ret_t set<Position>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto x = duk_require_number(ctx, 2);
    const auto y = duk_require_number(ctx, 3);
    registry.accomodate<Position>(entity, x, y);
    return 0;
}

template<>
duk_ret_t set<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    duk_dup(ctx, 2);

    if(!registry.has<DuktapeRuntime>(entity)) {
        registry.assign<DuktapeRuntime>(entity).components[type] = duk_json_encode(ctx, -1);
    } else {
        registry.get<DuktapeRuntime>(entity).components[type] = duk_json_encode(ctx, -1);
    }

    duk_pop(ctx);

    return 0;
}

template<typename Comp>
duk_ret_t unset(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    registry.remove<Comp>(entity);
    return 0;
}

template<>
duk_ret_t unset<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    auto &components = registry.get<DuktapeRuntime>(entity).components;
    assert(components.find(type) != components.cend());
    components.erase(type);

    if(components.empty()) {
        registry.remove<DuktapeRuntime>(entity);
    }

    return 0;
}

template<typename Comp>
duk_ret_t has(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    duk_push_boolean(ctx, registry.has<Comp>(entity));
    return 1;
}

template<>
duk_ret_t has<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    duk_push_boolean(ctx, registry.has<DuktapeRuntime>(entity));

    if(registry.has<DuktapeRuntime>(entity)) {
        const auto type = duk_require_uint(ctx, 1);
        const auto &components = registry.get<DuktapeRuntime>(entity).components;
        duk_push_boolean(ctx, components.find(type) != components.cend());
    } else {
        duk_push_false(ctx);
    }

    return 1;
}

template<typename Comp>
duk_ret_t get(duk_context *ctx, entt::DefaultRegistry &registry) {
    assert(registry.has<Comp>(duk_require_uint(ctx, 0)));
    duk_push_object(ctx);
    return 1;
}

template<>
duk_ret_t get<Position>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto &position = registry.get<Position>(entity);

    const auto idx = duk_push_object(ctx);

    duk_push_string(ctx, "x");
    duk_push_number(ctx, position.x);
    duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);

    duk_push_string(ctx, "y");
    duk_push_number(ctx, position.y);
    duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);

    return 1;
}

template<>
duk_ret_t get<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    auto &runtime = registry.get<DuktapeRuntime>(entity);
    assert(runtime.components.find(type) != runtime.components.cend());

    duk_push_string(ctx, runtime.components[type].c_str());
    duk_json_decode(ctx, -1);

    return 1;
}

class DuktapeRegistry {
    // I'm pretty sure I won't have more than 99 components in the example
    static constexpr entt::DefaultRegistry::component_type udef = 100;

    struct Func {
        using func_type = duk_ret_t(*)(duk_context *, entt::DefaultRegistry &);
        using test_type = bool(entt::DefaultRegistry:: *)(entt::DefaultRegistry::entity_type) const;

        func_type set;
        func_type unset;
        func_type has;
        func_type get;
        test_type test;
    };

    template<typename... Comp>
    void reg() {
        using accumulator_type = int[];
        accumulator_type acc = { (func[registry.component<Comp>()] = {
                                     &::set<Comp>,
                                     &::unset<Comp>,
                                     &::has<Comp>,
                                     &::get<Comp>,
                                     &entt::DefaultRegistry::has<Comp>
                                 }, 0)... };
        (void)acc;
    }

    static DuktapeRegistry & instance(duk_context *ctx) {
        duk_push_this(ctx);

        duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
        duk_get_prop(ctx, -2);
        auto &dreg = *static_cast<DuktapeRegistry *>(duk_require_pointer(ctx, -1));
        duk_pop_2(ctx);

        return dreg;
    }

    template<Func::func_type Func::*Op>
    static duk_ret_t invoke(duk_context *ctx) {
        auto &dreg = instance(ctx);
        auto &func = dreg.func;
        auto &registry = dreg.registry;
        auto type = duk_require_uint(ctx, 1);

        if(type >= udef) {
            type = registry.component<DuktapeRuntime>();
        }

        assert(func.find(type) != func.cend());

        return (func[type].*Op)(ctx, registry);
    }

public:
    DuktapeRegistry(entt::DefaultRegistry &registry)
        : registry{registry}
    {
        reg<Position, Renderable, DuktapeRuntime>();
    }

    static duk_ret_t identifier(duk_context *ctx) {
        static auto next = udef;
        duk_push_uint(ctx, next++);
        return 1;
    }

    static duk_ret_t create(duk_context *ctx) {
        auto &dreg = instance(ctx);
        duk_push_uint(ctx, dreg.registry.create());
        return 1;
    }

    static duk_ret_t set(duk_context *ctx) {
        return invoke<&Func::set>(ctx);
    }

    static duk_ret_t unset(duk_context *ctx) {
        return invoke<&Func::unset>(ctx);
    }

    static duk_ret_t has(duk_context *ctx) {
        return invoke<&Func::has>(ctx);
    }

    static duk_ret_t get(duk_context *ctx) {
        return invoke<&Func::get>(ctx);
    }

    static duk_ret_t entities(duk_context *ctx) {
        const duk_idx_t nargs = duk_get_top(ctx);
        auto &dreg = instance(ctx);
        duk_uarridx_t pos = 0;

        duk_push_array(ctx);

        dreg.registry.each([ctx, nargs, &pos, &dreg](auto entity) {
            auto &registry = dreg.registry;
            auto &func = dreg.func;
            bool match = true;

            for (duk_idx_t arg = 0; match && arg < nargs; arg++) {
                auto type = duk_require_uint(ctx, arg);

                if(type < udef) {
                    assert(func.find(type) != func.cend());
                    match = (registry.*func[type].test)(entity);
                } else {
                    const auto ctype = registry.component<DuktapeRuntime>();
                    assert(func.find(ctype) != func.cend());
                    match = (registry.*func[ctype].test)(entity);

                    if(match) {
                        auto &components = registry.get<DuktapeRuntime>(entity).components;
                        match = (components.find(type) != components.cend());
                    }
                }
            }

            if(match) {
                duk_push_uint(ctx, entity);
                duk_put_prop_index(ctx, -2, pos++);
            }
        });

        return 1;
    }

private:
    std::map<duk_uint_t, Func> func;
    entt::DefaultRegistry &registry;
};

const duk_function_list_entry js_DuktapeRegistry_methods[] = {
    { "identifier", &DuktapeRegistry::identifier, 0 },
    { "create", &DuktapeRegistry::create, 0 },
    { "set", &DuktapeRegistry::set, DUK_VARARGS },
    { "unset", &DuktapeRegistry::unset, 2 },
    { "has", &DuktapeRegistry::has, 2 },
    { "get", &DuktapeRegistry::get, 2 },
    { "entities", &DuktapeRegistry::entities, DUK_VARARGS },
    { nullptr, nullptr, 0 }
};

void exportTypes(duk_context *ctx, entt::DefaultRegistry &registry) {
    auto exportType = [](auto *ctx, auto &registry, auto idx, auto type, const auto *name) {
        duk_push_string(ctx, name);
        duk_push_uint(ctx, registry.template component<typename decltype(type)::type>());
        duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
    };

    auto idx = duk_push_object(ctx);

    exportType(ctx, registry, idx, tag<Position>{}, "POSITION");
    exportType(ctx, registry, idx, tag<Renderable>{}, "RENDERABLE");

    duk_put_global_string(ctx, "Types");
}

void exportDuktapeRegistry(duk_context *ctx, DuktapeRegistry &dreg) {
    auto idx = duk_push_object(ctx);

    duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
    duk_push_pointer(ctx, &dreg);
    duk_put_prop(ctx, idx);

    duk_put_function_list(ctx, idx, js_DuktapeRegistry_methods);
    duk_put_global_string(ctx, "Registry");
}

TEST(Mod, Duktape) {
    entt::DefaultRegistry registry;
    DuktapeRegistry dreg{registry};
    duk_context *ctx = duk_create_heap_default();

    if(!ctx) {
        FAIL();
    }

    exportTypes(ctx, registry);
    exportDuktapeRegistry(ctx, dreg);

    const char *s0 = ""
            "Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
            "Types[\"VELOCITY\"] = Registry.identifier();"
            "";

    if(duk_peval_string(ctx, s0)) {
        FAIL();
    }

    registry.create(Position{ 0., 0. }, Renderable{});
    registry.create(Position{ 0., 0. });

    const char *s1 = ""
            "Registry.entities(Types.POSITION, Types.RENDERABLE).forEach(function(entity) {"
                "Registry.set(entity, Types.POSITION, 100., 100.);"
            "});"
            "var entity = Registry.create();"
            "Registry.set(entity, Types.POSITION, 100., 100.);"
            "Registry.set(entity, Types.RENDERABLE);"
            "";

    if(duk_peval_string(ctx, s1)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<Position>().each([&registry](auto entity, const auto &position) {
        ASSERT_FALSE(registry.has<DuktapeRuntime>(entity));

        if(registry.has<Renderable>(entity)) {
            ASSERT_EQ(position.x, 100.);
            ASSERT_EQ(position.y, 100.);
        } else {
            ASSERT_EQ(position.x, 0.);
            ASSERT_EQ(position.y, 0.);
        }
    });

    const char *s2 = ""
            "Registry.entities(Types.POSITION).forEach(function(entity) {"
                "if(!Registry.has(entity, Types.RENDERABLE)) {"
                    "Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
                    "Registry.set(entity, Types.PLAYING_CHARACTER, {});"
                "}"
            "});"
            "";

    if(duk_peval_string(ctx, s2)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<DuktapeRuntime>().each([](auto, const DuktapeRuntime &runtime) {
        ASSERT_EQ(runtime.components.size(), 2u);
    });

    const char *s3 = ""
            "Registry.entities(Types.POSITION, Types.RENDERABLE, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
                "var velocity = Registry.get(entity, Types.VELOCITY);"
                "Registry.set(entity, Types.POSITION, velocity.dx, velocity.dy)"
            "});"
            "";

    if(duk_peval_string(ctx, s3)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<Position, Renderable, DuktapeRuntime>().each([](auto, const Position &position, const auto &...) {
        ASSERT_EQ(position.x, -100.);
        ASSERT_EQ(position.y, -100.);
    });

    const char *s4 = ""
            "Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
                "Registry.unset(entity, Types.VELOCITY);"
                "Registry.unset(entity, Types.PLAYING_CHARACTER);"
            "});"
            "Registry.entities(Types.POSITION).forEach(function(entity) {"
                "Registry.unset(entity, Types.POSITION);"
            "});"
            "";

    if(duk_peval_string(ctx, s4)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
    ASSERT_EQ(registry.view<Position>().size(), 0u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    duk_destroy_heap(ctx);
}
<commit_msg>cleanup<commit_after>#include <gtest/gtest.h>
#include <cassert>
#include <map>
#include <string>
#include <entt/entity/registry.hpp>
#include "duktape.h"

template<typename Type>
struct tag { using type = Type; };

struct Position {
    double x;
    double y;
};

struct Renderable {};

struct DuktapeRuntime {
    std::map<duk_uint_t, std::string> components;
};

template<typename Comp>
duk_ret_t set(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    registry.accomodate<Comp>(entity);
    return 0;
}

template<>
duk_ret_t set<Position>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto x = duk_require_number(ctx, 2);
    const auto y = duk_require_number(ctx, 3);
    registry.accomodate<Position>(entity, x, y);
    return 0;
}

template<>
duk_ret_t set<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    duk_dup(ctx, 2);

    if(!registry.has<DuktapeRuntime>(entity)) {
        registry.assign<DuktapeRuntime>(entity).components[type] = duk_json_encode(ctx, -1);
    } else {
        registry.get<DuktapeRuntime>(entity).components[type] = duk_json_encode(ctx, -1);
    }

    duk_pop(ctx);

    return 0;
}

template<typename Comp>
duk_ret_t unset(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    registry.remove<Comp>(entity);
    return 0;
}

template<>
duk_ret_t unset<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    auto &components = registry.get<DuktapeRuntime>(entity).components;
    assert(components.find(type) != components.cend());
    components.erase(type);

    if(components.empty()) {
        registry.remove<DuktapeRuntime>(entity);
    }

    return 0;
}

template<typename Comp>
duk_ret_t has(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    duk_push_boolean(ctx, registry.has<Comp>(entity));
    return 1;
}

template<>
duk_ret_t has<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    duk_push_boolean(ctx, registry.has<DuktapeRuntime>(entity));

    if(registry.has<DuktapeRuntime>(entity)) {
        const auto type = duk_require_uint(ctx, 1);
        const auto &components = registry.get<DuktapeRuntime>(entity).components;
        duk_push_boolean(ctx, components.find(type) != components.cend());
    } else {
        duk_push_false(ctx);
    }

    return 1;
}

template<typename Comp>
duk_ret_t get(duk_context *ctx, entt::DefaultRegistry &registry) {
    assert(registry.has<Comp>(duk_require_uint(ctx, 0)));
    duk_push_object(ctx);
    return 1;
}

template<>
duk_ret_t get<Position>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto &position = registry.get<Position>(entity);

    const auto idx = duk_push_object(ctx);

    duk_push_string(ctx, "x");
    duk_push_number(ctx, position.x);
    duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);

    duk_push_string(ctx, "y");
    duk_push_number(ctx, position.y);
    duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);

    return 1;
}

template<>
duk_ret_t get<DuktapeRuntime>(duk_context *ctx, entt::DefaultRegistry &registry) {
    const auto entity = duk_require_uint(ctx, 0);
    const auto type = duk_require_uint(ctx, 1);

    auto &runtime = registry.get<DuktapeRuntime>(entity);
    assert(runtime.components.find(type) != runtime.components.cend());

    duk_push_string(ctx, runtime.components[type].c_str());
    duk_json_decode(ctx, -1);

    return 1;
}

class DuktapeRegistry {
    // I'm pretty sure I won't have more than 99 components in the example
    static constexpr entt::DefaultRegistry::component_type udef = 100;

    struct Func {
        using func_type = duk_ret_t(*)(duk_context *, entt::DefaultRegistry &);
        using test_type = bool(entt::DefaultRegistry:: *)(entt::DefaultRegistry::entity_type) const;

        func_type set;
        func_type unset;
        func_type has;
        func_type get;
        test_type test;
    };

    template<typename... Comp>
    void reg() {
        using accumulator_type = int[];
        accumulator_type acc = { (func[registry.component<Comp>()] = {
                                     &::set<Comp>,
                                     &::unset<Comp>,
                                     &::has<Comp>,
                                     &::get<Comp>,
                                     &entt::DefaultRegistry::has<Comp>
                                 }, 0)... };
        (void)acc;
    }

    static DuktapeRegistry & instance(duk_context *ctx) {
        duk_push_this(ctx);

        duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
        duk_get_prop(ctx, -2);
        auto &dreg = *static_cast<DuktapeRegistry *>(duk_require_pointer(ctx, -1));
        duk_pop_2(ctx);

        return dreg;
    }

    template<Func::func_type Func::*Op>
    static duk_ret_t invoke(duk_context *ctx) {
        auto &dreg = instance(ctx);
        auto &func = dreg.func;
        auto &registry = dreg.registry;
        auto type = duk_require_uint(ctx, 1);

        if(type >= udef) {
            type = registry.component<DuktapeRuntime>();
        }

        assert(func.find(type) != func.cend());

        return (func[type].*Op)(ctx, registry);
    }

public:
    DuktapeRegistry(entt::DefaultRegistry &registry)
        : registry{registry}
    {
        reg<Position, Renderable, DuktapeRuntime>();
    }

    static duk_ret_t identifier(duk_context *ctx) {
        static auto next = udef;
        duk_push_uint(ctx, next++);
        return 1;
    }

    static duk_ret_t create(duk_context *ctx) {
        auto &dreg = instance(ctx);
        duk_push_uint(ctx, dreg.registry.create());
        return 1;
    }

    static duk_ret_t set(duk_context *ctx) {
        return invoke<&Func::set>(ctx);
    }

    static duk_ret_t unset(duk_context *ctx) {
        return invoke<&Func::unset>(ctx);
    }

    static duk_ret_t has(duk_context *ctx) {
        return invoke<&Func::has>(ctx);
    }

    static duk_ret_t get(duk_context *ctx) {
        return invoke<&Func::get>(ctx);
    }

    static duk_ret_t entities(duk_context *ctx) {
        const duk_idx_t nargs = duk_get_top(ctx);
        auto &dreg = instance(ctx);
        duk_uarridx_t pos = 0;

        duk_push_array(ctx);

        dreg.registry.each([ctx, nargs, &pos, &dreg](auto entity) {
            auto &registry = dreg.registry;
            auto &func = dreg.func;
            bool match = true;

            for (duk_idx_t arg = 0; match && arg < nargs; arg++) {
                auto type = duk_require_uint(ctx, arg);

                if(type < udef) {
                    assert(func.find(type) != func.cend());
                    match = (registry.*func[type].test)(entity);
                } else {
                    const auto ctype = registry.component<DuktapeRuntime>();
                    assert(func.find(ctype) != func.cend());
                    match = (registry.*func[ctype].test)(entity);

                    if(match) {
                        auto &components = registry.get<DuktapeRuntime>(entity).components;
                        match = (components.find(type) != components.cend());
                    }
                }
            }

            if(match) {
                duk_push_uint(ctx, entity);
                duk_put_prop_index(ctx, -2, pos++);
            }
        });

        return 1;
    }

private:
    std::map<duk_uint_t, Func> func;
    entt::DefaultRegistry &registry;
};

const duk_function_list_entry js_DuktapeRegistry_methods[] = {
    { "identifier", &DuktapeRegistry::identifier, 0 },
    { "create", &DuktapeRegistry::create, 0 },
    { "set", &DuktapeRegistry::set, DUK_VARARGS },
    { "unset", &DuktapeRegistry::unset, 2 },
    { "has", &DuktapeRegistry::has, 2 },
    { "get", &DuktapeRegistry::get, 2 },
    { "entities", &DuktapeRegistry::entities, DUK_VARARGS },
    { nullptr, nullptr, 0 }
};

void exportTypes(duk_context *ctx, entt::DefaultRegistry &registry) {
    auto exportType = [](auto *ctx, auto &registry, auto idx, auto type, const auto *name) {
        duk_push_string(ctx, name);
        duk_push_uint(ctx, registry.template component<typename decltype(type)::type>());
        duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
    };

    auto idx = duk_push_object(ctx);

    exportType(ctx, registry, idx, tag<Position>{}, "POSITION");
    exportType(ctx, registry, idx, tag<Renderable>{}, "RENDERABLE");

    duk_put_global_string(ctx, "Types");
}

void exportDuktapeRegistry(duk_context *ctx, DuktapeRegistry &dreg) {
    auto idx = duk_push_object(ctx);

    duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
    duk_push_pointer(ctx, &dreg);
    duk_put_prop(ctx, idx);

    duk_put_function_list(ctx, idx, js_DuktapeRegistry_methods);
    duk_put_global_string(ctx, "Registry");
}

TEST(Mod, Duktape) {
    entt::DefaultRegistry registry;
    DuktapeRegistry dreg{registry};
    duk_context *ctx = duk_create_heap_default();

    if(!ctx) {
        FAIL();
    }

    exportTypes(ctx, registry);
    exportDuktapeRegistry(ctx, dreg);

    const char *s0 = ""
            "Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
            "Types[\"VELOCITY\"] = Registry.identifier();"
            "";

    if(duk_peval_string(ctx, s0)) {
        FAIL();
    }

    registry.create(Position{ 0., 0. }, Renderable{});
    registry.create(Position{ 0., 0. });

    const char *s1 = ""
            "Registry.entities(Types.POSITION, Types.RENDERABLE).forEach(function(entity) {"
                "Registry.set(entity, Types.POSITION, 100., 100.);"
            "});"
            "var entity = Registry.create();"
            "Registry.set(entity, Types.POSITION, 100., 100.);"
            "Registry.set(entity, Types.RENDERABLE);"
            "";

    if(duk_peval_string(ctx, s1)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<Position>().each([&registry](auto entity, const auto &position) {
        ASSERT_FALSE(registry.has<DuktapeRuntime>(entity));

        if(registry.has<Renderable>(entity)) {
            ASSERT_EQ(position.x, 100.);
            ASSERT_EQ(position.y, 100.);
        } else {
            ASSERT_EQ(position.x, 0.);
            ASSERT_EQ(position.y, 0.);
        }
    });

    const char *s2 = ""
            "Registry.entities(Types.POSITION).forEach(function(entity) {"
                "if(!Registry.has(entity, Types.RENDERABLE)) {"
                    "Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
                    "Registry.set(entity, Types.PLAYING_CHARACTER, {});"
                "}"
            "});"
            "";

    if(duk_peval_string(ctx, s2)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<DuktapeRuntime>().each([](auto, const DuktapeRuntime &runtime) {
        ASSERT_EQ(runtime.components.size(), 2u);
    });

    const char *s3 = ""
            "Registry.entities(Types.POSITION, Types.RENDERABLE, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
                "var velocity = Registry.get(entity, Types.VELOCITY);"
                "Registry.set(entity, Types.POSITION, velocity.dx, velocity.dy)"
            "});"
            "";

    if(duk_peval_string(ctx, s3)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
    ASSERT_EQ(registry.view<Position>().size(), 3u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    registry.view<Position, Renderable, DuktapeRuntime>().each([](auto, const Position &position, const auto &...) {
        ASSERT_EQ(position.x, -100.);
        ASSERT_EQ(position.y, -100.);
    });

    const char *s4 = ""
            "Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
                "Registry.unset(entity, Types.VELOCITY);"
                "Registry.unset(entity, Types.PLAYING_CHARACTER);"
            "});"
            "Registry.entities(Types.POSITION).forEach(function(entity) {"
                "Registry.unset(entity, Types.POSITION);"
            "});"
            "";

    if(duk_peval_string(ctx, s4)) {
        FAIL();
    }

    ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
    ASSERT_EQ(registry.view<Position>().size(), 0u);
    ASSERT_EQ(registry.view<Renderable>().size(), 2u);

    duk_destroy_heap(ctx);
}
<|endoftext|>