<commit_before>#include <silicium/bridge.hpp>
#include <silicium/consume.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/test/unit_test.hpp>
#include <lua.hpp>

namespace Si
{
	struct lua_deleter
	{
		void operator()(lua_State *L) const
		{
			lua_close(L);
		}
	};

	std::unique_ptr<lua_State, lua_deleter> open_lua()
	{
		auto L = std::unique_ptr<lua_State, lua_deleter>(luaL_newstate());
		if (!L)
		{
			throw std::bad_alloc();
		}
		return L;
	}

	typedef Si::observer<lua_Integer> yield_destination;

	static int yield(lua_State *L)
	{
		yield_destination &dest = *static_cast<yield_destination *>(lua_touserdata(L, lua_upvalueindex(1)));
		lua_Integer element = lua_tointeger(L, 1);
		dest.got_element(element);
		return lua_yield(L, 0);
	}

	BOOST_AUTO_TEST_CASE(lua)
	{
		auto L = open_lua();

		lua_State * const coro = lua_newthread(L.get());
		BOOST_REQUIRE_EQUAL(0, luaL_loadstring(coro, "return function (yield) yield(4) yield(5) end"));
		if (0 != lua_pcall(coro, 0, 1, 0))
		{
			throw std::runtime_error(lua_tostring(L.get(), -1));
		}

		Si::bridge<lua_Integer> yielded;
		lua_pushlightuserdata(coro, &static_cast<yield_destination &>(yielded));
		lua_pushcclosure(coro, yield, 1);

		boost::optional<lua_Integer> got;
		auto consumer = Si::consume<lua_Integer>([&got](boost::optional<lua_Integer> element)
		{
			BOOST_REQUIRE(element);
			got = element;
		});

		{
			yielded.async_get_one(consumer);
			int rc = lua_resume(coro, 1);
			if (LUA_YIELD != rc)
			{
				throw std::runtime_error(lua_tostring(coro, -1));
			}
			BOOST_CHECK_EQUAL(boost::make_optional<lua_Integer>(4), got);
		}

		{
			yielded.async_get_one(consumer);
			int rc = lua_resume(coro, 1);
			if (LUA_YIELD != rc)
			{
				throw std::runtime_error(lua_tostring(coro, -1));
			}
			BOOST_CHECK_EQUAL(boost::make_optional<lua_Integer>(5), got);
		}
	}
}

namespace Si
{
	inline void swap_top(lua_State &lua)
	{
		lua_pushvalue(&lua, -2);
		lua_remove(&lua, -3);
	}

	struct pinned_value
	{
		pinned_value() BOOST_NOEXCEPT
		{
		}

		pinned_value(pinned_value &&other) BOOST_NOEXCEPT
			: lua(other.lua)
		{
			*this = std::move(other);
		}

		explicit pinned_value(lua_State &lua, int idx)
			: lua(&lua)
		{
			push_key(*this);
			lua_pushvalue(&lua, idx);
			lua_settable(&lua, LUA_REGISTRYINDEX);
		}

		~pinned_value() BOOST_NOEXCEPT
		{
			if (!lua)
			{
				return;
			}
			remove_this();
		}

		pinned_value &operator = (pinned_value &&other) BOOST_NOEXCEPT
		{
			if (lua)
			{
				remove_this();
			}
			else
			{
				lua = other.lua;
			}
			if (!other.lua)
			{
				lua = nullptr;
				return *this;
			}
			assert(lua == other.lua);

			push_key(other);
			lua_gettable(lua, LUA_REGISTRYINDEX);

			push_key(*this);
			swap_top(*lua);
			lua_settable(lua, LUA_REGISTRYINDEX);
			return *this;
		}

	private:

		lua_State *lua = nullptr;

		BOOST_DELETED_FUNCTION(pinned_value(pinned_value const &))
		BOOST_DELETED_FUNCTION(pinned_value &operator = (pinned_value const &))

		void remove_this()
		{
			push_key(*this);
			lua_pushnil(lua);
			lua_settable(lua, LUA_REGISTRYINDEX);
		}

		void push_key(pinned_value &pin)
		{
			lua_pushlightuserdata(lua, &pin);
		}
	};

	template <class Element, class ElementFromLua>
	struct lua_thread : public observable<Element>
	{
		using element_type = Element;

		explicit lua_thread(lua_State &thread, ElementFromLua from_lua)
			: s(std::make_shared<state>(thread, std::move(from_lua)))
		{
		}

		virtual void async_get_one(observer<element_type> &receiver) SILICIUM_OVERRIDE
		{
			s->receiver = &receiver;
			int rc;
			if (s->was_resumed)
			{
				rc = lua_resume(s->thread, 0);
			}
			else
			{
				s->was_resumed = true;
				void *bound_state = lua_newuserdata(s->thread, sizeof(weak_state));
				new (static_cast<weak_state *>(bound_state)) weak_state(s);

				//TODO error handling
				//TODO reuse the metatable
				lua_newtable(s->thread);
				lua_pushcfunction(s->thread, garbage_collect_weak_state);
				lua_setfield(s->thread, -2, "__gc");
				lua_setmetatable(s->thread, -2);

				lua_pushcclosure(s->thread, lua_thread::yield, 1);
				rc = lua_resume(s->thread, 1);
			}
			if (LUA_YIELD == rc)
			{
				return;
			}
			if (rc != 0)
			{
				throw std::logic_error("error handling not implemented yet");
			}
			if (s->receiver)
			{
				exchange(s->receiver, nullptr)->ended();
			}
		}

		virtual void cancel() SILICIUM_OVERRIDE
		{
			assert(s->receiver);
			s.reset();
		}

	private:

		struct state
		{
			lua_State *thread = nullptr;
			ElementFromLua from_lua;
			bool was_resumed = false;
			observer<element_type> *receiver = nullptr;
			pinned_value thread_pin;

			explicit state(lua_State &thread, ElementFromLua from_lua)
				: thread(&thread)
				, from_lua(std::move(from_lua))
			{
				lua_pushthread(&thread);
				thread_pin = pinned_value(thread, -1);
				lua_pop(&thread, 1);
			}
		};
		typedef std::weak_ptr<state> weak_state;

		std::shared_ptr<state> s;

		static int yield(lua_State *thread)
		{
			weak_state * const bound_state = static_cast<weak_state *>(lua_touserdata(thread, lua_upvalueindex(1)));
			std::shared_ptr<state> locked_state = bound_state->lock();
			if (!locked_state)
			{
				return 0;
			}
			assert(locked_state->receiver);
			exchange(locked_state->receiver, nullptr)->got_element(locked_state->from_lua(*thread, -1));
			return lua_yield(thread, 0);
		}

		static int garbage_collect_weak_state(lua_State *thread)
		{
			auto &ptr = *static_cast<weak_state *>(lua_touserdata(thread, 1));
			ptr.~weak_state();
			return 0;
		}
	};

	template <class Element, class ElementFromLua>
	auto make_lua_thread(lua_State &parent, ElementFromLua &&from_lua) -> lua_thread<Element, typename std::decay<ElementFromLua>::type>
	{
		lua_State * const coro = lua_newthread(&parent);
		lua_xmove(&parent, coro, 1);
		return lua_thread<Element, typename std::decay<ElementFromLua>::type>(parent, std::forward<ElementFromLua>(from_lua));
	}
}

namespace Si
{
	BOOST_AUTO_TEST_CASE(lua_thread_observable)
	{
		auto L = open_lua();
		BOOST_REQUIRE_EQUAL(0, luaL_loadstring(L.get(), "return function (yield) yield(4) yield(5) end"));
		if (0 != lua_pcall(L.get(), 0, 1, 0))
		{
			throw std::runtime_error(lua_tostring(L.get(), -1));
		}

		auto thread = Si::make_lua_thread<lua_Integer>(*L, [](lua_State &lua, int idx) -> lua_Integer { return lua_tointeger(&lua, idx); });
		std::vector<lua_Integer> generated;
		auto consumer = Si::consume<lua_Integer>([&generated](boost::optional<lua_Integer> element)
		{
			BOOST_REQUIRE(element);
			generated.emplace_back(*element);
		});
		thread.async_get_one(consumer);
		BOOST_REQUIRE_EQUAL(1U, generated.size());

		//make sure that the Lua thread is kept alive properly by trying to collect it before the next resume
		lua_gc(L.get(), LUA_GCCOLLECT, 0);

		thread.async_get_one(consumer);
		BOOST_REQUIRE_EQUAL(2U, generated.size());
		std::vector<lua_Integer> const expected{4, 5};
		BOOST_CHECK(expected == generated);
	}
}
<commit_msg>devirtualize lua_thread<commit_after>#include <silicium/bridge.hpp>
#include <silicium/consume.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/test/unit_test.hpp>
#include <lua.hpp>

namespace Si
{
	struct lua_deleter
	{
		void operator()(lua_State *L) const
		{
			lua_close(L);
		}
	};

	std::unique_ptr<lua_State, lua_deleter> open_lua()
	{
		auto L = std::unique_ptr<lua_State, lua_deleter>(luaL_newstate());
		if (!L)
		{
			throw std::bad_alloc();
		}
		return L;
	}

	typedef Si::observer<lua_Integer> yield_destination;

	static int yield(lua_State *L)
	{
		yield_destination &dest = *static_cast<yield_destination *>(lua_touserdata(L, lua_upvalueindex(1)));
		lua_Integer element = lua_tointeger(L, 1);
		dest.got_element(element);
		return lua_yield(L, 0);
	}

	BOOST_AUTO_TEST_CASE(lua)
	{
		auto L = open_lua();

		lua_State * const coro = lua_newthread(L.get());
		BOOST_REQUIRE_EQUAL(0, luaL_loadstring(coro, "return function (yield) yield(4) yield(5) end"));
		if (0 != lua_pcall(coro, 0, 1, 0))
		{
			throw std::runtime_error(lua_tostring(L.get(), -1));
		}

		Si::bridge<lua_Integer> yielded;
		lua_pushlightuserdata(coro, &static_cast<yield_destination &>(yielded));
		lua_pushcclosure(coro, yield, 1);

		boost::optional<lua_Integer> got;
		auto consumer = Si::consume<lua_Integer>([&got](boost::optional<lua_Integer> element)
		{
			BOOST_REQUIRE(element);
			got = element;
		});

		{
			yielded.async_get_one(consumer);
			int rc = lua_resume(coro, 1);
			if (LUA_YIELD != rc)
			{
				throw std::runtime_error(lua_tostring(coro, -1));
			}
			BOOST_CHECK_EQUAL(boost::make_optional<lua_Integer>(4), got);
		}

		{
			yielded.async_get_one(consumer);
			int rc = lua_resume(coro, 1);
			if (LUA_YIELD != rc)
			{
				throw std::runtime_error(lua_tostring(coro, -1));
			}
			BOOST_CHECK_EQUAL(boost::make_optional<lua_Integer>(5), got);
		}
	}
}

namespace Si
{
	inline void swap_top(lua_State &lua)
	{
		lua_pushvalue(&lua, -2);
		lua_remove(&lua, -3);
	}

	struct pinned_value
	{
		pinned_value() BOOST_NOEXCEPT
		{
		}

		pinned_value(pinned_value &&other) BOOST_NOEXCEPT
			: lua(other.lua)
		{
			*this = std::move(other);
		}

		explicit pinned_value(lua_State &lua, int idx)
			: lua(&lua)
		{
			push_key(*this);
			lua_pushvalue(&lua, idx);
			lua_settable(&lua, LUA_REGISTRYINDEX);
		}

		~pinned_value() BOOST_NOEXCEPT
		{
			if (!lua)
			{
				return;
			}
			remove_this();
		}

		pinned_value &operator = (pinned_value &&other) BOOST_NOEXCEPT
		{
			if (lua)
			{
				remove_this();
			}
			else
			{
				lua = other.lua;
			}
			if (!other.lua)
			{
				lua = nullptr;
				return *this;
			}
			assert(lua == other.lua);

			push_key(other);
			lua_gettable(lua, LUA_REGISTRYINDEX);

			push_key(*this);
			swap_top(*lua);
			lua_settable(lua, LUA_REGISTRYINDEX);
			return *this;
		}

	private:

		lua_State *lua = nullptr;

		BOOST_DELETED_FUNCTION(pinned_value(pinned_value const &))
		BOOST_DELETED_FUNCTION(pinned_value &operator = (pinned_value const &))

		void remove_this()
		{
			push_key(*this);
			lua_pushnil(lua);
			lua_settable(lua, LUA_REGISTRYINDEX);
		}

		void push_key(pinned_value &pin)
		{
			lua_pushlightuserdata(lua, &pin);
		}
	};

	template <class Element, class ElementFromLua>
	struct lua_thread
	{
		using element_type = Element;

		explicit lua_thread(lua_State &thread, ElementFromLua from_lua)
			: s(std::make_shared<state>(thread, std::move(from_lua)))
		{
		}

		void async_get_one(observer<element_type> &receiver)
		{
			s->receiver = &receiver;
			int rc;
			if (s->was_resumed)
			{
				rc = lua_resume(s->thread, 0);
			}
			else
			{
				s->was_resumed = true;
				void *bound_state = lua_newuserdata(s->thread, sizeof(weak_state));
				new (static_cast<weak_state *>(bound_state)) weak_state(s);

				//TODO error handling
				//TODO reuse the metatable
				lua_newtable(s->thread);
				lua_pushcfunction(s->thread, garbage_collect_weak_state);
				lua_setfield(s->thread, -2, "__gc");
				lua_setmetatable(s->thread, -2);

				lua_pushcclosure(s->thread, lua_thread::yield, 1);
				rc = lua_resume(s->thread, 1);
			}
			if (LUA_YIELD == rc)
			{
				return;
			}
			if (rc != 0)
			{
				throw std::logic_error("error handling not implemented yet");
			}
			if (s->receiver)
			{
				exchange(s->receiver, nullptr)->ended();
			}
		}

		void cancel()
		{
			assert(s->receiver);
			s.reset();
		}

	private:

		struct state
		{
			lua_State *thread = nullptr;
			ElementFromLua from_lua;
			bool was_resumed = false;
			observer<element_type> *receiver = nullptr;
			pinned_value thread_pin;

			explicit state(lua_State &thread, ElementFromLua from_lua)
				: thread(&thread)
				, from_lua(std::move(from_lua))
			{
				lua_pushthread(&thread);
				thread_pin = pinned_value(thread, -1);
				lua_pop(&thread, 1);
			}
		};
		typedef std::weak_ptr<state> weak_state;

		std::shared_ptr<state> s;

		static int yield(lua_State *thread)
		{
			weak_state * const bound_state = static_cast<weak_state *>(lua_touserdata(thread, lua_upvalueindex(1)));
			std::shared_ptr<state> locked_state = bound_state->lock();
			if (!locked_state)
			{
				return 0;
			}
			assert(locked_state->receiver);
			exchange(locked_state->receiver, nullptr)->got_element(locked_state->from_lua(*thread, -1));
			return lua_yield(thread, 0);
		}

		static int garbage_collect_weak_state(lua_State *thread)
		{
			auto &ptr = *static_cast<weak_state *>(lua_touserdata(thread, 1));
			ptr.~weak_state();
			return 0;
		}
	};

	template <class Element, class ElementFromLua>
	auto make_lua_thread(lua_State &parent, ElementFromLua &&from_lua) -> lua_thread<Element, typename std::decay<ElementFromLua>::type>
	{
		lua_State * const coro = lua_newthread(&parent);
		lua_xmove(&parent, coro, 1);
		return lua_thread<Element, typename std::decay<ElementFromLua>::type>(parent, std::forward<ElementFromLua>(from_lua));
	}
}

namespace Si
{
	BOOST_AUTO_TEST_CASE(lua_thread_observable)
	{
		auto L = open_lua();
		BOOST_REQUIRE_EQUAL(0, luaL_loadstring(L.get(), "return function (yield) yield(4) yield(5) end"));
		if (0 != lua_pcall(L.get(), 0, 1, 0))
		{
			throw std::runtime_error(lua_tostring(L.get(), -1));
		}

		auto thread = Si::make_lua_thread<lua_Integer>(*L, [](lua_State &lua, int idx) -> lua_Integer { return lua_tointeger(&lua, idx); });
		std::vector<lua_Integer> generated;
		auto consumer = Si::consume<lua_Integer>([&generated](boost::optional<lua_Integer> element)
		{
			BOOST_REQUIRE(element);
			generated.emplace_back(*element);
		});
		thread.async_get_one(consumer);
		BOOST_REQUIRE_EQUAL(1U, generated.size());

		//make sure that the Lua thread is kept alive properly by trying to collect it before the next resume
		lua_gc(L.get(), LUA_GCCOLLECT, 0);

		thread.async_get_one(consumer);
		BOOST_REQUIRE_EQUAL(2U, generated.size());
		std::vector<lua_Integer> const expected{4, 5};
		BOOST_CHECK(expected == generated);
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) Sjors Gielen, 2010
 * See LICENSE for license.
 */

#include "perl_callbacks.h"
#include "embedperl.h"
#include <assert.h>

#define MAX_EMBED_PERL 10
// #define DEBUG

// Totally thread-unsafe...
static EmbedPerl *ePerl[MAX_EMBED_PERL];
static int numEmbedPerl = 0;

extern "C" {
void xs_init(pTHX);

/***** CALLBACKS *****/

#define FOR_EVERY_EMBED(UNIQUEID,CALLBACK) \
  for( int i = 0; i < numEmbedPerl; ++i ) \
     if( strcmp(ePerl[i]->uniqueid(),UNIQUEID)==0 && ePerl[i]->CALLBACK )

void emoteEmbed(const char *uniqueid, const char *receiver, const char *message)
{
#ifdef DEBUG
  printf("***Callback*** Send emote to %s (on %s): %s\n", receiver, uniqueid, message);
#endif
  FOR_EVERY_EMBED(uniqueid,emoteCallback) {
    ePerl[i]->emoteCallback( uniqueid, receiver, message, ePerl[i]->data );
    return;
  }
  fprintf(stderr,"[CallbackError] No handler for Emote callback (uniqueid=%s)\n", uniqueid);
}

void privmsgEmbed(const char *uniqueid, const char *receiver, const char *message)
{
#ifdef DEBUG
  printf("***Callback*** Send privmsg to %s (on %s): %s\n", receiver, uniqueid, message);
#endif
  FOR_EVERY_EMBED(uniqueid,privmsgCallback) {
    ePerl[i]->privmsgCallback( uniqueid, receiver, message, ePerl[i]->data );
    return;
  }
  fprintf(stderr,"[CallbackError] No handler for Privmsg callback (uniqueid=%s)\n", uniqueid);
}

const char *getPropertyEmbed(const char *uniqueid, const char *variable)
{
  FOR_EVERY_EMBED(uniqueid,getPropertyCallback) {
    return ePerl[i]->getPropertyCallback( uniqueid, variable, ePerl[i]->data );
  }
}

void setPropertyEmbed(const char *uniqueid, const char *variable, const char *value)
{
  FOR_EVERY_EMBED(uniqueid,setPropertyCallback) {
    ePerl[i]->setPropertyCallback( uniqueid, variable, value, ePerl[i]->data );
    return;
  }
}

void unsetPropertyEmbed(const char *uniqueid, const char *variable)
{
  FOR_EVERY_EMBED(uniqueid,unsetPropertyCallback) {
    ePerl[i]->unsetPropertyCallback( uniqueid, variable, ePerl[i]->data );
    return;
  }
}

const char *getNickEmbed(const char *uniqueid)
{
  FOR_EVERY_EMBED(uniqueid,getNickCallback) {
    return ePerl[i]->getNickCallback( ePerl[i]->data );
  }
}

void sendWhoisEmbed(const char *uniqueid, const char *who)
{
  FOR_EVERY_EMBED(uniqueid,sendWhoisCallback) {
    ePerl[i]->sendWhoisCallback( who, ePerl[i]->data );
    return;
  }
}

void joinEmbed(const char *uniqueid, const char *channel)
{
  FOR_EVERY_EMBED(uniqueid,joinCallback) {
    ePerl[i]->joinCallback( channel, ePerl[i]->data );
    return;
  }
}

void partEmbed(const char *uniqueid, const char *channel)
{
  FOR_EVERY_EMBED(uniqueid,partCallback) {
    ePerl[i]->partCallback( channel, ePerl[i]->data );
    return;
  }
}

/***** END CALLBACKS ****/
}

EmbedPerl::EmbedPerl(const char *uniqueid)
: emoteCallback(0)
, privmsgCallback(0)
, getPropertyCallback(0)
, setPropertyCallback(0)
, unsetPropertyCallback(0)
, sendWhoisCallback(0)
, joinCallback(0)
, partCallback(0)
, getNickCallback(0)
{
  if( MAX_EMBED_PERL == numEmbedPerl )
  {
    fprintf(stderr, "Too many EmbedPerls. Raise MAX_EMBED_PERL\n");
    abort();
  }

  for( int i = 0; i < numEmbedPerl; ++i )
  {
    if( strcmp(ePerl[i]->uniqueid(), uniqueid) == 0 )
    {
      fprintf(stderr,"EmbedPerl created for a duplicate uniqueid '%s'.\n",
        uniqueid);
      abort();
    }
  }
  uniqueid_ = strdup(uniqueid);
  ePerl[numEmbedPerl++] = this;

  my_perl = perl_alloc();
  perl_construct(my_perl);

  char *argv[] = {"", "dazeus2_api_emulate.pl"};
  perl_parse(my_perl, xs_init, 1, argv, NULL);

  init();
}

EmbedPerl::~EmbedPerl()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl destructor\n");
#endif
  perl_destruct(my_perl);
  perl_free(my_perl);
}

const char *EmbedPerl::uniqueid() const
{
  return uniqueid_;
}

void EmbedPerl::setCallbacks( void (*emoteCallback)  (const char*, const char*, const char*, void*),
                              void (*privmsgCallback)(const char*, const char*, const char*, void*),
                              const char* (*getPropertyCallback)(const char*, const char*, void*),
                              void (*setPropertyCallback)(const char*, const char*, const char*, void*),
                              void (*unsetPropertyCallback)(const char*, const char*, void*),
                              void (*sendWhoisCallback)(const char*, void*),
                              void (*joinCallback)(const char*, void*),
                              void (*partCallback)(const char*, void*),
                              const char* (*getNickCallback)(void*),
                              void *data )
{
  this->emoteCallback = emoteCallback;
  this->privmsgCallback = privmsgCallback;
  this->getPropertyCallback = getPropertyCallback;
  this->setPropertyCallback = setPropertyCallback;
  this->unsetPropertyCallback = unsetPropertyCallback;
  this->sendWhoisCallback = sendWhoisCallback;
  this->joinCallback = joinCallback;
  this->partCallback = partCallback;
  this->getNickCallback = getNickCallback;
  this->data = data;
}

#define PREPARE_CALL \
  int argn = 0; \
  dSP; \
  ENTER; \
  SAVETMPS; \
  PUSHMARK(SP);

#define DO_CALL(NAME) \
  PUTBACK; \
  call_pv(NAME, G_SCALAR); \
  SPAGAIN;

#define END_CALL \
  PUTBACK; \
  FREETMPS; \
  LEAVE;

void EmbedPerl::init()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::init()\n");
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(uniqueid_, strlen(uniqueid_)));
  DO_CALL( "init" );
  END_CALL;
}

void EmbedPerl::message(const char *from, const char *to, const char *msg)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::message(%s,%s,%s)\n", from, to, msg);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(from, strlen(from)) );
  XPUSHs( newSVpv(to,   strlen(to))   );
  XPUSHs( newSVpv(msg,  strlen(msg))  );
  XPUSHs( newSVpv(msg,  strlen(msg))  );
  DO_CALL("message");
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, "... message(...)=%d\n", result);
#endif
}

void EmbedPerl::whois(char const *nick, int isIdentified)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::whois(%s,%d)\n", nick, isIdentified);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(nick, strlen(nick)) );
  XPUSHs( newSVpv(isIdentified == 1 ? "1" : "0", strlen("1")));
  DO_CALL( "whois" );
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, ".. whois(...)=%d\n", result);
#endif
}

bool EmbedPerl::loadModule(const char *module)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::loadModule(%s)", module);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(module, strlen(module)) );
  DO_CALL("loadModule");
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, "=%d\n", result);
#endif
  return result != 0;
}

void EmbedPerl::join(const char *channel, const char *who)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::join(%s, %s)\n", channel, who);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(channel, strlen(channel)) );
  XPUSHs( newSVpv(who,     strlen(who))     );
  DO_CALL("join");
  END_CALL;
}

void EmbedPerl::nick(const char *who, const char *new_nick)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::nick(%s, %s)\n", who, new_nick);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(who,      strlen(who))      );
  XPUSHs( newSVpv(new_nick, strlen(new_nick)) );
  DO_CALL("nick");
  END_CALL;
}

void EmbedPerl::connected()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::connected()\n");
#endif
  PREPARE_CALL;
  DO_CALL("connected");
  END_CALL;
}

/**
 * 'names' should be space separated!
 */
void EmbedPerl::namesReceived(const char *channel, const char *names)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::namesReceived(%s, %s)\n", channel, names);
#endif
  PREPARE_CALL;
  XPUSHs( newSVpv(channel, strlen(channel)) );
  XPUSHs( newSVpv(names,   strlen(names))   );
  DO_CALL("namesReceived");
  END_CALL;
}

/**
 * Call this method every second
 */
void EmbedPerl::tick()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::tick()\n");
#endif
  PREPARE_CALL;
  DO_CALL("tick");
  END_CALL;
}
<commit_msg>Change all function parameters into mortals<commit_after>/**
 * Copyright (c) Sjors Gielen, 2010
 * See LICENSE for license.
 */

#include "perl_callbacks.h"
#include "embedperl.h"
#include <assert.h>

#define MAX_EMBED_PERL 10
// #define DEBUG

// Totally thread-unsafe...
static EmbedPerl *ePerl[MAX_EMBED_PERL];
static int numEmbedPerl = 0;

extern "C" {
void xs_init(pTHX);

/***** CALLBACKS *****/

#define FOR_EVERY_EMBED(UNIQUEID,CALLBACK) \
  for( int i = 0; i < numEmbedPerl; ++i ) \
     if( strcmp(ePerl[i]->uniqueid(),UNIQUEID)==0 && ePerl[i]->CALLBACK )

void emoteEmbed(const char *uniqueid, const char *receiver, const char *message)
{
#ifdef DEBUG
  printf("***Callback*** Send emote to %s (on %s): %s\n", receiver, uniqueid, message);
#endif
  FOR_EVERY_EMBED(uniqueid,emoteCallback) {
    ePerl[i]->emoteCallback( uniqueid, receiver, message, ePerl[i]->data );
    return;
  }
  fprintf(stderr,"[CallbackError] No handler for Emote callback (uniqueid=%s)\n", uniqueid);
}

void privmsgEmbed(const char *uniqueid, const char *receiver, const char *message)
{
#ifdef DEBUG
  printf("***Callback*** Send privmsg to %s (on %s): %s\n", receiver, uniqueid, message);
#endif
  FOR_EVERY_EMBED(uniqueid,privmsgCallback) {
    ePerl[i]->privmsgCallback( uniqueid, receiver, message, ePerl[i]->data );
    return;
  }
  fprintf(stderr,"[CallbackError] No handler for Privmsg callback (uniqueid=%s)\n", uniqueid);
}

const char *getPropertyEmbed(const char *uniqueid, const char *variable)
{
  FOR_EVERY_EMBED(uniqueid,getPropertyCallback) {
    return ePerl[i]->getPropertyCallback( uniqueid, variable, ePerl[i]->data );
  }
}

void setPropertyEmbed(const char *uniqueid, const char *variable, const char *value)
{
  FOR_EVERY_EMBED(uniqueid,setPropertyCallback) {
    ePerl[i]->setPropertyCallback( uniqueid, variable, value, ePerl[i]->data );
    return;
  }
}

void unsetPropertyEmbed(const char *uniqueid, const char *variable)
{
  FOR_EVERY_EMBED(uniqueid,unsetPropertyCallback) {
    ePerl[i]->unsetPropertyCallback( uniqueid, variable, ePerl[i]->data );
    return;
  }
}

const char *getNickEmbed(const char *uniqueid)
{
  FOR_EVERY_EMBED(uniqueid,getNickCallback) {
    return ePerl[i]->getNickCallback( ePerl[i]->data );
  }
}

void sendWhoisEmbed(const char *uniqueid, const char *who)
{
  FOR_EVERY_EMBED(uniqueid,sendWhoisCallback) {
    ePerl[i]->sendWhoisCallback( who, ePerl[i]->data );
    return;
  }
}

void joinEmbed(const char *uniqueid, const char *channel)
{
  FOR_EVERY_EMBED(uniqueid,joinCallback) {
    ePerl[i]->joinCallback( channel, ePerl[i]->data );
    return;
  }
}

void partEmbed(const char *uniqueid, const char *channel)
{
  FOR_EVERY_EMBED(uniqueid,partCallback) {
    ePerl[i]->partCallback( channel, ePerl[i]->data );
    return;
  }
}

/***** END CALLBACKS ****/
}

EmbedPerl::EmbedPerl(const char *uniqueid)
: emoteCallback(0)
, privmsgCallback(0)
, getPropertyCallback(0)
, setPropertyCallback(0)
, unsetPropertyCallback(0)
, sendWhoisCallback(0)
, joinCallback(0)
, partCallback(0)
, getNickCallback(0)
{
  if( MAX_EMBED_PERL == numEmbedPerl )
  {
    fprintf(stderr, "Too many EmbedPerls. Raise MAX_EMBED_PERL\n");
    abort();
  }

  for( int i = 0; i < numEmbedPerl; ++i )
  {
    if( strcmp(ePerl[i]->uniqueid(), uniqueid) == 0 )
    {
      fprintf(stderr,"EmbedPerl created for a duplicate uniqueid '%s'.\n",
        uniqueid);
      abort();
    }
  }
  uniqueid_ = strdup(uniqueid);
  ePerl[numEmbedPerl++] = this;

  my_perl = perl_alloc();
  perl_construct(my_perl);

  char *argv[] = {"", "dazeus2_api_emulate.pl"};
  perl_parse(my_perl, xs_init, 1, argv, NULL);

  init();
}

EmbedPerl::~EmbedPerl()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl destructor\n");
#endif
  perl_destruct(my_perl);
  perl_free(my_perl);
}

const char *EmbedPerl::uniqueid() const
{
  return uniqueid_;
}

void EmbedPerl::setCallbacks( void (*emoteCallback)  (const char*, const char*, const char*, void*),
                              void (*privmsgCallback)(const char*, const char*, const char*, void*),
                              const char* (*getPropertyCallback)(const char*, const char*, void*),
                              void (*setPropertyCallback)(const char*, const char*, const char*, void*),
                              void (*unsetPropertyCallback)(const char*, const char*, void*),
                              void (*sendWhoisCallback)(const char*, void*),
                              void (*joinCallback)(const char*, void*),
                              void (*partCallback)(const char*, void*),
                              const char* (*getNickCallback)(void*),
                              void *data )
{
  this->emoteCallback = emoteCallback;
  this->privmsgCallback = privmsgCallback;
  this->getPropertyCallback = getPropertyCallback;
  this->setPropertyCallback = setPropertyCallback;
  this->unsetPropertyCallback = unsetPropertyCallback;
  this->sendWhoisCallback = sendWhoisCallback;
  this->joinCallback = joinCallback;
  this->partCallback = partCallback;
  this->getNickCallback = getNickCallback;
  this->data = data;
}

#define PREPARE_CALL \
  int argn = 0; \
  dSP; \
  ENTER; \
  SAVETMPS; \
  PUSHMARK(SP);

#define DO_CALL(NAME) \
  PUTBACK; \
  call_pv(NAME, G_SCALAR); \
  SPAGAIN;

#define END_CALL \
  PUTBACK; \
  FREETMPS; \
  LEAVE;

void EmbedPerl::init()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::init()\n");
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(uniqueid_, strlen(uniqueid_))));
  DO_CALL( "init" );
  END_CALL;
}

void EmbedPerl::message(const char *from, const char *to, const char *msg)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::message(%s,%s,%s)\n", from, to, msg);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(from, strlen(from)) ));
  XPUSHs(sv_2mortal(newSVpv(to,   strlen(to))   ));
  XPUSHs(sv_2mortal(newSVpv(msg,  strlen(msg))  ));
  XPUSHs(sv_2mortal(newSVpv(msg,  strlen(msg))  ));
  DO_CALL("message");
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, "... message(...)=%d\n", result);
#endif
}

void EmbedPerl::whois(char const *nick, int isIdentified)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::whois(%s,%d)\n", nick, isIdentified);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(nick, strlen(nick)) ));
  XPUSHs(sv_2mortal(newSVpv(isIdentified == 1 ? "1" : "0", strlen("1"))));
  DO_CALL( "whois" );
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, ".. whois(...)=%d\n", result);
#endif
}

bool EmbedPerl::loadModule(const char *module)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::loadModule(%s)", module);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(module, strlen(module)) ));
  DO_CALL("loadModule");
  int result = POPi;
  END_CALL;
#ifdef DEBUG
  fprintf(stderr, "=%d\n", result);
#endif
  return result != 0;
}

void EmbedPerl::join(const char *channel, const char *who)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::join(%s, %s)\n", channel, who);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(channel, strlen(channel)) ));
  XPUSHs(sv_2mortal(newSVpv(who,     strlen(who))     ));
  DO_CALL("join");
  END_CALL;
}

void EmbedPerl::nick(const char *who, const char *new_nick)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::nick(%s, %s)\n", who, new_nick);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(who,      strlen(who))      ));
  XPUSHs(sv_2mortal(newSVpv(new_nick, strlen(new_nick)) ));
  DO_CALL("nick");
  END_CALL;
}

void EmbedPerl::connected()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::connected()\n");
#endif
  PREPARE_CALL;
  DO_CALL("connected");
  END_CALL;
}

/**
 * 'names' should be space separated!
 */
void EmbedPerl::namesReceived(const char *channel, const char *names)
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::namesReceived(%s, %s)\n", channel, names);
#endif
  PREPARE_CALL;
  XPUSHs(sv_2mortal(newSVpv(channel, strlen(channel)) ));
  XPUSHs(sv_2mortal(newSVpv(names,   strlen(names))   ));
  DO_CALL("namesReceived");
  END_CALL;
}

/**
 * Call this method every second
 */
void EmbedPerl::tick()
{
#ifdef DEBUG
  fprintf(stderr, "EmbedPerl::tick()\n");
#endif
  PREPARE_CALL;
  DO_CALL("tick");
  END_CALL;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under 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)
//=======================================================================

#include <system.hpp>
#include <graphics.hpp>
#include <print.hpp>
#include <malloc.hpp>

namespace {

uint32_t* z_buffer = nullptr;

uint64_t width = 0;
uint64_t height = 0;
uint64_t x_shift = 0;
uint64_t y_shift = 0;
uint64_t bytes_per_scan_line = 0;
uint64_t red_shift = 0;
uint64_t green_shift = 0;
uint64_t blue_shift = 0;

uint32_t make_color(uint8_t r, uint8_t g, uint8_t b){
    return (r << red_shift) + (g << green_shift) + (b << blue_shift);
}

void fill_buffer(uint32_t color){
    auto where = 0;
    for(size_t j = 0; j < height; ++j){
        for(size_t i = 0; i < width; ++i){
            z_buffer[where + i] = color;
        }

        where += y_shift;
    }
}

void draw_rect(size_t x, size_t y, size_t w, size_t h, uint32_t color){
    auto where = x + y * y_shift;

    for(size_t j = 0; j < h; ++j){
        for(size_t i = 0; i < w; ++i){
            z_buffer[where + i] = color;
        }

        where += y_shift;
    }
}

void paint_cursor(){
    auto x = graphics::mouse_x();
    auto y = graphics::mouse_y();

    auto color = make_color(255, 0, 0);

    draw_rect(x, y, 5, 5, color);
}

} // end of anonnymous namespace

int main(int /*argc*/, char* /*argv*/[]){
    width = graphics::get_width();
    height = graphics::get_height();
    x_shift = graphics::get_x_shift();
    y_shift = graphics::get_y_shift();
    bytes_per_scan_line = graphics::get_bytes_per_scan_line();
    red_shift = graphics::get_red_shift();
    green_shift = graphics::get_green_shift();
    blue_shift = graphics::get_blue_shift();

    size_t total_size = height * bytes_per_scan_line;

    printf("total_size: %u\n",total_size);

    auto buffer = new char[total_size];

    z_buffer = reinterpret_cast<uint32_t*>(buffer);

    auto white = make_color(255, 255, 255);

    while(true){
        fill_buffer(white);

        paint_cursor();

        graphics::redraw(buffer);
        sleep_ms(10);
    }

    delete[] buffer;

    exit(0);
}
<commit_msg>Sleep a bit less<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under 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)
//=======================================================================

#include <system.hpp>
#include <graphics.hpp>
#include <print.hpp>
#include <malloc.hpp>

namespace {

uint32_t* z_buffer = nullptr;

uint64_t width = 0;
uint64_t height = 0;
uint64_t x_shift = 0;
uint64_t y_shift = 0;
uint64_t bytes_per_scan_line = 0;
uint64_t red_shift = 0;
uint64_t green_shift = 0;
uint64_t blue_shift = 0;

uint32_t make_color(uint8_t r, uint8_t g, uint8_t b){
    return (r << red_shift) + (g << green_shift) + (b << blue_shift);
}

void fill_buffer(uint32_t color){
    auto where = 0;
    for(size_t j = 0; j < height; ++j){
        for(size_t i = 0; i < width; ++i){
            z_buffer[where + i] = color;
        }

        where += y_shift;
    }
}

void draw_rect(size_t x, size_t y, size_t w, size_t h, uint32_t color){
    auto where = x + y * y_shift;

    for(size_t j = 0; j < h; ++j){
        for(size_t i = 0; i < w; ++i){
            z_buffer[where + i] = color;
        }

        where += y_shift;
    }
}

void paint_cursor(){
    auto x = graphics::mouse_x();
    auto y = graphics::mouse_y();

    auto color = make_color(255, 0, 0);

    draw_rect(x, y, 5, 5, color);
}

} // end of anonnymous namespace

int main(int /*argc*/, char* /*argv*/[]){
    width = graphics::get_width();
    height = graphics::get_height();
    x_shift = graphics::get_x_shift();
    y_shift = graphics::get_y_shift();
    bytes_per_scan_line = graphics::get_bytes_per_scan_line();
    red_shift = graphics::get_red_shift();
    green_shift = graphics::get_green_shift();
    blue_shift = graphics::get_blue_shift();

    size_t total_size = height * bytes_per_scan_line;

    printf("total_size: %u\n",total_size);

    auto buffer = new char[total_size];

    z_buffer = reinterpret_cast<uint32_t*>(buffer);

    auto white = make_color(255, 255, 255);

    while(true){
        fill_buffer(white);

        paint_cursor();

        graphics::redraw(buffer);
        sleep_ms(5);
    }

    delete[] buffer;

    exit(0);
}
<|endoftext|>


<commit_before>/**
 * DP
 */
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if(matrix.empty()) return 0;
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> f(m, vector<int>(n));
        for (int i = 0; i < n; ++i) f[0][i] = matrix[0][i] == '1';
        for (int i = 1; i < m; ++i) f[i][0] = matrix[i][0] == '1';
        for (int i = 1; i < m; ++i)
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == '0') continue;
                f[i][j] = 1 + min(f[i-1][j], min(f[i-1][j-1], f[i][j-1]));
            }
        int ans = 0;
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) ans = max(ans, f[i][j]);
        return ans*ans;
    }
};<commit_msg>Update 221.cpp<commit_after>/**
 * DP
 *
 */
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if(matrix.empty()) return 0;
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> f(m, vector<int>(n));
        for (int i = 0; i < n; ++i) f[0][i] = matrix[0][i] == '1';
        for (int i = 1; i < m; ++i) f[i][0] = matrix[i][0] == '1';
        for (int i = 1; i < m; ++i)
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == '0') continue;
                f[i][j] = 1 + min(f[i-1][j], min(f[i-1][j-1], f[i][j-1]));
            }
        int ans = 0;
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) ans = max(ans, f[i][j]);
        return ans*ans;
    }
};

// Conclusion:
//

<|endoftext|>


<commit_before>/*
 * String, Design
 *
 */
class Codec {
public:

    // Encodes a list of strings to a single string.
    string encode(vector<string>& strs) {
        string encoded = "";
        for (const string& s: strs) {
            int len = s.size();
            encoded += to_string(len) + "/" + s;
        }
        return encoded;
    }

    // Decodes a single string to a list of strings.
    vector<string> decode(string s) {
        vector<string> decoded;
        int pos = 0;
        while (pos < s.size()) {
            int slashPos = s.find_first_of('/', pos);
            int len = stoi(s.substr(pos, slashPos - pos));
            pos = slashPos + 1;
            decoded.push_back(s.substr(pos, len));
            pos += len;
        }
        return decoded;
    }
};

// Conlcusion:

<commit_msg>Update 271.cpp<commit_after>/*
 * String, Design
 *
 */

// version 1:
class Codec {
public:

    // Encodes a list of strings to a single string.
    string encode(vector<string>& strs) {
        string encoded;
        for(const auto &s : strs) {
            int len = s.size();
            encoded += to_string(len) + '/' + s;
        }
        return encoded;
    }

    // Decodes a single string to a list of strings.
    vector<string> decode(string s) {
        vector<string> decoded;
        int i = 0, n = s.size();
        while(i < n) {
            int start = i;
            while(i < n && s[i] != '/') i++; // find the position of '/'
            int len = stoi(s.substr(start, i-start));
            start = ++i;
            decoded.push_back(s.substr(start, len));
            i += len;
        }
        return decoded;
    }
};

// version 2:
class Codec {
public:

    // Encodes a list of strings to a single string.
    string encode(vector<string>& strs) {
        string encoded = "";
        for (const string& s: strs) {
            int len = s.size();
            encoded += to_string(len) + "/" + s;
        }
        return encoded;
    }

    // Decodes a single string to a list of strings.
    vector<string> decode(string s) {
        vector<string> decoded;
        int pos = 0;
        while (pos < s.size()) {
            int slashPos = s.find_first_of('/', pos);
            int len = stoi(s.substr(pos, slashPos - pos));
            pos = slashPos + 1;
            decoded.push_back(s.substr(pos, len));
            pos += len;
        }
        return decoded;
    }
};

// Conlcusion:

<|endoftext|>


<commit_before>#include "stdafx.h"

App app;

std::string g_type = "mesh";

static float CalcRadius(const Mesh* m)
{
	const Block& b = m->GetRawDatas();
	float maxSq = 0;
	for (auto& it : b.vertices) {
		float sq = lengthSq(it.xyz);
		maxSq = std::max(maxSq, sq);
	}
	return std::sqrt(maxSq);
}

App::App()
{
	meshId = MeshMan::INVALID_MMID;
}

void App::Draw()
{
	AF_PROFILE_RANGE(AppDraw);

	IVec2 scrSize = systemMisc.GetScreenSize();
	float f = 1000;
	float n = 1;
	float aspect = (float)scrSize.x / scrSize.y;
	Mat proj = perspectiveLH(45.0f * (float)M_PI / 180.0f, aspect, n, f);
	matrixMan.Set(MatrixMan::PROJ, proj);
	/*
	MeshXAnimResult r;
	MeshX* mesh = (MeshX*)meshMan.Get(meshId);
	if (mesh) {
		double now = GetTime();
		mesh->CalcAnimation(0, now, r);
		float wrappedTime = float(now - floor(now));
		auto normToRad = [](float n) { return n * float(M_PI * 2); };
		mesh->Draw(r, translate(0, radius * 1.5f, 0) * q2m(Quat(Vec3(0, 0, 1.0f), normToRad(wrappedTime))));
		mesh->Draw(r, translate(radius * 2.0f, 0, 0) * q2m(Quat(Vec3(0, 1.0f, 0), normToRad(wrappedTime))));
	}
	*/
	AFCommandList& cmd = afGetCommandList();

	appRenderTarget.BeginRenderToThis();
	moduleManager.Draw3DAll(cmd, appRenderTarget);
	luaMan.Draw3D();
	meshRenderer.Flush();
	skyMan.Draw(cmd);
	luaMan.Draw2D(cmd);
	moduleManager.Draw2DAll(cmd, appRenderTarget);
	appRenderTarget.EndRenderToThis();

	afBeginRenderToSwapChain();
	cmd.SetRenderStates(copyPSO);
	stockObjects.ApplyFullScreenVertexBuffer(cmd);
	cmd.SetTexture(appRenderTarget.GetTexture(), 0);
	cmd.Draw(4);
	fontMan.Draw(cmd, systemMisc.GetScreenSize());
	afEndRenderToSwapChain();
}

static const SamplerType samplers[] =
{
	AFST_POINT_WRAP,
};

void App::Create()
{
#ifdef _MSC_VER
	GoMyDir();
//	SetCurrentDirectoryA("D:\\bitbucket\\Janken\\Program\\pack\\assets");
#endif
#ifdef GL_TRUE
	glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
	glClearDepthf(0);	// for left-handed coordinate
#endif
	meshRenderer.Create();
	fontMan.Create();
	spriteRenderer.Create();
	stockObjects.Create();
	luaMan.Create();
	appRenderTarget.Init(systemMisc.GetScreenSize(), AFF_R8G8B8A8_UNORM, AFF_AUTO_DEPTH_STENCIL);
#if defined(AF_GLES) || defined(AF_VULKAN)
	int numElements = 0;
	const InputElement* elements = stockObjects.GetFullScreenInputElements(numElements);
	copyPSO.Create("glow_copy", numElements, elements, 0, arrayparam(samplers));
#else
	copyPSO.Create("copy_rgba", 0, nullptr, 0, 0, nullptr);
#endif
}

void App::LoadMesh(const char* fileName)
{
	meshId = meshMan.Create(fileName);
	MeshX* mesh = (MeshX*)meshMan.Get(meshId);
	if (mesh) {
		float radius = CalcRadius(mesh);
		float scale = std::max(0.00001f, radius);
		devCamera.SetDistance(scale * 3);
	}
	g_type = "mesh";
}

void App::Destroy()
{
#if defined(AF_DX12) || defined(AF_VULKAN)
	deviceMan.Flush();
#endif
	luaMan.Destroy();
	stockObjects.Destroy();
	spriteRenderer.Destroy();
	texMan.Destroy();
	fontMan.Destroy();
	meshRenderer.Destroy();
	meshMan.Destroy();
	skyMan.Destroy();
	glow.Destroy();
	letterBox.Destroy();
	appRenderTarget.Destroy();
	copyPSO.Destroy();
	ClearMenu();
	meshId = MeshMan::INVALID_MMID;
}

void App::Update()
{
	{
		AF_PROFILE_RANGE(Update);
		systemMisc.lastUpdateTime = GetTime();
		inputMan.Update();
		matrixMan.Set(MatrixMan::VIEW, devCamera.CalcViewMatrix());
		moduleManager.UpdateAll();
		luaMan.Update();
		fps.Update();
	}
	fontMan.DrawString(Vec2(20, 40), 20, SPrintf("FPS: %f", fps.Get()), 0xffffffff);
	afProfiler.Print();
	Draw();
}
<commit_msg>[DX12] Draw detailed profiling to find the bottleneck.<commit_after>#include "stdafx.h"

App app;

std::string g_type = "mesh";

static float CalcRadius(const Mesh* m)
{
	const Block& b = m->GetRawDatas();
	float maxSq = 0;
	for (auto& it : b.vertices) {
		float sq = lengthSq(it.xyz);
		maxSq = std::max(maxSq, sq);
	}
	return std::sqrt(maxSq);
}

App::App()
{
	meshId = MeshMan::INVALID_MMID;
}

void App::Draw()
{

	IVec2 scrSize = systemMisc.GetScreenSize();
	float f = 1000;
	float n = 1;
	float aspect = (float)scrSize.x / scrSize.y;
	Mat proj = perspectiveLH(45.0f * (float)M_PI / 180.0f, aspect, n, f);
	matrixMan.Set(MatrixMan::PROJ, proj);
	/*
	MeshXAnimResult r;
	MeshX* mesh = (MeshX*)meshMan.Get(meshId);
	if (mesh) {
		double now = GetTime();
		mesh->CalcAnimation(0, now, r);
		float wrappedTime = float(now - floor(now));
		auto normToRad = [](float n) { return n * float(M_PI * 2); };
		mesh->Draw(r, translate(0, radius * 1.5f, 0) * q2m(Quat(Vec3(0, 0, 1.0f), normToRad(wrappedTime))));
		mesh->Draw(r, translate(radius * 2.0f, 0, 0) * q2m(Quat(Vec3(0, 1.0f, 0), normToRad(wrappedTime))));
	}
	*/
	AFCommandList& cmd = afGetCommandList();

	{
		AF_PROFILE_RANGE(AppDrawOffscreen);
		appRenderTarget.BeginRenderToThis();
		moduleManager.Draw3DAll(cmd, appRenderTarget);
		luaMan.Draw3D();
		meshRenderer.Flush();
		skyMan.Draw(cmd);
		luaMan.Draw2D(cmd);
		moduleManager.Draw2DAll(cmd, appRenderTarget);
		appRenderTarget.EndRenderToThis();
	}

	{
		AF_PROFILE_RANGE(AppDrawBeginSwapchain);
		afBeginRenderToSwapChain();
	}
	{
		AF_PROFILE_RANGE(AppDrawDrawSwapchain);
		cmd.SetRenderStates(copyPSO);
		stockObjects.ApplyFullScreenVertexBuffer(cmd);
		cmd.SetTexture(appRenderTarget.GetTexture(), 0);
		cmd.Draw(4);
	}
	{
		AF_PROFILE_RANGE(AppDrawDrawFontToSwapchain);
		fontMan.Draw(cmd, systemMisc.GetScreenSize());
	}
	{
		AF_PROFILE_RANGE(AppDrawEndSwapchain);
		afEndRenderToSwapChain();
	}
}

static const SamplerType samplers[] =
{
	AFST_POINT_WRAP,
};

void App::Create()
{
#ifdef _MSC_VER
	GoMyDir();
//	SetCurrentDirectoryA("D:\\bitbucket\\Janken\\Program\\pack\\assets");
#endif
#ifdef GL_TRUE
	glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
	glClearDepthf(0);	// for left-handed coordinate
#endif
	meshRenderer.Create();
	fontMan.Create();
	spriteRenderer.Create();
	stockObjects.Create();
	luaMan.Create();
	appRenderTarget.Init(systemMisc.GetScreenSize(), AFF_R8G8B8A8_UNORM, AFF_AUTO_DEPTH_STENCIL);
#if defined(AF_GLES) || defined(AF_VULKAN)
	int numElements = 0;
	const InputElement* elements = stockObjects.GetFullScreenInputElements(numElements);
	copyPSO.Create("glow_copy", numElements, elements, 0, arrayparam(samplers));
#else
	copyPSO.Create("copy_rgba", 0, nullptr, 0, 0, nullptr);
#endif
}

void App::LoadMesh(const char* fileName)
{
	meshId = meshMan.Create(fileName);
	MeshX* mesh = (MeshX*)meshMan.Get(meshId);
	if (mesh) {
		float radius = CalcRadius(mesh);
		float scale = std::max(0.00001f, radius);
		devCamera.SetDistance(scale * 3);
	}
	g_type = "mesh";
}

void App::Destroy()
{
#if defined(AF_DX12) || defined(AF_VULKAN)
	deviceMan.Flush();
#endif
	luaMan.Destroy();
	stockObjects.Destroy();
	spriteRenderer.Destroy();
	texMan.Destroy();
	fontMan.Destroy();
	meshRenderer.Destroy();
	meshMan.Destroy();
	skyMan.Destroy();
	glow.Destroy();
	letterBox.Destroy();
	appRenderTarget.Destroy();
	copyPSO.Destroy();
	ClearMenu();
	meshId = MeshMan::INVALID_MMID;
}

void App::Update()
{
	{
		AF_PROFILE_RANGE(Update);
		systemMisc.lastUpdateTime = GetTime();
		inputMan.Update();
		matrixMan.Set(MatrixMan::VIEW, devCamera.CalcViewMatrix());
		moduleManager.UpdateAll();
		luaMan.Update();
		fps.Update();
	}
	fontMan.DrawString(Vec2(20, 40), 20, SPrintf("FPS: %f", fps.Get()), 0xffffffff);
	afProfiler.Print();
	Draw();
}
<|endoftext|>


<commit_before>#include "extconfig.h"
#include <utility>
#include <Eigen/Cholesky>
#include <cmath>

/*
 * Member function definitions of TSEncoder template class.
 * See tsencoder.h for template class declaration.
 */

template <size_t M, size_t N, size_t O>
TSEncoder<M, N, O>::TSEncoder(const TSEncoderConfig& config) :
    m_events(),
    m_event_index(0),
    m_skip_order_counter(0),
    m_A(decltype(m_A)::Ones()), /* elements in last column of time stamp matrix are always 1 */
    m_P(decltype(m_P)::Zero()),
    m_B(decltype(m_B)::Zero()),
    m_T(decltype(m_T)::Ones()), /* last element of time vector is always 1 */
    m_t0(0),
    m_alpha(0.0f),
    m_config(config),
    m_count(0),
    m_state(state_t::STOP),
    m_index(index_t::NONE) { }

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::start() {
    osalSysLock();
    osalDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
            "invalid state");
    if (extp->state == EXT_STOP) {
        extStartI(extp);
    }
    extChannelEnableSetModeI(extp, m_config.a, EXT_CH_MODE_BOTH_EDGES, ab_callback, this);
    extChannelEnableSetModeI(extp, m_config.b, EXT_CH_MODE_BOTH_EDGES, ab_callback, this);
    if (m_config.z == PAL_NOLINE) {
        m_index = index_t::NONE;
    } else {
        extChannelEnableSetModeI(extp, m_config.z, EXT_CH_MODE_RISING_EDGE, index_callback, this);
        m_index = index_t::NOTFOUND;
    }
    m_state = state_t::READY;
    osalSysUnlock();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::stop() {
    osalSysLock();
    m_state = state_t::STOP;
    m_index = index_t::NONE;

    extChannelDisableClearModeI(extp, PAL_PAD(m_config.a));
    extChannelDisableClearModeI(extp, PAL_PAD(m_config.b));
    if (m_config.z != PAL_NOLINE) {
        extChannelDisableClearModeI(extp, PAL_PAD(m_config.z));
    }
    extStopIfChannelsDisabledI(extp);
    osalSysUnlock();
}

template <size_t M, size_t N, size_t O>
typename TSEncoder<M, N, O>::state_t TSEncoder<M, N, O>::state() const {
    return m_state;
}

template <size_t M, size_t N, size_t O>
typename TSEncoder<M, N, O>::index_t TSEncoder<M, N, O>::index() const volatile {
    return m_index;
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::update_polynomial_fit() {
    /*
     * Redefine oldest event to be at time zero. Scale time so that difference
     * in time from oldest to newest event is equal to 1.
     *
     * If the difference A(N - 1, 1) - A(0, 1) is too large, the value will be
     * nonsensical due to uint rolling back to zero. The difference must also
     * be less than int32_t_max as these values will be cast from uint32_t to
     * int32_t.
     */
    m_t0 = m_events[(m_event_index + 1) % N].first;
    m_alpha = 1.0f / (m_events[m_event_index].first - m_t0);
    for (unsigned int i = 0; i < N; ++i) {
        m_A(i, M - 1) = m_alpha * (m_events[(m_event_index + i + 1) % N].first - m_t0);
        m_B(i) = m_events[(m_event_index + i + 1) % N].second;
    }
    for (unsigned int i = 0; i < M - 1; ++i) {
        m_A.col(M - 2 - i) = m_A.col(M - 1 - i).cwiseProduct(m_A.col(M - 1));
    }
    m_P.noalias() = (m_A.transpose() * m_A).ldlt().solve(
            (m_A.transpose() * m_B.template cast<polycoeff_t>()));
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::update_estimate_time(rtcnt_t tc) {
    polycoeff_t tcf = m_alpha * static_cast<polycoeff_t>(tc - m_t0);
    m_T(M - 1) = tcf;
    for (unsigned int i = 0; i < M - 1; ++i) {
        m_T(M - 2 - i) = m_T(M - 1 - i) * tcf;
    }
}

template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::position() const {
    polycoeff_t x = m_P.transpose() * m_T;
    polycoeff_t count = static_cast<polycoeff_t>(m_count);
    if (std::abs(x - count) > 1.0f) {
        return count;
    }
    return x;
}

// TODO: Validity of velocity and acceleration for large error in estimated
// position (i.e. position is adjusted due to a large amount of time without
// an encoder event).
template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::velocity() const {
    Eigen::Matrix<tsenccnt_t, M, 1> velocity_coefficients;
    for (int i = 0; i < M; ++i) {
        velocity_coefficients(i) = M - i;
    }
    return velocity_coefficients.cwiseProduct(m_P.template top<M>()).transpose() *
        m_T.template bottom<M>();
}

template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::acceleration() const {
    Eigen::Matrix<tsenccnt_t, M - 1 , 1> acceleration_coefficients;
    for (int i = 0; i < M - 1; ++i) {
        acceleration_coefficients(i) = (M - i) * (M - i - 1);
    }
    return acceleration_coefficients.cwiseProduct(m_P.template top<M - 1>()).transpose() *
        m_T.template bottom<M - 1>();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::ab_callback(EXTDriver* extp, expchannel_t channel) {
    (void)extp;
    osalSysLockFromISR();
    TSEncoder<M, N, O>* enc = reinterpret_cast<TSEncoder<M, N, O>*>(extGetChannelCallbackObject(channel));
    if (((channel == PAL_PAD(enc->m_config.a)) &&
                (palReadLine(enc->m_config.a) != palReadLine(enc->m_config.b))) ||
        ((channel == PAL_PAD(enc->m_config.b)) &&
                (palReadLine(enc->m_config.a) == palReadLine(enc->m_config.b)))) {
        ++enc->m_count;
    } else {
        --enc->m_count;
    }
    enc->m_events[enc->m_event_index] = std::make_pair(chSysGetRealtimeCounterX(), enc->m_count);
    enc->m_skip_order_counter = (enc->m_skip_order_counter + 1) % O;
    if (enc->m_skip_order_counter == 0) {
        enc->m_event_index = (enc->m_event_index + 1) % N;
    }
    osalSysUnlockFromISR();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::index_callback(EXTDriver* extp, expchannel_t channel) {
    osalSysLockFromISR();
    TSEncoder<M, N, O>* enc = reinterpret_cast<TSEncoder<M, N, O>*>(extGetChannelCallbackObject(channel));
    enc->m_count = 0;
    enc->m_index = index_t::FOUND;
    extChannelDisableClearModeI(extp, PAL_PAD(enc->m_config.z));
    osalSysUnlockFromISR();
}
<commit_msg>Fix velocity and acceleration functions in TSEncoder<commit_after>#include "extconfig.h"
#include <utility>
#include <Eigen/Cholesky>
#include <cmath>

/*
 * Member function definitions of TSEncoder template class.
 * See tsencoder.h for template class declaration.
 */

template <size_t M, size_t N, size_t O>
TSEncoder<M, N, O>::TSEncoder(const TSEncoderConfig& config) :
    m_events(),
    m_event_index(0),
    m_skip_order_counter(0),
    m_A(decltype(m_A)::Ones()), /* elements in last column of time stamp matrix are always 1 */
    m_P(decltype(m_P)::Zero()),
    m_B(decltype(m_B)::Zero()),
    m_T(decltype(m_T)::Ones()), /* last element of time vector is always 1 */
    m_t0(0),
    m_alpha(0.0f),
    m_config(config),
    m_count(0),
    m_state(state_t::STOP),
    m_index(index_t::NONE) { }

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::start() {
    osalSysLock();
    osalDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
            "invalid state");
    if (extp->state == EXT_STOP) {
        extStartI(extp);
    }
    extChannelEnableSetModeI(extp, m_config.a, EXT_CH_MODE_BOTH_EDGES, ab_callback, this);
    extChannelEnableSetModeI(extp, m_config.b, EXT_CH_MODE_BOTH_EDGES, ab_callback, this);
    if (m_config.z == PAL_NOLINE) {
        m_index = index_t::NONE;
    } else {
        extChannelEnableSetModeI(extp, m_config.z, EXT_CH_MODE_RISING_EDGE, index_callback, this);
        m_index = index_t::NOTFOUND;
    }
    m_state = state_t::READY;
    osalSysUnlock();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::stop() {
    osalSysLock();
    m_state = state_t::STOP;
    m_index = index_t::NONE;

    extChannelDisableClearModeI(extp, PAL_PAD(m_config.a));
    extChannelDisableClearModeI(extp, PAL_PAD(m_config.b));
    if (m_config.z != PAL_NOLINE) {
        extChannelDisableClearModeI(extp, PAL_PAD(m_config.z));
    }
    extStopIfChannelsDisabledI(extp);
    osalSysUnlock();
}

template <size_t M, size_t N, size_t O>
typename TSEncoder<M, N, O>::state_t TSEncoder<M, N, O>::state() const {
    return m_state;
}

template <size_t M, size_t N, size_t O>
typename TSEncoder<M, N, O>::index_t TSEncoder<M, N, O>::index() const volatile {
    return m_index;
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::update_polynomial_fit() {
    /*
     * Redefine oldest event to be at time zero. Scale time so that difference
     * in time from oldest to newest event is equal to 1.
     *
     * If the difference A(N - 1, 1) - A(0, 1) is too large, the value will be
     * nonsensical due to uint rolling back to zero. The difference must also
     * be less than int32_t_max as these values will be cast from uint32_t to
     * int32_t.
     */
    m_t0 = m_events[(m_event_index + 1) % N].first;
    m_alpha = 1.0f / (m_events[m_event_index].first - m_t0);
    for (unsigned int i = 0; i < N; ++i) {
        m_A(i, M - 1) = m_alpha * (m_events[(m_event_index + i + 1) % N].first - m_t0);
        m_B(i) = m_events[(m_event_index + i + 1) % N].second;
    }
    for (unsigned int i = 0; i < M - 1; ++i) {
        m_A.col(M - 2 - i) = m_A.col(M - 1 - i).cwiseProduct(m_A.col(M - 1));
    }
    m_P.noalias() = (m_A.transpose() * m_A).ldlt().solve(
            (m_A.transpose() * m_B.template cast<polycoeff_t>()));
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::update_estimate_time(rtcnt_t tc) {
    polycoeff_t tcf = m_alpha * static_cast<polycoeff_t>(tc - m_t0);
    m_T(M - 1) = tcf;
    for (unsigned int i = 0; i < M - 1; ++i) {
        m_T(M - 2 - i) = m_T(M - 1 - i) * tcf;
    }
}

template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::position() const {
    polycoeff_t x = m_P.transpose() * m_T;
    polycoeff_t count = static_cast<polycoeff_t>(m_count);
    if (std::abs(x - count) > 1.0f) {
        return count;
    }
    return x;
}

// TODO: Validity of velocity and acceleration for large error in estimated
// position (i.e. position is adjusted due to a large amount of time without
// an encoder event).
template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::velocity() const {
    Eigen::Matrix<tsenccnt_t, M, 1> velocity_coefficients;
    for (unsigned int i = 0; i < M; ++i) {
        velocity_coefficients(i) = M - i;
    }
    return velocity_coefficients.template cast<polycoeff_t>().cwiseProduct(
            m_P.template head<M>()).transpose() * m_T.template tail<M>();
}

template <size_t M, size_t N, size_t O>
polycoeff_t TSEncoder<M, N, O>::acceleration() const {
    Eigen::Matrix<tsenccnt_t, M - 1 , 1> acceleration_coefficients;
    for (unsigned int i = 0; i < M - 1; ++i) {
        acceleration_coefficients(i) = (M - i) * (M - i - 1);
    }
    return acceleration_coefficients.template cast<polycoeff_t>().cwiseProduct(
            m_P.template head<M - 1>()).transpose() * m_T.template tail<M - 1>();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::ab_callback(EXTDriver* extp, expchannel_t channel) {
    (void)extp;
    osalSysLockFromISR();
    TSEncoder<M, N, O>* enc = reinterpret_cast<TSEncoder<M, N, O>*>(extGetChannelCallbackObject(channel));
    if (((channel == PAL_PAD(enc->m_config.a)) &&
                (palReadLine(enc->m_config.a) != palReadLine(enc->m_config.b))) ||
        ((channel == PAL_PAD(enc->m_config.b)) &&
                (palReadLine(enc->m_config.a) == palReadLine(enc->m_config.b)))) {
        ++enc->m_count;
    } else {
        --enc->m_count;
    }
    enc->m_events[enc->m_event_index] = std::make_pair(chSysGetRealtimeCounterX(), enc->m_count);
    enc->m_skip_order_counter = (enc->m_skip_order_counter + 1) % O;
    if (enc->m_skip_order_counter == 0) {
        enc->m_event_index = (enc->m_event_index + 1) % N;
    }
    osalSysUnlockFromISR();
}

template <size_t M, size_t N, size_t O>
void TSEncoder<M, N, O>::index_callback(EXTDriver* extp, expchannel_t channel) {
    osalSysLockFromISR();
    TSEncoder<M, N, O>* enc = reinterpret_cast<TSEncoder<M, N, O>*>(extGetChannelCallbackObject(channel));
    enc->m_count = 0;
    enc->m_index = index_t::FOUND;
    extChannelDisableClearModeI(extp, PAL_PAD(enc->m_config.z));
    osalSysUnlockFromISR();
}
<|endoftext|>


<commit_before>#ifndef UI_UILAYER_HPP
#define UI_UILAYER_HPP
#include "SDL.h"
namespace UI {

class Layer {
public:
    static Layer *front;

    virtual ~Layer();
    virtual void handleEvent(SDL_Event const &evt);
    virtual void draw();
};

}
#endif
<commit_msg>Fixed header guards on ui/layer.hpp.<commit_after>#ifndef UI_LAYER_HPP
#define UI_LAYER_HPP
#include "SDL.h"
namespace UI {

class Layer {
public:
    static Layer *front;

    virtual ~Layer();
    virtual void handleEvent(SDL_Event const &evt);
    virtual void draw();
};

}
#endif
<|endoftext|>


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

#include "Base.hxx"

#include <assert.h>
#include <string.h>

const char *
base_tail(const char *uri, const char *base)
{
	assert(uri != nullptr);
	assert(base != nullptr);

	const size_t uri_length = strlen(uri);
	const size_t base_length = strlen(base);

	return base_length > 0 && base[base_length - 1] == '/' &&
		uri_length >= base_length && memcmp(uri, base, base_length) == 0
		? uri + base_length
		: nullptr;
}

const char *
require_base_tail(const char *uri, const char *base)
{
	assert(uri != nullptr);
	assert(base != nullptr);
	assert(memcmp(base, uri, strlen(base)) == 0);

	return uri + strlen(base);
}

size_t
base_string(const char *p, const char *tail)
{
	assert(p != nullptr);
	assert(tail != nullptr);

	size_t length = strlen(p), tail_length = strlen(tail);

	if (length == tail_length)
		/* special case: zero-length prefix (not followed by a
		   slash) */
		return memcmp(p, tail, length) == 0
			? 0 : (size_t)-1;

	return length > tail_length && p[length - tail_length - 1] == '/' &&
		memcmp(p + length - tail_length, tail, tail_length) == 0
		? length - tail_length
		: (size_t)-1;
}

bool
is_base(const char *uri)
{
	assert(uri != nullptr);

	size_t length = strlen(uri);
	return length > 0 && uri[length - 1] == '/';
}
<commit_msg>uri/Base: use StringAfterPrefix()<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Base.hxx"
#include "util/StringCompare.hxx"

#include <assert.h>
#include <string.h>

const char *
base_tail(const char *uri, const char *base)
{
	assert(uri != nullptr);
	assert(base != nullptr);

	const size_t base_length = strlen(base);

	if (base_length == 0 || base[base_length - 1] != '/')
		/* not a valid base */
		return nullptr;

	return StringAfterPrefix(uri, {base, base_length});
}

const char *
require_base_tail(const char *uri, const char *base)
{
	assert(uri != nullptr);
	assert(base != nullptr);
	assert(StringStartsWith(uri, base));

	return uri + strlen(base);
}

size_t
base_string(const char *p, const char *tail)
{
	assert(p != nullptr);
	assert(tail != nullptr);

	size_t length = strlen(p), tail_length = strlen(tail);

	if (length == tail_length)
		/* special case: zero-length prefix (not followed by a
		   slash) */
		return memcmp(p, tail, length) == 0
			? 0 : (size_t)-1;

	return length > tail_length && p[length - tail_length - 1] == '/' &&
		memcmp(p + length - tail_length, tail, tail_length) == 0
		? length - tail_length
		: (size_t)-1;
}

bool
is_base(const char *uri)
{
	assert(uri != nullptr);

	size_t length = strlen(uri);
	return length > 0 && uri[length - 1] == '/';
}
<|endoftext|>


<commit_before>/**
 * @file uuid.cc
 * @author Bartek Kryza
 * @copyright (C) 2017 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in
 * 'LICENSE.txt'
 */

#include "uuid.h"

#include "base64.h"

#include <spdlog/fmt/fmt.h>

namespace one {
namespace client {
namespace util {
namespace uuid {

folly::fbstring uuidToSpaceId(const folly::fbstring &uuid)
{
    folly::fbstring result;
    folly::fbstring decodedUuid;
    auto status = util::base64::base64_url_decode(uuid, decodedUuid);

    if (status) {
        std::vector<folly::StringPiece> v;
        folly::split("#", decodedUuid, v);

        if ((v[0] != "guid"))
            throw std::invalid_argument("Invalid Onedata uuid format.");

        auto spaceFragment = v[1];

        if (spaceFragment.removePrefix("space_")) {
            return spaceFragment.toString();
        }

        throw std::invalid_argument("Onedata uuid does not contain space id.");
    }

    throw std::invalid_argument("Base64 decoding of Onedata uuid failed.");
}

folly::fbstring spaceIdToSpaceUUID(const folly::fbstring &spaceId)
{
    folly::fbstring result;
    util::base64::base64_url_encode(
        fmt::format(
            "guid#space_{}#{}", spaceId.toStdString(), spaceId.toStdString()),
        result);
    return result;
}
} // namespace uuid
} // namespace util
} // namespace client
} // namespace one
<commit_msg>VFS-7360 Fixed UUID space_id parser<commit_after>/**
 * @file uuid.cc
 * @author Bartek Kryza
 * @copyright (C) 2017 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in
 * 'LICENSE.txt'
 */

#include "uuid.h"

#include "base64.h"

#include <spdlog/fmt/fmt.h>

namespace one {
namespace client {
namespace util {
namespace uuid {

folly::fbstring uuidToSpaceId(const folly::fbstring &uuid)
{
    folly::fbstring result;
    folly::fbstring decodedUuid;
    auto status = util::base64::base64_url_decode(uuid, decodedUuid);

    if (status) {
        std::vector<folly::StringPiece> v;
        folly::split("#", decodedUuid, v);

        if ((v.size() < 3) || (v[0] != "guid" && v[0] != "shareGuid"))
            throw std::invalid_argument("Invalid Onedata uuid format.");

        return v[2].toString();
    }

    throw std::invalid_argument("Base64 decoding of Onedata uuid failed.");
}

folly::fbstring spaceIdToSpaceUUID(const folly::fbstring &spaceId)
{
    folly::fbstring result;
    util::base64::base64_url_encode(
        fmt::format(
            "guid#space_{}#{}", spaceId.toStdString(), spaceId.toStdString()),
        result);
    return result;
}
} // namespace uuid
} // namespace util
} // namespace client
} // namespace one
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2012 by INdT
 * Copyright (C) 2014 Bacon2D Project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * @author Rodrigo Goncalves de Oliveira <rodrigo.goncalves@openbossa.org>
 * @author Roger Felipe Zanoni da Silva <roger.zanoni@openbossa.org>
 */

#include "viewport.h"
#include "scene.h"

/*!
  \qmltype Viewport
  \inqmlmodule Bacon2D
  \brief The visible portion of a larger \l Scene.

   The Viewport is the visible area of a larger \l Scene and can be scrolled
   horizontally or vertically showing different parts of the larger \l Scene.
*/
Viewport::Viewport(Scene *parent)
    : Entity(parent)
    , m_animationEasingCurve(QEasingCurve::Linear)
    , m_xOffset(0.0f)
    , m_yOffset(0.0f)
    , m_contentWidth(0.0f)
    , m_contentHeight(0.0f)
    , m_maxXOffset(0.0f)
    , m_maxYOffset(0.0f)
    , m_scene(parent)
    , m_animationDuration(100)
{
    m_xGroupAnimation = new QParallelAnimationGroup(this);
    m_yGroupAnimation = new QParallelAnimationGroup(this);
}

/*!
  \qmlproperty float Viewport::xOffset
  \brief This property holds the horizontal offset of the Viewport's x property from the
   Scene's x property.
*/
float Viewport::xOffset()
{
    return m_xOffset;
}

void Viewport::setXOffset(float xOffset)
{
    xOffset = qBound<float>(0.0f, xOffset, m_maxXOffset);

    // TODO create a heuristic function to calculate the animation duration
    // when m_animationDuration < 0, according to this delta
    // (bigger delta -> bigger animation duration)
    //qreal delta = qAbs(m_xOffset - xOffset);

    if (m_xOffset != xOffset) {
        m_xOffset = xOffset;

        if (m_scene) {
            m_xGroupAnimation->clear();

            QPropertyAnimation *xAnim = new QPropertyAnimation(m_scene, "x");
            xAnim->setDuration(m_animationDuration); // TODO set duration according the offset value
            xAnim->setEasingCurve(m_animationEasingCurve);
            xAnim->setStartValue(m_scene->x());
            xAnim->setEndValue(-m_xOffset);
            m_xGroupAnimation->addAnimation(xAnim);

            /* TODO: Fix this according to current layer scheme
            QPropertyAnimation *xLayerAnimation = new QPropertyAnimation(m_scene->gameLayers(), "xOffset"); // TODO
            xLayerAnimation->setDuration(m_animationDuration); // TODO
            xLayerAnimation->setEasingCurve(m_animationEasingCurve);
            xLayerAnimation->setStartValue(m_scene->gameLayers()->xOffset());
            xLayerAnimation->setEndValue(m_xOffset);
            m_xGroupAnimation->addAnimation(xLayerAnimation);
            */

            m_xGroupAnimation->start();
        }

        emit xOffsetChanged();
    }
}

/*!
  \qmlproperty float Viewport::yOffset
  \brief This property holds the vertical offset of the Viewport's y property from the
   Scene's y property.
*/
float Viewport::yOffset()
{
    return m_yOffset;
}

void Viewport::setYOffset(float yOffset)
{
    yOffset = qBound<float>(0.0f, yOffset, m_maxYOffset);

    if (m_yOffset != yOffset) {
        m_yOffset = yOffset;

        if (m_scene) {
            m_yGroupAnimation->clear();

            QPropertyAnimation *yAnim = new QPropertyAnimation(m_scene, "y");
            yAnim->setDuration(m_animationDuration); // TODO set duration according the offset value
            yAnim->setEasingCurve(m_animationEasingCurve);
            yAnim->setStartValue(m_scene->y());
            yAnim->setEndValue(-m_yOffset);
            m_yGroupAnimation->addAnimation(yAnim);

            /* TODO: Fix this according to current layer scheme
            QPropertyAnimation *yLayerAnimation = new QPropertyAnimation(m_scene->gameLayers(), "yOffset"); // TODO
            yLayerAnimation->setDuration(m_animationDuration); // TODO
            yLayerAnimation->setEasingCurve(m_animationEasingCurve);
            yLayerAnimation->setStartValue(m_scene->gameLayers()->yOffset());
            yLayerAnimation->setEndValue(m_yOffset);
            m_yGroupAnimation->addAnimation(yLayerAnimation);
            */

            m_yGroupAnimation->start();
        }

        emit yOffsetChanged();
    }
}

/*!
  \qmlmethod Viewport::hScroll(float step)
  \brief Scroll the Viewport horizontally by \a step
*/
void Viewport::hScroll(float step)
{
    setXOffset(step);
}

/*!
  \qmlmethod Viewport::yScroll(float step)
  \brief Scroll the Viewport vertically by \a step
*/
void Viewport::vScroll(float step)
{
    setYOffset(step);
}

float Viewport::contentWidth() const
{
    return m_contentWidth;
}

void Viewport::setContentWidth(const float &contentWidth)
{
    if (m_contentWidth != contentWidth) {
        m_contentWidth = contentWidth;

        emit contentWidthChanged();
    }
}

float Viewport::contentHeight() const
{
    return m_contentHeight;
}

void Viewport::setContentHeight(const float &contentHeight)
{
    if (m_contentHeight != contentHeight) {
        m_contentHeight = contentHeight;

        emit contentHeightChanged();
    }
}

/*!
  \qmlproperty int Viewport::animationDuration
  \brief Viewport scrolling uses an animation, the animationDuration
   property sets the duration in milliseconds of that animation.  You can
   change this duration for smooth panning of the Viewport.  The default
   is 100ms.
*/
int Viewport::animationDuration() const
{
    return m_animationDuration;
}

void Viewport::setAnimationDuration(const int &animationDuration)
{
    if (m_animationDuration != animationDuration) {
        m_animationDuration = animationDuration;

        emit animationDurationChanged();
    }
}

void Viewport::setScene(Scene *scene)
{
    m_scene = scene;
    scene->setParentItem(this);
    setContentWidth(scene->width());
    setContentHeight(scene->height());
    setVisible(true);
    updateMaxOffsets();
}

void Viewport::updateMaxOffsets()
{
    m_maxXOffset = m_contentWidth - width();
    m_maxYOffset = m_contentHeight - height();
}
<commit_msg>Fixed typo in docstring for Viewport::vScroll<commit_after>/**
 * Copyright (C) 2012 by INdT
 * Copyright (C) 2014 Bacon2D Project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * @author Rodrigo Goncalves de Oliveira <rodrigo.goncalves@openbossa.org>
 * @author Roger Felipe Zanoni da Silva <roger.zanoni@openbossa.org>
 */

#include "viewport.h"
#include "scene.h"

/*!
  \qmltype Viewport
  \inqmlmodule Bacon2D
  \brief The visible portion of a larger \l Scene.

   The Viewport is the visible area of a larger \l Scene and can be scrolled
   horizontally or vertically showing different parts of the larger \l Scene.
*/
Viewport::Viewport(Scene *parent)
    : Entity(parent)
    , m_animationEasingCurve(QEasingCurve::Linear)
    , m_xOffset(0.0f)
    , m_yOffset(0.0f)
    , m_contentWidth(0.0f)
    , m_contentHeight(0.0f)
    , m_maxXOffset(0.0f)
    , m_maxYOffset(0.0f)
    , m_scene(parent)
    , m_animationDuration(100)
{
    m_xGroupAnimation = new QParallelAnimationGroup(this);
    m_yGroupAnimation = new QParallelAnimationGroup(this);
}

/*!
  \qmlproperty float Viewport::xOffset
  \brief This property holds the horizontal offset of the Viewport's x property from the
   Scene's x property.
*/
float Viewport::xOffset()
{
    return m_xOffset;
}

void Viewport::setXOffset(float xOffset)
{
    xOffset = qBound<float>(0.0f, xOffset, m_maxXOffset);

    // TODO create a heuristic function to calculate the animation duration
    // when m_animationDuration < 0, according to this delta
    // (bigger delta -> bigger animation duration)
    //qreal delta = qAbs(m_xOffset - xOffset);

    if (m_xOffset != xOffset) {
        m_xOffset = xOffset;

        if (m_scene) {
            m_xGroupAnimation->clear();

            QPropertyAnimation *xAnim = new QPropertyAnimation(m_scene, "x");
            xAnim->setDuration(m_animationDuration); // TODO set duration according the offset value
            xAnim->setEasingCurve(m_animationEasingCurve);
            xAnim->setStartValue(m_scene->x());
            xAnim->setEndValue(-m_xOffset);
            m_xGroupAnimation->addAnimation(xAnim);

            /* TODO: Fix this according to current layer scheme
            QPropertyAnimation *xLayerAnimation = new QPropertyAnimation(m_scene->gameLayers(), "xOffset"); // TODO
            xLayerAnimation->setDuration(m_animationDuration); // TODO
            xLayerAnimation->setEasingCurve(m_animationEasingCurve);
            xLayerAnimation->setStartValue(m_scene->gameLayers()->xOffset());
            xLayerAnimation->setEndValue(m_xOffset);
            m_xGroupAnimation->addAnimation(xLayerAnimation);
            */

            m_xGroupAnimation->start();
        }

        emit xOffsetChanged();
    }
}

/*!
  \qmlproperty float Viewport::yOffset
  \brief This property holds the vertical offset of the Viewport's y property from the
   Scene's y property.
*/
float Viewport::yOffset()
{
    return m_yOffset;
}

void Viewport::setYOffset(float yOffset)
{
    yOffset = qBound<float>(0.0f, yOffset, m_maxYOffset);

    if (m_yOffset != yOffset) {
        m_yOffset = yOffset;

        if (m_scene) {
            m_yGroupAnimation->clear();

            QPropertyAnimation *yAnim = new QPropertyAnimation(m_scene, "y");
            yAnim->setDuration(m_animationDuration); // TODO set duration according the offset value
            yAnim->setEasingCurve(m_animationEasingCurve);
            yAnim->setStartValue(m_scene->y());
            yAnim->setEndValue(-m_yOffset);
            m_yGroupAnimation->addAnimation(yAnim);

            /* TODO: Fix this according to current layer scheme
            QPropertyAnimation *yLayerAnimation = new QPropertyAnimation(m_scene->gameLayers(), "yOffset"); // TODO
            yLayerAnimation->setDuration(m_animationDuration); // TODO
            yLayerAnimation->setEasingCurve(m_animationEasingCurve);
            yLayerAnimation->setStartValue(m_scene->gameLayers()->yOffset());
            yLayerAnimation->setEndValue(m_yOffset);
            m_yGroupAnimation->addAnimation(yLayerAnimation);
            */

            m_yGroupAnimation->start();
        }

        emit yOffsetChanged();
    }
}

/*!
  \qmlmethod Viewport::hScroll(float step)
  \brief Scroll the Viewport horizontally by \a step
*/
void Viewport::hScroll(float step)
{
    setXOffset(step);
}

/*!
  \qmlmethod Viewport::vScroll(float step)
  \brief Scroll the Viewport vertically by \a step
*/
void Viewport::vScroll(float step)
{
    setYOffset(step);
}

float Viewport::contentWidth() const
{
    return m_contentWidth;
}

void Viewport::setContentWidth(const float &contentWidth)
{
    if (m_contentWidth != contentWidth) {
        m_contentWidth = contentWidth;

        emit contentWidthChanged();
    }
}

float Viewport::contentHeight() const
{
    return m_contentHeight;
}

void Viewport::setContentHeight(const float &contentHeight)
{
    if (m_contentHeight != contentHeight) {
        m_contentHeight = contentHeight;

        emit contentHeightChanged();
    }
}

/*!
  \qmlproperty int Viewport::animationDuration
  \brief Viewport scrolling uses an animation, the animationDuration
   property sets the duration in milliseconds of that animation.  You can
   change this duration for smooth panning of the Viewport.  The default
   is 100ms.
*/
int Viewport::animationDuration() const
{
    return m_animationDuration;
}

void Viewport::setAnimationDuration(const int &animationDuration)
{
    if (m_animationDuration != animationDuration) {
        m_animationDuration = animationDuration;

        emit animationDurationChanged();
    }
}

void Viewport::setScene(Scene *scene)
{
    m_scene = scene;
    scene->setParentItem(this);
    setContentWidth(scene->width());
    setContentHeight(scene->height());
    setVisible(true);
    updateMaxOffsets();
}

void Viewport::updateMaxOffsets()
{
    m_maxXOffset = m_contentWidth - width();
    m_maxYOffset = m_contentHeight - height();
}
<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/http/client/HttpClient.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HeaderFieldList.h>
#include <xzero/net/TcpEndPoint.h>
#include <xzero/executor/NativeScheduler.h>
#include <xzero/net/DnsClient.h>
#include <xzero/io/FileUtil.h>
#include <xzero/Application.h>
#include <xzero/RuntimeError.h>
#include <xzero/Uri.h>
#include <xzero/Flags.h>
#include <xzero/logging.h>
#include <unordered_map>
#include "sysconfig.h"
#include <iostream>
#include <unistd.h>

// #define PACKAGE_VERSION X0_VERSION
#define PACKAGE_HOMEPAGE_URL "https://xzero.io"

#define DEBUG(msg...) logDebug("xurl", msg)
#define TRACE(msg...) logTrace("xurl", msg)

using namespace xzero;
using namespace xzero::http;
using namespace xzero::http::client;

using xzero::http::HttpRequestInfo;
using xzero::http::HttpVersion;
using xzero::http::HeaderField;

class ServicePortMapping { // {{{
 public:
  ServicePortMapping();

  void loadFile(const std::string& path = "/etc/services");
  void loadContent(const std::string& content);

  int tcp(const std::string& name);
  int udp(const std::string& name);

 private:
  std::unordered_map<std::string, int> tcp_;
  std::unordered_map<std::string, int> udp_;
};

ServicePortMapping::ServicePortMapping()
    : tcp_() {
  tcp_["http"] = 80;
  tcp_["https"] = 443;
}

int ServicePortMapping::tcp(const std::string& name) {
  auto i = tcp_.find(name);
  if (i != tcp_.end())
    return i->second;

  RAISE(RuntimeError, "Unknown service '%s'", name.c_str());
}
// }}}

class XUrl {
 public:
  XUrl();

  int run(int argc, const char* argv[]);

 private:
  void addRequestHeader(const std::string& value);
  Uri makeUri(const std::string& url);
  IPAddress getIPAddress(const std::string& host);
  int getPort(const Uri& uri);
  void query(const Uri& uri);

 private:
  NativeScheduler scheduler_;
  Flags flags_;
  DnsClient dns_;
  Duration connectTimeout_;
  Duration readTimeout_;
  Duration writeTimeout_;
  http::HeaderFieldList requestHeaders_;
};

XUrl::XUrl()
    : scheduler_(CatchAndLogExceptionHandler("xurl")),
      flags_(),
      dns_(),
      connectTimeout_(4_seconds),
      readTimeout_(60_seconds),
      writeTimeout_(10_seconds)
{
  Application::logToStderr(LogLevel::Info);

  requestHeaders_.push_back("User-Agent", "xurl/" PACKAGE_VERSION);

  flags_.defineBool("help", 'h', "Prints this help.");
  flags_.defineBool("head", 'I', "Performs a HEAD request");
  flags_.defineString("output", 'o', "PATH", "Write response body to given file.");
  flags_.defineString("log-level", 0, "STRING", "Log level.", "info");
  flags_.defineString("method", 'X', "METHOD", "HTTP method", "GET");
  flags_.defineNumber("connect-timeout", 0, "MS", "TCP connect() timeout", 10_seconds .milliseconds());
  flags_.defineString("upload-file", 'T', "PATH", "Uploads given file.", "");
  flags_.defineString("header", 'H', "HEADER", "Adds a custom request header",
      None(),
      std::bind(&XUrl::addRequestHeader, this, std::placeholders::_1));
  flags_.defineBool("ipv4", '4', "Favor IPv4 for TCP/IP communication.");
  flags_.defineBool("ipv6", '6', "Favor IPv6 for TCP/IP communication.");
  flags_.enableParameters("URL", "URL to query");
}

void XUrl::addRequestHeader(const std::string& field) {
  requestHeaders_.push_back(HeaderField::parse(field));
}

int XUrl::run(int argc, const char* argv[]) {
  std::error_code ec = flags_.parse(argc, argv);
  if (ec) {
    fprintf(stderr, "Failed to parse flags. %s\n", ec.message().c_str());
    return 1;
  }

  Logger::get()->setMinimumLogLevel(make_loglevel(flags_.getString("log-level")));

  if (flags_.getBool("help")) {
    std::cerr
      << "xurl: Xzero HTTP Client" PACKAGE_VERSION
          << " [" PACKAGE_HOMEPAGE_URL "]" << std::endl
      << "Copyright (c) 2009-2017 by Christian Parpart <christian@parpart.family>" << std::endl
      << std::endl
      << "Usage: xurl [options ...]" << std::endl
      << std::endl
      << "Options:" << std::endl
      << flags_.helpText() << std::endl;
    return 0;
  }

  if (flags_.parameters().empty()) {
    logError("xurl", "No URL given.");
    return 1;
  }

  if (flags_.parameters().size() != 1) {
    logError("xurl", "Too many URLs given.");
  }

  Uri uri = makeUri(flags_.parameters()[0]);
  query(uri);

  return 0;
}

Uri XUrl::makeUri(const std::string& url) {
  Uri uri(url);

  if (uri.path() == "")
    uri.setPath("/");

  return uri;
}

IPAddress XUrl::getIPAddress(const std::string& host) {
  std::vector<IPAddress> ipaddresses = dns_.ipv4(host);
  if (ipaddresses.empty())
    RAISE(RuntimeError, "Could not resolve %s.", host.c_str());

  return ipaddresses.front();
}

int XUrl::getPort(const Uri& uri) {
  if (uri.port())
    return uri.port();

  return ServicePortMapping().tcp(uri.scheme());
}

void XUrl::query(const Uri& uri) {
  IPAddress ipaddr = getIPAddress(uri.host());
  int port = getPort(uri);
  InetAddress inetAddr(ipaddr, port);
  Duration keepAlive = 8_seconds;

  const bool secure = false; // HTTP ?

  TRACE("getting request method");
  std::string method = flags_.getString("method");

  TRACE("getting request head?");
  if (flags_.getBool("head")) {
    method = "HEAD";
  }

  TRACE("getting request body");
  HugeBuffer body;
  if (!flags_.getString("upload-file").empty()) {
    method = "PUT";
    body = FileUtil::read(flags_.getString("upload-file"));
  }

  requestHeaders_.overwrite("Host", uri.hostAndPort());

  HttpRequest req(HttpVersion::VERSION_1_1,
                  method,
                  uri.pathAndQuery(),
                  requestHeaders_,
                  secure,
                  std::move(body));

  logInfo("xurl", "$0 $1 HTTP/$2",
          req.unparsedMethod(), req.unparsedUri(), req.version());

  for (const HeaderField& field: req.headers()) {
    logInfo("xurl", "< $0: $1", field.name(), field.value());
  }

  HttpClient httpClient(
      &scheduler_, inetAddr,
      connectTimeout_, readTimeout_, writeTimeout_,
      keepAlive);

  Future<HttpClient::Response> f = httpClient.send(req);

  f.onSuccess([](HttpClient::Response& response) {
    logInfo("xurl", "HTTP/$0 $1 $2", response.version(),
                                     (int) response.status(),
                                     response.reason());

    for (const HeaderField& field: response.headers()) {
      logInfo("xurl", "> $0: $1", field.name(), field.value());
    }

    const BufferRef& content = response.content().getBuffer();
    write(STDOUT_FILENO, content.data(), content.size());
  });

  f.onFailure([](std::error_code ec) {
    logError("xurl", "connect() failed. $0", ec.message());
  });

  scheduler_.runLoop();
}

int main(int argc, const char* argv[]) {
  XUrl app;
  return app.run(argc, argv);
}
<commit_msg>[xurl] spacing<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/http/client/HttpClient.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HeaderFieldList.h>
#include <xzero/net/TcpEndPoint.h>
#include <xzero/executor/NativeScheduler.h>
#include <xzero/net/DnsClient.h>
#include <xzero/io/FileUtil.h>
#include <xzero/Application.h>
#include <xzero/RuntimeError.h>
#include <xzero/Uri.h>
#include <xzero/Flags.h>
#include <xzero/logging.h>
#include <unordered_map>
#include "sysconfig.h"
#include <iostream>
#include <unistd.h>

// #define PACKAGE_VERSION X0_VERSION
#define PACKAGE_HOMEPAGE_URL "https://xzero.io"

#define DEBUG(msg...) logDebug("xurl", msg)
#define TRACE(msg...) logTrace("xurl", msg)

using namespace xzero;
using namespace xzero::http;
using namespace xzero::http::client;

using xzero::http::HttpRequestInfo;
using xzero::http::HttpVersion;
using xzero::http::HeaderField;

class ServicePortMapping { // {{{
 public:
  ServicePortMapping();

  void loadFile(const std::string& path = "/etc/services");
  void loadContent(const std::string& content);

  int tcp(const std::string& name);
  int udp(const std::string& name);

 private:
  std::unordered_map<std::string, int> tcp_;
  std::unordered_map<std::string, int> udp_;
};

ServicePortMapping::ServicePortMapping()
    : tcp_() {
  tcp_["http"] = 80;
  tcp_["https"] = 443;
}

int ServicePortMapping::tcp(const std::string& name) {
  auto i = tcp_.find(name);
  if (i != tcp_.end())
    return i->second;

  RAISE(RuntimeError, "Unknown service '%s'", name.c_str());
}
// }}}

class XUrl {
 public:
  XUrl();

  int run(int argc, const char* argv[]);

 private:
  void addRequestHeader(const std::string& value);
  Uri makeUri(const std::string& url);
  IPAddress getIPAddress(const std::string& host);
  int getPort(const Uri& uri);
  void query(const Uri& uri);

 private:
  NativeScheduler scheduler_;
  Flags flags_;
  DnsClient dns_;
  Duration connectTimeout_;
  Duration readTimeout_;
  Duration writeTimeout_;
  http::HeaderFieldList requestHeaders_;
};

XUrl::XUrl()
    : scheduler_(CatchAndLogExceptionHandler("xurl")),
      flags_(),
      dns_(),
      connectTimeout_(4_seconds),
      readTimeout_(60_seconds),
      writeTimeout_(10_seconds)
{
  Application::logToStderr(LogLevel::Info);

  requestHeaders_.push_back("User-Agent", "xurl/" PACKAGE_VERSION);

  flags_.defineBool("help", 'h', "Prints this help.");
  flags_.defineBool("head", 'I', "Performs a HEAD request");
  flags_.defineString("output", 'o', "PATH", "Write response body to given file.");
  flags_.defineString("log-level", 0, "STRING", "Log level.", "info");
  flags_.defineString("method", 'X', "METHOD", "HTTP method", "GET");
  flags_.defineNumber("connect-timeout", 0, "MS", "TCP connect() timeout", 10_seconds .milliseconds());
  flags_.defineString("upload-file", 'T', "PATH", "Uploads given file.", "");
  flags_.defineString("header", 'H', "HEADER", "Adds a custom request header",
      None(),
      std::bind(&XUrl::addRequestHeader, this, std::placeholders::_1));
  flags_.defineBool("ipv4", '4', "Favor IPv4 for TCP/IP communication.");
  flags_.defineBool("ipv6", '6', "Favor IPv6 for TCP/IP communication.");
  flags_.enableParameters("URL", "URL to query");
}

void XUrl::addRequestHeader(const std::string& field) {
  requestHeaders_.push_back(HeaderField::parse(field));
}

int XUrl::run(int argc, const char* argv[]) {
  std::error_code ec = flags_.parse(argc, argv);
  if (ec) {
    fprintf(stderr, "Failed to parse flags. %s\n", ec.message().c_str());
    return 1;
  }

  Logger::get()->setMinimumLogLevel(make_loglevel(flags_.getString("log-level")));

  if (flags_.getBool("help")) {
    std::cerr
      << "xurl: Xzero HTTP Client" PACKAGE_VERSION
          << " [" PACKAGE_HOMEPAGE_URL "]" << std::endl
      << "Copyright (c) 2009-2017 by Christian Parpart <christian@parpart.family>" << std::endl
      << std::endl
      << "Usage: xurl [options ...]" << std::endl
      << std::endl
      << "Options:" << std::endl
      << flags_.helpText() << std::endl;
    return 0;
  }

  if (flags_.parameters().empty()) {
    logError("xurl", "No URL given.");
    return 1;
  }

  if (flags_.parameters().size() != 1) {
    logError("xurl", "Too many URLs given.");
  }

  Uri uri = makeUri(flags_.parameters()[0]);
  query(uri);

  return 0;
}

Uri XUrl::makeUri(const std::string& url) {
  Uri uri(url);

  if (uri.path() == "")
    uri.setPath("/");

  return uri;
}

IPAddress XUrl::getIPAddress(const std::string& host) {
  std::vector<IPAddress> ipaddresses = dns_.ipv4(host);
  if (ipaddresses.empty())
    RAISE(RuntimeError, "Could not resolve %s.", host.c_str());

  return ipaddresses.front();
}

int XUrl::getPort(const Uri& uri) {
  if (uri.port())
    return uri.port();

  return ServicePortMapping().tcp(uri.scheme());
}

void XUrl::query(const Uri& uri) {
  IPAddress ipaddr = getIPAddress(uri.host());
  int port = getPort(uri);
  InetAddress inetAddr(ipaddr, port);
  Duration keepAlive = 8_seconds;

  const bool secure = false; // HTTP ?

  TRACE("getting request method");
  std::string method = flags_.getString("method");

  TRACE("getting request head?");
  if (flags_.getBool("head")) {
    method = "HEAD";
  }

  TRACE("getting request body");
  HugeBuffer body;
  if (!flags_.getString("upload-file").empty()) {
    method = "PUT";
    body = FileUtil::read(flags_.getString("upload-file"));
  }

  requestHeaders_.overwrite("Host", uri.hostAndPort());

  HttpRequest req(HttpVersion::VERSION_1_1,
                  method,
                  uri.pathAndQuery(),
                  requestHeaders_,
                  secure,
                  std::move(body));

  logInfo("xurl", "$0 $1 HTTP/$2",
          req.unparsedMethod(), req.unparsedUri(), req.version());

  for (const HeaderField& field: req.headers()) {
    logInfo("xurl", "< $0: $1", field.name(), field.value());
  }

  HttpClient httpClient(&scheduler_, inetAddr,
                        connectTimeout_, readTimeout_, writeTimeout_,
                        keepAlive);

  Future<HttpClient::Response> f = httpClient.send(req);

  f.onSuccess([](HttpClient::Response& response) {
    logInfo("xurl", "HTTP/$0 $1 $2", response.version(),
                                     (int) response.status(),
                                     response.reason());

    for (const HeaderField& field: response.headers()) {
      logInfo("xurl", "> $0: $1", field.name(), field.value());
    }

    const BufferRef& content = response.content().getBuffer();
    write(STDOUT_FILENO, content.data(), content.size());
  });

  f.onFailure([](std::error_code ec) {
    logError("xurl", "connect() failed. $0", ec.message());
  });

  scheduler_.runLoop();
}

int main(int argc, const char* argv[]) {
  XUrl app;
  return app.run(argc, argv);
}
<|endoftext|>