<commit_before>//
// C++ Implementation: BuiltinFuncs
//
// Description:
//
//
// Author: Carmelo Piccione <carmelo.piccione@gmail.com>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//

/* Loads all builtin functions */

/* Loads a builtin function */
#include "BuiltinFuncs.hpp"
#include <string>
#include <iostream>
#include "fatal.h"

std::map<std::string, Func*> BuiltinFuncs::builtin_func_tree;

int BuiltinFuncs::load_builtin_func(const std::string & name, float (*func_ptr)(float*), int num_args) {

  Func * func;
  int retval;

  /* Create new function */
  func = new Func(name, func_ptr, num_args);

  if (func == 0)
    return PROJECTM_OUTOFMEM_ERROR;

  retval = insert_func( func );

  return retval;

}

Func * BuiltinFuncs::find_func(const std::string & name) {

  std::map<std::string, Func*>::iterator pos = builtin_func_tree.find(name);

  // Case: function not found, return null
  if (pos == builtin_func_tree.end())
	return 0;

  // Case: function found, return a pointer to it
  return pos->second;

}

int BuiltinFuncs::load_all_builtin_func() {

  if (load_builtin_func("int", FuncWrappers::int_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("abs", FuncWrappers::abs_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sin", FuncWrappers::sin_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("cos", FuncWrappers::cos_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("tan", FuncWrappers::tan_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("asin", FuncWrappers::asin_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("acos", FuncWrappers::acos_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("atan", FuncWrappers::atan_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sqr", FuncWrappers::sqr_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sqrt", FuncWrappers::sqrt_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("pow", FuncWrappers::pow_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("exp", FuncWrappers::exp_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("log", FuncWrappers::log_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("log10", FuncWrappers::log10_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sign", FuncWrappers::sign_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("min", FuncWrappers::min_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("max", FuncWrappers::max_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sigmoid", FuncWrappers::sigmoid_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("atan2", FuncWrappers::atan2_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("rand", FuncWrappers::rand_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("band", FuncWrappers::band_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("bor", FuncWrappers::bor_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("bnot", FuncWrappers::bnot_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("if", FuncWrappers::if_wrapper, 3) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("equal", FuncWrappers::equal_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("above", FuncWrappers::above_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("below", FuncWrappers::below_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("nchoosek", FuncWrappers::nchoosek_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("fact", FuncWrappers::fact_wrapper, 1) < 0)
    return PROJECTM_ERROR;

  return PROJECTM_SUCCESS;
}

volatile bool BuiltinFuncs::initialized = false;

/* Initialize the builtin function database.
   Should only be necessary once */
int BuiltinFuncs::init_builtin_func_db() {
  int retval;

  if (initialized) {
    return 0;
  } else
    initialized = true;

  retval = load_all_builtin_func();
  return retval;
}

/* Destroy the builtin function database.
   Generally, do this on projectm exit */
int BuiltinFuncs::destroy_builtin_func_db() {

traverse<TraverseFunctors::Delete<Func> >(builtin_func_tree);

builtin_func_tree.clear();
initialized = false;
return PROJECTM_SUCCESS;
}

/* Insert a function into the database */
int BuiltinFuncs::insert_func( Func *func ) {

  assert(func);
  if (func == 0) {
      std::cerr << "Received a null function object, ignoring...." << std::endl;
      return PROJECTM_ERROR;
  }

  std::cout << "inserting function " << func->getName() << std::endl;

  const std::pair<std::string, Func*> pair = std::make_pair(std::string(func->getName()), func);

  assert(pair.second);

  const std::pair<std::map<std::string, Func*>::iterator, bool> inserteePair =
  	builtin_func_tree.insert(pair);

  if (!inserteePair.second) {
	std::cerr << "Failed to insert builtin function \"" << func->getName() << "\" into collection! Bailing..." << std::endl;
	abort();

  }

  return PROJECTM_SUCCESS;
}

<commit_msg>removed debugging<commit_after>//
// C++ Implementation: BuiltinFuncs
//
// Description:
//
//
// Author: Carmelo Piccione <carmelo.piccione@gmail.com>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//

/* Loads all builtin functions */

/* Loads a builtin function */
#include "BuiltinFuncs.hpp"
#include <string>
#include <iostream>
#include "fatal.h"

std::map<std::string, Func*> BuiltinFuncs::builtin_func_tree;

int BuiltinFuncs::load_builtin_func(const std::string & name, float (*func_ptr)(float*), int num_args) {

  Func * func;
  int retval;

  /* Create new function */
  func = new Func(name, func_ptr, num_args);

  if (func == 0)
    return PROJECTM_OUTOFMEM_ERROR;

  retval = insert_func( func );

  return retval;

}

Func * BuiltinFuncs::find_func(const std::string & name) {

  std::map<std::string, Func*>::iterator pos = builtin_func_tree.find(name);

  // Case: function not found, return null
  if (pos == builtin_func_tree.end())
	return 0;

  // Case: function found, return a pointer to it
  return pos->second;

}

int BuiltinFuncs::load_all_builtin_func() {

  if (load_builtin_func("int", FuncWrappers::int_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("abs", FuncWrappers::abs_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sin", FuncWrappers::sin_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("cos", FuncWrappers::cos_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("tan", FuncWrappers::tan_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("asin", FuncWrappers::asin_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("acos", FuncWrappers::acos_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("atan", FuncWrappers::atan_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sqr", FuncWrappers::sqr_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sqrt", FuncWrappers::sqrt_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("pow", FuncWrappers::pow_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("exp", FuncWrappers::exp_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("log", FuncWrappers::log_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("log10", FuncWrappers::log10_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sign", FuncWrappers::sign_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("min", FuncWrappers::min_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("max", FuncWrappers::max_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("sigmoid", FuncWrappers::sigmoid_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("atan2", FuncWrappers::atan2_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("rand", FuncWrappers::rand_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("band", FuncWrappers::band_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("bor", FuncWrappers::bor_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("bnot", FuncWrappers::bnot_wrapper, 1) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("if", FuncWrappers::if_wrapper, 3) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("equal", FuncWrappers::equal_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("above", FuncWrappers::above_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("below", FuncWrappers::below_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("nchoosek", FuncWrappers::nchoosek_wrapper, 2) < 0)
    return PROJECTM_ERROR;
  if (load_builtin_func("fact", FuncWrappers::fact_wrapper, 1) < 0)
    return PROJECTM_ERROR;

  return PROJECTM_SUCCESS;
}

volatile bool BuiltinFuncs::initialized = false;

/* Initialize the builtin function database.
   Should only be necessary once */
int BuiltinFuncs::init_builtin_func_db() {
  int retval;

  if (initialized) {
    return 0;
  } else
    initialized = true;

  retval = load_all_builtin_func();
  return retval;
}

/* Destroy the builtin function database.
   Generally, do this on projectm exit */
int BuiltinFuncs::destroy_builtin_func_db() {

traverse<TraverseFunctors::Delete<Func> >(builtin_func_tree);

builtin_func_tree.clear();
initialized = false;
return PROJECTM_SUCCESS;
}

/* Insert a function into the database */
int BuiltinFuncs::insert_func( Func *func ) {

  assert(func);

  if (func == 0) {
      std::cerr << "Received a null function object, ignoring...." << std::endl;
      return PROJECTM_ERROR;
  }

//   //std::cout << "inserting function " << func->getName() << std::endl;

  const std::pair<std::string, Func*> pair = std::make_pair(std::string(func->getName()), func);

  assert(pair.second);

  const std::pair<std::map<std::string, Func*>::iterator, bool> inserteePair =
  	builtin_func_tree.insert(pair);

  if (!inserteePair.second) {
	std::cerr << "Failed to insert builtin function \"" << func->getName() << "\" into collection! Bailing..." << std::endl;
	abort();
  }

  return PROJECTM_SUCCESS;
}

<|endoftext|>


<commit_before>#include <GLFW/glfw3.h>

#include "GLFWSystem.hpp"
#include "kengine.hpp"

#include "data/InputBufferComponent.hpp"
#include "data/GLFWWindowComponent.hpp"
#include "data/WindowComponent.hpp"

#include "functions/OnTerminate.hpp"
#include "functions/OnMouseCaptured.hpp"
#include "functions/Execute.hpp"

#include "imgui.h"
#include "with.hpp"

namespace kengine::glfw {
	namespace Input {
		static InputBufferComponent * g_buffer;

		static void onKey(GLFWwindow * window, int key, int scancode, int action, int mods) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureKeyboard))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			if (action == GLFW_PRESS)
				g_buffer->keys.push_back(InputBufferComponent::KeyEvent{ id, key, true });
			else if (action == GLFW_RELEASE)
				g_buffer->keys.push_back(InputBufferComponent::KeyEvent{ id, key, false });
		}

		static putils::Point2f lastPos{ FLT_MAX, FLT_MAX };

		static void onClick(GLFWwindow * window, int button, int action, int mods) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			if (action == GLFW_PRESS)
				g_buffer->clicks.push_back(InputBufferComponent::ClickEvent{ id, lastPos, button, true });
			else if (action == GLFW_RELEASE)
				g_buffer->clicks.push_back(InputBufferComponent::ClickEvent{ id, lastPos, button, false });
		}

		static void onMouseMove(GLFWwindow * window, double xpos, double ypos) noexcept {
			if (lastPos.x == FLT_MAX) {
				lastPos.x = (float)xpos;
				lastPos.y = (float)ypos;
			}

			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			InputBufferComponent::MouseMoveEvent info;
			info.window = id;
			info.pos = { (float)xpos, (float)ypos };
			info.rel = { (float)xpos - lastPos.x, (float)ypos - lastPos.y };
			lastPos = info.pos;

			g_buffer->moves.push_back(info);
		}

		static void onScroll(GLFWwindow * window, double xoffset, double yoffset) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);
			g_buffer->scrolls.push_back(InputBufferComponent::MouseScrollEvent{ id, (float)xoffset, (float)yoffset, lastPos });
		}
	}

	struct impl {
		static void init(Entity & e) noexcept {
			e += functions::Execute{ glfw::impl::execute };
			e += functions::OnEntityCreated{ glfw::impl::onEntityCreated };
			e += functions::OnTerminate{ glfw::impl::terminate };
			e += functions::OnMouseCaptured{ glfw::impl::onMouseCaptured };

			for (const auto & [e, buffer] : entities.with<InputBufferComponent>()) {
				Input::g_buffer = &buffer;
				break;
			}

			glfwInit();
			execute(0.f); // init already existing windows
		}

		static void terminate() noexcept {
			glfwTerminate();
		}

		static void onMouseCaptured(EntityID window, bool captured) noexcept {
			const auto inputMode = captured ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;

			if (captured)
				ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouse;
			else
				ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouse;

			if (window == INVALID_ID) {
				for (const auto & [e, glfw] : entities.with<GLFWWindowComponent>())
					glfwSetInputMode(glfw.window.get(), GLFW_CURSOR, inputMode);
				return;
			}

			const auto glfw = entities.get(window).tryGet<GLFWWindowComponent>();
			if (glfw == nullptr)
				return;

			glfwSetInputMode(glfw->window.get(), GLFW_CURSOR, inputMode);
		}

		static void onEntityCreated(Entity & e) noexcept {
			const auto window = e.tryGet<WindowComponent>();
			if (!window)
				return;

			const auto initGlfw = e.tryGet<GLFWWindowInitComponent>();
			if (!initGlfw)
				return;

			createWindow(e, *window, *initGlfw);
		}

		static void execute(float deltaTime) noexcept {
			for (const auto & [e, window, glfw] : entities.with<WindowComponent, GLFWWindowComponent>())
				if (glfwWindowShouldClose(glfw.window.get())) {
					if (window.shutdownOnClose)
						stopRunning();
					else
						entities.remove(e.id);
				}

			for (auto [e, window, initGlfw, noGLFW] : entities.with<WindowComponent, GLFWWindowInitComponent, no<GLFWWindowComponent>>()) {
				createWindow(e, window, initGlfw);
				e.detach<GLFWWindowInitComponent>();
			}
		}

		static void createWindow(Entity & e, WindowComponent & window, const GLFWWindowInitComponent & initGlfw) noexcept {
			auto & glfwComp = e.attach<GLFWWindowComponent>();

			if (initGlfw.setHints)
				initGlfw.setHints();

			// TODO: depend on g_windowComponent->fullscreen
			glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

			glfwComp.window = glfwCreateWindow((int)window.size.x, (int)window.size.y, window.name, nullptr, nullptr);
			// Desired size may not have been available, update to actual size
			int width, height;
			glfwGetWindowSize(glfwComp.window.get(), &width, &height);
			window.size = { (unsigned int)width, (unsigned int)height };
			glfwSetWindowAspectRatio(glfwComp.window.get(), window.size.x, window.size.y);

			glfwMakeContextCurrent(glfwComp.window.get());
			glfwSetWindowSizeCallback(glfwComp.window.get(), [](auto window, int width, int height) noexcept {
				const auto id = (EntityID)glfwGetWindowUserPointer(window);
				auto & comp = entities.get(id).get<WindowComponent>();
				comp.size = { (unsigned int)width, (unsigned int)height };
			});

			glfwSetMouseButtonCallback(glfwComp.window.get(), Input::onClick);
			glfwSetCursorPosCallback(glfwComp.window.get(), Input::onMouseMove);
			glfwSetScrollCallback(glfwComp.window.get(), Input::onScroll);
			glfwSetKeyCallback(glfwComp.window.get(), Input::onKey);

			glfwSetWindowUserPointer(glfwComp.window.get(), (void *)e.id);

			if (initGlfw.onWindowCreated)
				initGlfw.onWindowCreated();
		}
	};
}

namespace kengine {
	EntityCreator * GLFWSystem() noexcept {
		return [](Entity & e) noexcept {
			glfw::impl::init(e);
		};
	}
}<commit_msg>glfw -- style<commit_after>#include <GLFW/glfw3.h>

#include "GLFWSystem.hpp"
#include "kengine.hpp"

#include "data/InputBufferComponent.hpp"
#include "data/GLFWWindowComponent.hpp"
#include "data/WindowComponent.hpp"

#include "functions/OnTerminate.hpp"
#include "functions/OnMouseCaptured.hpp"
#include "functions/Execute.hpp"

#include "imgui.h"
#include "with.hpp"

namespace kengine {
	namespace Input {
		static InputBufferComponent * g_buffer;

		static void onKey(GLFWwindow * window, int key, int scancode, int action, int mods) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureKeyboard))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			if (action == GLFW_PRESS)
				g_buffer->keys.push_back(InputBufferComponent::KeyEvent{ id, key, true });
			else if (action == GLFW_RELEASE)
				g_buffer->keys.push_back(InputBufferComponent::KeyEvent{ id, key, false });
		}

		static putils::Point2f lastPos{ FLT_MAX, FLT_MAX };

		static void onClick(GLFWwindow * window, int button, int action, int mods) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			if (action == GLFW_PRESS)
				g_buffer->clicks.push_back(InputBufferComponent::ClickEvent{ id, lastPos, button, true });
			else if (action == GLFW_RELEASE)
				g_buffer->clicks.push_back(InputBufferComponent::ClickEvent{ id, lastPos, button, false });
		}

		static void onMouseMove(GLFWwindow * window, double xpos, double ypos) noexcept {
			if (lastPos.x == FLT_MAX) {
				lastPos.x = (float)xpos;
				lastPos.y = (float)ypos;
			}

			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);

			InputBufferComponent::MouseMoveEvent info;
			info.window = id;
			info.pos = { (float)xpos, (float)ypos };
			info.rel = { (float)xpos - lastPos.x, (float)ypos - lastPos.y };
			lastPos = info.pos;

			g_buffer->moves.push_back(info);
		}

		static void onScroll(GLFWwindow * window, double xoffset, double yoffset) noexcept {
			if (g_buffer == nullptr || (ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse))
				return;

			const auto id = (EntityID)glfwGetWindowUserPointer(window);
			g_buffer->scrolls.push_back(InputBufferComponent::MouseScrollEvent{ id, (float)xoffset, (float)yoffset, lastPos });
		}
	}

	EntityCreator * GLFWSystem() noexcept {
		struct impl {
			static void init(Entity & e) noexcept {
				e += functions::Execute{ execute };
				e += functions::OnEntityCreated{ onEntityCreated };
				e += functions::OnTerminate{ terminate };
				e += functions::OnMouseCaptured{ onMouseCaptured };

				for (const auto & [e, buffer] : entities.with<InputBufferComponent>()) {
					Input::g_buffer = &buffer;
					break;
				}

				glfwInit();
				execute(0.f); // init already existing windows
			}

			static void terminate() noexcept {
				glfwTerminate();
			}

			static void onMouseCaptured(EntityID window, bool captured) noexcept {
				const auto inputMode = captured ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;

				if (captured)
					ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouse;
				else
					ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouse;

				if (window == INVALID_ID) {
					for (const auto & [e, glfw] : entities.with<GLFWWindowComponent>())
						glfwSetInputMode(glfw.window.get(), GLFW_CURSOR, inputMode);
					return;
				}

				const auto glfw = entities.get(window).tryGet<GLFWWindowComponent>();
				if (glfw == nullptr)
					return;

				glfwSetInputMode(glfw->window.get(), GLFW_CURSOR, inputMode);
			}

			static void onEntityCreated(Entity & e) noexcept {
				const auto window = e.tryGet<WindowComponent>();
				if (!window)
					return;

				const auto initGlfw = e.tryGet<GLFWWindowInitComponent>();
				if (!initGlfw)
					return;

				createWindow(e, *window, *initGlfw);
			}

			static void execute(float deltaTime) noexcept {
				for (const auto & [e, window, glfw] : entities.with<WindowComponent, GLFWWindowComponent>())
					if (glfwWindowShouldClose(glfw.window.get())) {
						if (window.shutdownOnClose)
							stopRunning();
						else
							entities.remove(e.id);
					}

				for (auto [e, window, initGlfw, noGLFW] : entities.with<WindowComponent, GLFWWindowInitComponent, no<GLFWWindowComponent>>()) {
					createWindow(e, window, initGlfw);
					e.detach<GLFWWindowInitComponent>();
				}
			}

			static void createWindow(Entity & e, WindowComponent & window, const GLFWWindowInitComponent & initGlfw) noexcept {
				auto & glfwComp = e.attach<GLFWWindowComponent>();

				if (initGlfw.setHints)
					initGlfw.setHints();

				// TODO: depend on g_windowComponent->fullscreen
				glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

				glfwComp.window = glfwCreateWindow((int)window.size.x, (int)window.size.y, window.name, nullptr, nullptr);
				// Desired size may not have been available, update to actual size
				int width, height;
				glfwGetWindowSize(glfwComp.window.get(), &width, &height);
				window.size = { (unsigned int)width, (unsigned int)height };
				glfwSetWindowAspectRatio(glfwComp.window.get(), window.size.x, window.size.y);

				glfwMakeContextCurrent(glfwComp.window.get());
				glfwSetWindowSizeCallback(glfwComp.window.get(), [](auto window, int width, int height) noexcept {
					const auto id = (EntityID)glfwGetWindowUserPointer(window);
					auto & comp = entities.get(id).get<WindowComponent>();
					comp.size = { (unsigned int)width, (unsigned int)height };
					});

				glfwSetMouseButtonCallback(glfwComp.window.get(), Input::onClick);
				glfwSetCursorPosCallback(glfwComp.window.get(), Input::onMouseMove);
				glfwSetScrollCallback(glfwComp.window.get(), Input::onScroll);
				glfwSetKeyCallback(glfwComp.window.get(), Input::onKey);

				glfwSetWindowUserPointer(glfwComp.window.get(), (void *)e.id);

				if (initGlfw.onWindowCreated)
					initGlfw.onWindowCreated();
			}
		};

		return impl::init;
	}
}<|endoftext|>


<commit_before>#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
#include "barney_pilot.h"
#include "ros/ros.h"
#include <sensor_msgs/Joy.h>
#include "rxtx_server/distance.h"
#include "rxtx_server/AngularPos.h"
#include "rxtx_server/status.h"
#include "rxtx_server/command.h"
#include "osuar_telepilot/altitude_request.h"
#include "osuar_telepilot/altitude_command.h"
#include "osuar_telepilot/wall_command.h"
#include "osuar_telepilot/wall_request.h"
#include "osuar_telepilot/wall_command.h"
#include "osuar_telepilot/wall_request.h"
#include "rxtx_server/heading_pos.h"
#include "altitude.h"
#include "wall_follow.h"

int main(int argc, char ** argv){
	initscr();
	cbreak();
	noecho();
	timeout(0);

	ros::init(argc, argv, "Barney");

	status platStatus;
	joystick stick;
	distance lidar;
	angular_pos orientation;

	lidar.prev = 0;

	ros::NodeHandle n;
	ros::Publisher command = n.advertise<rxtx_server::command>("command", 1);
	rxtx_server::command commandData;

	ros::Publisher altitude_handler_pub = n.advertise<osuar_telepilot::altitude_request>("altitude_request", 1);
	osuar_telepilot::altitude_request altitude_request_data;
	altitude_request_data.p = 28;
	altitude_request_data.d = 0;
	altitude_request_data.i = 15;
	altitude_request_data.target = 40;
	altitude_request_data.status = STARTING;

	altitude_handler altitude_controller_data;

	ros::Publisher wall_handler_pub = n.advertise<osuar_telepilot::wall_request>("wall_request", 1);
	osuar_telepilot::wall_request wall_request_data;
	wall_request_data.p = 4;
	wall_request_data.i = 0;
	wall_request_data.d = 0;
	wall_request_data.target = 250;
	wall_request_data.status = FOLLOW;

	wall_handler wall_controller_data;

		char myStatus = 1;

		lidar.updateFlag = 0;
		lidar.prev = 0;

		platStatus.platStatus = 1;

		char prev = 20;

		int i;

		int throttle_little_buffer = 0;
		int throttle_big_buffer = 0;

		int roll_buffer = 0;

		ros::spinOnce();
		for(i=0;i<11;i++){
			stick.button[i] = 0;
		}
		for(i=0;i<5;i++){
			stick.axes[i] = -126;
		}

	char keyboard;
	char landingflag = 0;
	//char sent_status = 9;

	//printw("BARNEY TIME!\n");
	mvprintw(0,0,"STARTING   \n");
	while(1){
		ros::spinOnce();
		if((keyboard = getch()) != ERR){
			if(keyboard == 's'){
				endwin();
				return 0;
			}
			else if(keyboard == 'y'){
				altitude_request_data.p += 1;
			}
			else if(keyboard == 'h'){
				altitude_request_data.p -= 1;
			}
			else if(keyboard == 'u'){
				altitude_request_data.d += 1;
			}
			else if(keyboard == 'j'){
				altitude_request_data.d -= 1;
			}
			else if(keyboard == 'i'){
				altitude_request_data.i += 1;
			}
			else if(keyboard == 'k'){
				altitude_request_data.i -= 1;
			}

			//For starting takeoff
			else if((keyboard == 't') && ((altitude_request_data.status == STARTING) || (altitude_request_data.status == 7)) ){
				altitude_request_data.status = TAKEOFF;
				mvprintw(0,0,"TAKEOFF    \n");
			}
			else if((keyboard == 'l')){
				//altitude_request_data.status = LAND;
				landingflag = 1;
				altitude_request_data.target = 0;
				mvprintw(0,0,"LAND       \n");
			}
			else if(keyboard == 'r'){
				altitude_request_data.status = STARTING;
				mvprintw(0,0,"STARTING   \n");
			}

			mvprintw(15,40,"ALTITUDE:\n");
			mvprintw(16,40,"P: %4f\n",altitude_request_data.p * .2);
			mvprintw(17,40,"D: %4f\n",altitude_request_data.d * .2);
			mvprintw(18,40,"I: %4i/5625\n",altitude_request_data.i);

		}

		//TAKEOFF AND LAND AUTOMATIC STATUS CHANGES
		//When Taking Off, check to make transition to hover
		if((altitude_request_data.status == TAKEOFF) && (altitude_controller_data.status == HOVER)){
			altitude_request_data.status = HOVER;
			mvprintw(0,0,"HOVERING      \n");
		}
		else if((altitude_request_data.status == HOVER) && (altitude_controller_data.status == LAND) && (landingflag == 0)){
			altitude_request_data.status = TAKEOFF;
			mvprintw(0,0,"TAKEOFF    \n");

		}
		else if((landingflag == 1) && (altitude_controller_data.status == LAND)){
			landingflag = 0;
			altitude_request_data.status = LAND;
		}

		if(orientation.updateFlag){
			orientation.updateFlag = 0;
			mvprintw(2,0,"X: %4i\n",orientation.x);
			mvprintw(3,0,"Y: %4i\n",orientation.y);
			mvprintw(4,0,"Z: %4i\n",orientation.z);
		}

			myStatus = platStatus.platStatus;
			if(myStatus == 9){
				mvprintw(1,0,"booting  \n");

				//Don't have bad things happen when module resets
				//sent_status = 9;
			}
			else if(myStatus == 4){
				mvprintw(1,0,"stopping \n");
				altitude_request_data.status = 7;
				altitude_controller_data.throttle_big = 0;
				altitude_controller_data.throttle_little = 0;
			}
			else if(myStatus == 1){
				mvprintw(1,0,"running  \n");
			}
			else if(myStatus == 10){
				mvprintw(1,0,"offsetting\n");
			}

		if(lidar.updateFlag){
			lidar.updateFlag = 0;
			mvprintw(5,0,"Vertical: %4i\n", lidar.vertical);
			mvprintw(6,0,"Horizontal 1: %4i\n", lidar.horizontal_1);
			if((abs(orientation.x) < 200) && (abs(orientation.y) < 200)){
				altitude_request_data.distance = lidar.vertical;
				altitude_handler_pub.publish(altitude_request_data);
				wall_request_data.distance = lidar.horizontal_1;
				wall_handler_pub.publish(wall_request_data);
			}
			else{
				//altitude_controller_data.throttle_big = 0;
				//altitude_controller_data.throttle_little = 0;

			}
		}
		throttle_little_buffer = stick.axes[2] + altitude_controller_data.throttle_little;
		throttle_big_buffer = stick.axes[4] + altitude_controller_data.throttle_big;

		//Will eventually be phased out of this section of code and put in altitude
/*
		if(throttle_big_buffer > 127){
			throttle_big_buffer = 127;
		}
		else if(throttle_big_buffer < -127){
			throttle_big_buffer = -127;
		}
		if(throttle_little_buffer > 127){
			if(throttle_big_buffer < 94){
				throttle_little_buffer -= 127;
				throttle_big_buffer += 32;
			}
			else{
				throttle_little_buffer = 127;
			}
		}
		else if(throttle_little_buffer < -127){
			if(throttle_big_buffer > -94){
				throttle_little_buffer += 127;
				throttle_big_buffer -= 32;
			}
			else{
				throttle_little_buffer = -127;
			}
		}
*/
/*
		throttle_overflow = throttle_little_buffer/127;
		throttle_little_buffer %= 127;
		throttle_big_buffer += throttle_overflow * 32;
*/
		while(throttle_little_buffer > 127){
			throttle_little_buffer -= 127;
			throttle_big_buffer += 32;
		}
		while(throttle_little_buffer < -127){
			throttle_little_buffer += 127;
			throttle_big_buffer -= 32;
		}

		if(throttle_big_buffer > 127){
			throttle_big_buffer = 127;
		}
		else if(throttle_big_buffer < -127){
			throttle_big_buffer = -127;
			throttle_little_buffer = -127;
		}
		roll_buffer = stick.axes[0];//+ wall_controller_data.tilt;
		if(roll_buffer > 127){
			roll_buffer = 127;
		}
		else if(roll_buffer < -127){
			roll_buffer = -127;
		}

		commandData.roll = char(roll_buffer) + 127;
		commandData.pitch = char(stick.axes[1]) + 127;
		commandData.throttleLittle = char(throttle_little_buffer) + 127;
		commandData.yaw = char(stick.axes[3]) + 127;
		commandData.throttleBig = char(throttle_big_buffer) + 127;

		commandData.button = 20;
		for(i = 0; i < 11; i ++){
			if(stick.button[i]){
				commandData.button = i;
			}
		}

		//For status alignment varification
/*
		if(commandData.button == 9){
			sent_status = 9;
		}
		else if(commandData.button == 4){
			sent_status = 4;
		}
		else if(commandData.button == 1){
			sent_status = 1;
		}
		else if(commandData.button == 10){
			sent_status = 10;
		}
		else if(sent_status != myStatus){
			commandData.button = sent_status;
		}
*/
		/*Make sure it doesn't repeat any commands but stop and reset*/
		if((commandData.button == prev) && (prev != 4) && (prev != 9)){
			commandData.button = 20;
		}
		else{
			prev = commandData.button;
		}

		mvprintw(10,0,"pitch: %4d\n", commandData.pitch);
		mvprintw(11,0,"roll: %4d\n", commandData.roll);
		mvprintw(12,0,"yaw: %4d\n", commandData.yaw);
		mvprintw(13,0,"throttleLittle: %4d\n", commandData.throttleLittle);
		mvprintw(14,0,"throttleBig: %4d\n", commandData.throttleBig);
		mvprintw(15,0,"throttleLittle Buffer: %4d", stick.axes[2]);
		mvprintw(16,0,"throttleBig Buffer: %4d", stick.axes[4]);

		command.publish(commandData);

		usleep(16600);

	}
}

<commit_msg>Fixed bug where landing put target at 0 but didn't reset<commit_after>#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
#include "barney_pilot.h"
#include "ros/ros.h"
#include <sensor_msgs/Joy.h>
#include "rxtx_server/distance.h"
#include "rxtx_server/AngularPos.h"
#include "rxtx_server/status.h"
#include "rxtx_server/command.h"
#include "osuar_telepilot/altitude_request.h"
#include "osuar_telepilot/altitude_command.h"
#include "osuar_telepilot/wall_command.h"
#include "osuar_telepilot/wall_request.h"
#include "osuar_telepilot/wall_command.h"
#include "osuar_telepilot/wall_request.h"
#include "rxtx_server/heading_pos.h"
#include "altitude.h"
#include "wall_follow.h"

int main(int argc, char ** argv){
	initscr();
	cbreak();
	noecho();
	timeout(0);

	ros::init(argc, argv, "Barney");

	status platStatus;
	joystick stick;
	distance lidar;
	angular_pos orientation;

	lidar.prev = 0;

	ros::NodeHandle n;
	ros::Publisher command = n.advertise<rxtx_server::command>("command", 1);
	rxtx_server::command commandData;

	ros::Publisher altitude_handler_pub = n.advertise<osuar_telepilot::altitude_request>("altitude_request", 1);
	osuar_telepilot::altitude_request altitude_request_data;
	altitude_request_data.p = 28;
	altitude_request_data.d = 0;
	altitude_request_data.i = 15;
	altitude_request_data.target = 40;
	altitude_request_data.status = STARTING;

	altitude_handler altitude_controller_data;

	ros::Publisher wall_handler_pub = n.advertise<osuar_telepilot::wall_request>("wall_request", 1);
	osuar_telepilot::wall_request wall_request_data;
	wall_request_data.p = 4;
	wall_request_data.i = 0;
	wall_request_data.d = 0;
	wall_request_data.target = 250;
	wall_request_data.status = FOLLOW;

	wall_handler wall_controller_data;

		char myStatus = 1;

		lidar.updateFlag = 0;
		lidar.prev = 0;

		platStatus.platStatus = 1;

		char prev = 20;

		int i;

		int throttle_little_buffer = 0;
		int throttle_big_buffer = 0;

		int roll_buffer = 0;

		ros::spinOnce();
		for(i=0;i<11;i++){
			stick.button[i] = 0;
		}
		for(i=0;i<5;i++){
			stick.axes[i] = -126;
		}

	char keyboard;
	char landingflag = 0;
	//char sent_status = 9;

	//printw("BARNEY TIME!\n");
	mvprintw(0,0,"STARTING   \n");
	while(1){
		ros::spinOnce();
		if((keyboard = getch()) != ERR){
			if(keyboard == 's'){
				endwin();
				return 0;
			}
			else if(keyboard == 'y'){
				altitude_request_data.p += 1;
			}
			else if(keyboard == 'h'){
				altitude_request_data.p -= 1;
			}
			else if(keyboard == 'u'){
				altitude_request_data.d += 1;
			}
			else if(keyboard == 'j'){
				altitude_request_data.d -= 1;
			}
			else if(keyboard == 'i'){
				altitude_request_data.i += 1;
			}
			else if(keyboard == 'k'){
				altitude_request_data.i -= 1;
			}

			//For starting takeoff
			else if((keyboard == 't') && ((altitude_request_data.status == STARTING) || (altitude_request_data.status == 7)) ){
				altitude_request_data.status = TAKEOFF;
				mvprintw(0,0,"TAKEOFF    \n");
			}
			else if((keyboard == 'l')){
				//altitude_request_data.status = LAND;
				landingflag = 1;
				altitude_request_data.target = 0;
				mvprintw(0,0,"LAND       \n");
			}
			else if(keyboard == 'r'){
				altitude_request_data.target = 40;
				altitude_request_data.status = STARTING;
				mvprintw(0,0,"STARTING   \n");
			}

			mvprintw(15,40,"ALTITUDE:\n");
			mvprintw(16,40,"P: %4f\n",altitude_request_data.p * .2);
			mvprintw(17,40,"D: %4f\n",altitude_request_data.d * .2);
			mvprintw(18,40,"I: %4i/5625\n",altitude_request_data.i);

		}

		//TAKEOFF AND LAND AUTOMATIC STATUS CHANGES
		//When Taking Off, check to make transition to hover
		if((altitude_request_data.status == TAKEOFF) && (altitude_controller_data.status == HOVER)){
			altitude_request_data.status = HOVER;
			mvprintw(0,0,"HOVERING      \n");
		}
		else if((altitude_request_data.status == HOVER) && (altitude_controller_data.status == LAND) && (landingflag == 0)){
			altitude_request_data.status = TAKEOFF;
			mvprintw(0,0,"TAKEOFF    \n");

		}
		else if((landingflag == 1) && (altitude_controller_data.status == LAND)){
			landingflag = 0;
			altitude_request_data.status = LAND;
		}

		if(orientation.updateFlag){
			orientation.updateFlag = 0;
			mvprintw(2,0,"X: %4i\n",orientation.x);
			mvprintw(3,0,"Y: %4i\n",orientation.y);
			mvprintw(4,0,"Z: %4i\n",orientation.z);
		}

			myStatus = platStatus.platStatus;
			if(myStatus == 9){
				mvprintw(1,0,"booting  \n");

				//Don't have bad things happen when module resets
				//sent_status = 9;
			}
			else if(myStatus == 4){
				mvprintw(1,0,"stopping \n");
				altitude_request_data.status = 7;
				altitude_controller_data.throttle_big = 0;
				altitude_controller_data.throttle_little = 0;
			}
			else if(myStatus == 1){
				mvprintw(1,0,"running  \n");
			}
			else if(myStatus == 10){
				mvprintw(1,0,"offsetting\n");
			}

		if(lidar.updateFlag){
			lidar.updateFlag = 0;
			mvprintw(5,0,"Vertical: %4i\n", lidar.vertical);
			mvprintw(6,0,"Horizontal 1: %4i\n", lidar.horizontal_1);
			if((abs(orientation.x) < 200) && (abs(orientation.y) < 200)){
				altitude_request_data.distance = lidar.vertical;
				altitude_handler_pub.publish(altitude_request_data);
				wall_request_data.distance = lidar.horizontal_1;
				wall_handler_pub.publish(wall_request_data);
			}
			else{
				//altitude_controller_data.throttle_big = 0;
				//altitude_controller_data.throttle_little = 0;

			}
		}
		throttle_little_buffer = stick.axes[2] + altitude_controller_data.throttle_little;
		throttle_big_buffer = stick.axes[4] + altitude_controller_data.throttle_big;

		//Will eventually be phased out of this section of code and put in altitude
/*
		if(throttle_big_buffer > 127){
			throttle_big_buffer = 127;
		}
		else if(throttle_big_buffer < -127){
			throttle_big_buffer = -127;
		}
		if(throttle_little_buffer > 127){
			if(throttle_big_buffer < 94){
				throttle_little_buffer -= 127;
				throttle_big_buffer += 32;
			}
			else{
				throttle_little_buffer = 127;
			}
		}
		else if(throttle_little_buffer < -127){
			if(throttle_big_buffer > -94){
				throttle_little_buffer += 127;
				throttle_big_buffer -= 32;
			}
			else{
				throttle_little_buffer = -127;
			}
		}
*/
/*
		throttle_overflow = throttle_little_buffer/127;
		throttle_little_buffer %= 127;
		throttle_big_buffer += throttle_overflow * 32;
*/
		while(throttle_little_buffer > 127){
			throttle_little_buffer -= 127;
			throttle_big_buffer += 32;
		}
		while(throttle_little_buffer < -127){
			throttle_little_buffer += 127;
			throttle_big_buffer -= 32;
		}

		if(throttle_big_buffer > 127){
			throttle_big_buffer = 127;
		}
		else if(throttle_big_buffer < -127){
			throttle_big_buffer = -127;
			throttle_little_buffer = -127;
		}
		roll_buffer = stick.axes[0];//+ wall_controller_data.tilt;
		if(roll_buffer > 127){
			roll_buffer = 127;
		}
		else if(roll_buffer < -127){
			roll_buffer = -127;
		}

		commandData.roll = char(roll_buffer) + 127;
		commandData.pitch = char(stick.axes[1]) + 127;
		commandData.throttleLittle = char(throttle_little_buffer) + 127;
		commandData.yaw = char(stick.axes[3]) + 127;
		commandData.throttleBig = char(throttle_big_buffer) + 127;

		commandData.button = 20;
		for(i = 0; i < 11; i ++){
			if(stick.button[i]){
				commandData.button = i;
			}
		}

		//For status alignment varification
/*
		if(commandData.button == 9){
			sent_status = 9;
		}
		else if(commandData.button == 4){
			sent_status = 4;
		}
		else if(commandData.button == 1){
			sent_status = 1;
		}
		else if(commandData.button == 10){
			sent_status = 10;
		}
		else if(sent_status != myStatus){
			commandData.button = sent_status;
		}
*/
		/*Make sure it doesn't repeat any commands but stop and reset*/
		if((commandData.button == prev) && (prev != 4) && (prev != 9)){
			commandData.button = 20;
		}
		else{
			prev = commandData.button;
		}

		mvprintw(10,0,"pitch: %4d\n", commandData.pitch);
		mvprintw(11,0,"roll: %4d\n", commandData.roll);
		mvprintw(12,0,"yaw: %4d\n", commandData.yaw);
		mvprintw(13,0,"throttleLittle: %4d\n", commandData.throttleLittle);
		mvprintw(14,0,"throttleBig: %4d\n", commandData.throttleBig);
		mvprintw(15,0,"throttleLittle Buffer: %4d", stick.axes[2]);
		mvprintw(16,0,"throttleBig Buffer: %4d", stick.axes[4]);

		command.publish(commandData);

		usleep(16600);

	}
}

<|endoftext|>


<commit_before>#include "sqlitewrapper/exceptions.h"

#include "result_names.h"
#include "sqlitewrapper/sqlite3.h"

namespace SqliteWrapper {

Exception::Exception(const std::string &message) throw()
    : m_message(message)
{
}

const char *Exception::what() const throw()
{
    return m_message.c_str();
}

SqliteException::SqliteException(int resultCode)
    : Exception(
          resultToResultName(resultCode) + ": " +
          sqlite3_errstr(resultCode)
          )
{
}

SqliteException::SqliteException(int resultCode, const std::string &message)
    : Exception(
          resultToResultName(resultCode) + ": " +
          sqlite3_errstr(resultCode) + "\n" +
          message
          )
{
}

}
<commit_msg>SqliteException: improve result code formatting<commit_after>#include "sqlitewrapper/exceptions.h"

#include "result_names.h"
#include "sqlitewrapper/sqlite3.h"

namespace SqliteWrapper {

Exception::Exception(const std::string &message) throw()
    : m_message(message)
{
}

const char *Exception::what() const throw()
{
    return m_message.c_str();
}

SqliteException::SqliteException(int resultCode)
    : Exception(
          resultToResultName(resultCode) +
          " (" + sqlite3_errstr(resultCode) + ")"
          )
{
}

SqliteException::SqliteException(int resultCode, const std::string &message)
    : Exception(
          resultToResultName(resultCode) +
          " (" + sqlite3_errstr(resultCode) + "): " +
          message
          )
{
}

}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2020 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include "caf/deserializer.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"

#include <memory>
#include <stack>
#include <vector>

namespace caf {

/// Extracts objects from a @ref config_value.
class CAF_CORE_EXPORT config_value_reader final : public deserializer {
public:
  // -- member types------------------------------------------------------------

  using super = deserializer;

  using key_ptr = const std::string*;

  struct absent_field {};

  struct sequence {
    using list_pointer = const std::vector<config_value>*;
    size_t index;
    list_pointer ls;
    explicit sequence(list_pointer ls) : index(0), ls(ls) {
      // nop
    }
    bool at_end() const noexcept;
    const config_value& current();
    void advance() {
      ++index;
    }
  };

  struct associative_array {
    settings::const_iterator pos;
    settings::const_iterator end;
    bool at_end() const noexcept;
    const std::pair<const std::string, config_value>& current();
  };

  using value_type = variant<const settings*, const config_value*, key_ptr,
                             absent_field, sequence, associative_array>;

  using stack_type = std::stack<value_type, std::vector<value_type>>;

  // -- constructors, destructors, and assignment operators --------------------

  config_value_reader(const config_value* input, actor_system& sys)
    : super(sys) {
    st_.push(input);
    has_human_readable_format_ = true;
  }

  config_value_reader(const config_value* input, execution_unit* ctx)
    : super(ctx) {
    st_.push(input);
    has_human_readable_format_ = true;
  }
  explicit config_value_reader(const config_value* input)
    : config_value_reader(input, nullptr) {
    // nop
  }

  ~config_value_reader() override;

  // -- stack access -----------------------------------------------------------

  value_type& top() {
    return st_.top();
  }

  void pop() {
    return st_.pop();
  }

  // -- interface functions ----------------------------------------------------

  bool fetch_next_object_type(type_id_t& type) override;

  bool begin_object(type_id_t type, string_view name) override;

  bool end_object() override;

  bool begin_field(string_view) override;

  bool begin_field(string_view name, bool& is_present) override;

  bool begin_field(string_view name, span<const type_id_t> types,
                   size_t& index) override;

  bool begin_field(string_view name, bool& is_present,
                   span<const type_id_t> types, size_t& index) override;

  bool end_field() override;

  bool begin_tuple(size_t size) override;

  bool end_tuple() override;

  bool begin_key_value_pair() override;

  bool end_key_value_pair() override;

  bool begin_sequence(size_t& size) override;

  bool end_sequence() override;

  bool begin_associative_array(size_t& size) override;

  bool end_associative_array() override;

  bool value(byte& x) override;

  bool value(bool& x) override;

  bool value(int8_t& x) override;

  bool value(uint8_t& x) override;

  bool value(int16_t& x) override;

  bool value(uint16_t& x) override;

  bool value(int32_t& x) override;

  bool value(uint32_t& x) override;

  bool value(int64_t& x) override;

  bool value(uint64_t& x) override;

  bool value(float& x) override;

  bool value(double& x) override;

  bool value(long double& x) override;

  bool value(std::string& x) override;

  bool value(std::u16string& x) override;

  bool value(std::u32string& x) override;

  bool value(span<byte> x) override;

private:
  bool fetch_object_type(const settings* obj, type_id_t& type);

  stack_type st_;

  // Stores on-the-fly converted values.
  std::vector<std::unique_ptr<config_value>> scratch_space_;
};

} // namespace caf
<commit_msg>Fix build on MSVC<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2020 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include "caf/deserializer.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"

#include <memory>
#include <stack>
#include <vector>

namespace caf {

/// Extracts objects from a @ref config_value.
class CAF_CORE_EXPORT config_value_reader final : public deserializer {
public:
  // -- member types------------------------------------------------------------

  using super = deserializer;

  using key_ptr = const std::string*;

  struct absent_field {};

  struct sequence {
    using list_pointer = const std::vector<config_value>*;
    size_t index;
    list_pointer ls;
    explicit sequence(list_pointer ls) : index(0), ls(ls) {
      // nop
    }
    bool at_end() const noexcept;
    const config_value& current();
    void advance() {
      ++index;
    }
  };

  struct associative_array {
    settings::const_iterator pos;
    settings::const_iterator end;
    bool at_end() const noexcept;
    const std::pair<const std::string, config_value>& current();
  };

  using value_type = variant<const settings*, const config_value*, key_ptr,
                             absent_field, sequence, associative_array>;

  using stack_type = std::stack<value_type, std::vector<value_type>>;

  // -- constructors, destructors, and assignment operators --------------------

  config_value_reader(const config_value* input, actor_system& sys)
    : super(sys) {
    st_.push(input);
    has_human_readable_format_ = true;
  }

  config_value_reader(const config_value* input, execution_unit* ctx)
    : super(ctx) {
    st_.push(input);
    has_human_readable_format_ = true;
  }
  explicit config_value_reader(const config_value* input)
    : config_value_reader(input, nullptr) {
    // nop
  }

  ~config_value_reader() override;

  config_value_reader(const config_value_reader&) = delete;

  config_value_reader& operator=(const config_value_reader&) = delete;

  // -- stack access -----------------------------------------------------------

  value_type& top() {
    return st_.top();
  }

  void pop() {
    return st_.pop();
  }

  // -- interface functions ----------------------------------------------------

  bool fetch_next_object_type(type_id_t& type) override;

  bool begin_object(type_id_t type, string_view name) override;

  bool end_object() override;

  bool begin_field(string_view) override;

  bool begin_field(string_view name, bool& is_present) override;

  bool begin_field(string_view name, span<const type_id_t> types,
                   size_t& index) override;

  bool begin_field(string_view name, bool& is_present,
                   span<const type_id_t> types, size_t& index) override;

  bool end_field() override;

  bool begin_tuple(size_t size) override;

  bool end_tuple() override;

  bool begin_key_value_pair() override;

  bool end_key_value_pair() override;

  bool begin_sequence(size_t& size) override;

  bool end_sequence() override;

  bool begin_associative_array(size_t& size) override;

  bool end_associative_array() override;

  bool value(byte& x) override;

  bool value(bool& x) override;

  bool value(int8_t& x) override;

  bool value(uint8_t& x) override;

  bool value(int16_t& x) override;

  bool value(uint16_t& x) override;

  bool value(int32_t& x) override;

  bool value(uint32_t& x) override;

  bool value(int64_t& x) override;

  bool value(uint64_t& x) override;

  bool value(float& x) override;

  bool value(double& x) override;

  bool value(long double& x) override;

  bool value(std::string& x) override;

  bool value(std::u16string& x) override;

  bool value(std::u32string& x) override;

  bool value(span<byte> x) override;

private:
  bool fetch_object_type(const settings* obj, type_id_t& type);

  stack_type st_;

  // Stores on-the-fly converted values.
  std::vector<std::unique_ptr<config_value>> scratch_space_;
};

} // namespace caf
<|endoftext|>


<commit_before>/*
 * Stack.cpp
 *
 *  Created on: 24 Oct 2015
 *      Author: hieu
 */
#include <algorithm>
#include <boost/foreach.hpp>
#include "Stack.h"
#include "../Hypothesis.h"
#include "../Manager.h"
#include "../../Scores.h"
#include "../../System.h"

using namespace std;

namespace Moses2
{

namespace NSCubePruningCardinalStack
{

///////////////////////////////////////////////////////////////
Stack::Stack(const Manager &mgr)
:m_mgr(mgr)
,m_coll(MemPoolAllocator<const Hypothesis*>(mgr.GetPool()))
{
}

Stack::~Stack() {
	// TODO Auto-generated destructor stub
}

void Stack::Add(const Hypothesis *hypo, Recycler<Hypothesis*> &hypoRecycle)
{
  std::pair<_HCType::iterator, bool> addRet = m_coll.insert(hypo);

  // CHECK RECOMBINATION
  if (addRet.second) {
	// equiv hypo doesn't exists
  }
  else {
	  const Hypothesis *hypoExisting = *addRet.first;
	  if (hypo->GetScores().GetTotalScore() > hypoExisting->GetScores().GetTotalScore()) {
		  // incoming hypo is better than the one we have
		  const Hypothesis *const &hypoExisting1 = *addRet.first;
		  const Hypothesis *&hypoExisting2 = const_cast<const Hypothesis *&>(hypoExisting1);
		  hypoExisting2 = hypo;

		  //hypoExisting->Prefetch();
		  Hypothesis *hypoToBeDeleted = const_cast<Hypothesis*>(hypoExisting);
		  hypoRecycle.Recycle(hypoToBeDeleted);
	  }
	  else {
		  // already storing the best hypo. discard incoming hypo
		  Hypothesis *hypoToBeDeleted = const_cast<Hypothesis*>(hypo);
		  hypoRecycle.Recycle(hypoToBeDeleted);
	  }
  }
}

std::vector<const Hypothesis*> Stack::GetBestHypos(size_t num) const
{
  std::vector<const Hypothesis*> ret;
  ret.insert(ret.end(), m_coll.begin(), m_coll.end());

  std::vector<const Hypothesis*>::iterator iterMiddle;
  iterMiddle = (num == 0 || ret.size() < num)
			   ? ret.end()
			   : ret.begin()+num;

  std::partial_sort(ret.begin(), iterMiddle, ret.end(),
		  HypothesisFutureScoreOrderer());

  return ret;
}

size_t Stack::GetHypoSize() const
{
	return m_coll.size();
}

void Stack::Clear()
{

	m_coll.clear();
}

Stack::SortedHypos Stack::GetSortedAndPruneHypos(const Manager &mgr) const
{
  typedef boost::unordered_map<HypoCoverage, vector<const Hypothesis*> > SortedHypos2;
  SortedHypos2 ret2;

  MemPool &pool = mgr.GetPool();

  // divide hypos by [bitmap, last end pos]
  BOOST_FOREACH(const Hypothesis *hypo, m_coll) {
	  HypoCoverage key(&hypo->GetBitmap(), hypo->GetInputPath().range.GetEndPos());

	  vector<const Hypothesis*> &hypos = ret2[key];
	  hypos.push_back(hypo);
  }

  // put into real return variable and sort
  SortedHypos ret;
  BOOST_FOREACH(SortedHypos2::value_type &val, ret2) {
	  const vector<const Hypothesis*> &hypos2 = val.second;
	  Hypotheses *hypos = new (pool.Allocate<Hypotheses>()) Hypotheses(pool, hypos2.size());

	  for (size_t i = 0; i < hypos2.size(); ++i) {
		  (*hypos)[i] = hypos2[i];
	  }

	  SortAndPruneHypos(mgr, *hypos);

	  ret[val.first] = hypos;
  }

  return ret;
}

void Stack::SortAndPruneHypos(const Manager &mgr, Hypotheses &hypos) const
{
  size_t stackSize = mgr.system.stackSize;
  Recycler<Hypothesis*> &recycler = mgr.GetHypoRecycle();

  /*
  cerr << "UNSORTED hypos:" << endl;
  for (size_t i = 0; i < hypos.size(); ++i) {
	  const Hypothesis *hypo = hypos[i];
	  cerr << *hypo << endl;
  }
  cerr << endl;
  */
  Hypotheses::iterator iterMiddle;
  iterMiddle = (stackSize == 0 || hypos.size() < stackSize)
			   ? hypos.end()
			   : hypos.begin() + stackSize;

  std::partial_sort(hypos.begin(), iterMiddle, hypos.end(),
		  HypothesisFutureScoreOrderer());

  // prune
  if (stackSize && hypos.size() > stackSize) {
	  for (size_t i = stackSize; i < hypos.size(); ++i) {
		  Hypothesis *hypo = const_cast<Hypothesis*>(hypos[i]);
		  recycler.Recycle(hypo);
	  }
	  hypos.resize(stackSize);
  }

  /*
  cerr << "sorted hypos:" << endl;
  for (size_t i = 0; i < hypos.size(); ++i) {
	  const Hypothesis *hypo = hypos[i];
	  cerr << hypo << " " << *hypo << endl;
  }
  cerr << endl;
  */

}

}

}

<commit_msg>don't copy from vector to Hypotheses<commit_after>/*
 * Stack.cpp
 *
 *  Created on: 24 Oct 2015
 *      Author: hieu
 */
#include <algorithm>
#include <boost/foreach.hpp>
#include "Stack.h"
#include "../Hypothesis.h"
#include "../Manager.h"
#include "../../Scores.h"
#include "../../System.h"

using namespace std;

namespace Moses2
{

namespace NSCubePruningCardinalStack
{

///////////////////////////////////////////////////////////////
Stack::Stack(const Manager &mgr)
:m_mgr(mgr)
,m_coll(MemPoolAllocator<const Hypothesis*>(mgr.GetPool()))
{
}

Stack::~Stack() {
	// TODO Auto-generated destructor stub
}

void Stack::Add(const Hypothesis *hypo, Recycler<Hypothesis*> &hypoRecycle)
{
  std::pair<_HCType::iterator, bool> addRet = m_coll.insert(hypo);

  // CHECK RECOMBINATION
  if (addRet.second) {
	// equiv hypo doesn't exists
  }
  else {
	  const Hypothesis *hypoExisting = *addRet.first;
	  if (hypo->GetScores().GetTotalScore() > hypoExisting->GetScores().GetTotalScore()) {
		  // incoming hypo is better than the one we have
		  const Hypothesis *const &hypoExisting1 = *addRet.first;
		  const Hypothesis *&hypoExisting2 = const_cast<const Hypothesis *&>(hypoExisting1);
		  hypoExisting2 = hypo;

		  //hypoExisting->Prefetch();
		  Hypothesis *hypoToBeDeleted = const_cast<Hypothesis*>(hypoExisting);
		  hypoRecycle.Recycle(hypoToBeDeleted);
	  }
	  else {
		  // already storing the best hypo. discard incoming hypo
		  Hypothesis *hypoToBeDeleted = const_cast<Hypothesis*>(hypo);
		  hypoRecycle.Recycle(hypoToBeDeleted);
	  }
  }
}

std::vector<const Hypothesis*> Stack::GetBestHypos(size_t num) const
{
  std::vector<const Hypothesis*> ret;
  ret.insert(ret.end(), m_coll.begin(), m_coll.end());

  std::vector<const Hypothesis*>::iterator iterMiddle;
  iterMiddle = (num == 0 || ret.size() < num)
			   ? ret.end()
			   : ret.begin()+num;

  std::partial_sort(ret.begin(), iterMiddle, ret.end(),
		  HypothesisFutureScoreOrderer());

  return ret;
}

size_t Stack::GetHypoSize() const
{
	return m_coll.size();
}

void Stack::Clear()
{

	m_coll.clear();
}

Stack::SortedHypos Stack::GetSortedAndPruneHypos(const Manager &mgr) const
{
  SortedHypos ret;

  MemPool &pool = mgr.GetPool();

  // divide hypos by [bitmap, last end pos]
  BOOST_FOREACH(const Hypothesis *hypo, m_coll) {
	  HypoCoverage key(&hypo->GetBitmap(), hypo->GetInputPath().range.GetEndPos());

	  Hypotheses *hypos;
	  SortedHypos::iterator iter;
	  iter = ret.find(key);
	  if (iter == ret.end()) {
		  hypos = new (pool.Allocate<Hypotheses>()) Hypotheses(pool, 0);
		  ret[key] = hypos;
	  }
	  else {
		  hypos = iter->second;
	  }
	  hypos->push_back(hypo);
  }

  // put into real return variable and sort
  BOOST_FOREACH(SortedHypos::value_type &val, ret) {
	  Hypotheses &hypos = *val.second;
	  SortAndPruneHypos(mgr, hypos);
  }

  return ret;
}

void Stack::SortAndPruneHypos(const Manager &mgr, Hypotheses &hypos) const
{
  size_t stackSize = mgr.system.stackSize;
  Recycler<Hypothesis*> &recycler = mgr.GetHypoRecycle();

  /*
  cerr << "UNSORTED hypos:" << endl;
  for (size_t i = 0; i < hypos.size(); ++i) {
	  const Hypothesis *hypo = hypos[i];
	  cerr << *hypo << endl;
  }
  cerr << endl;
  */
  Hypotheses::iterator iterMiddle;
  iterMiddle = (stackSize == 0 || hypos.size() < stackSize)
			   ? hypos.end()
			   : hypos.begin() + stackSize;

  std::partial_sort(hypos.begin(), iterMiddle, hypos.end(),
		  HypothesisFutureScoreOrderer());

  // prune
  if (stackSize && hypos.size() > stackSize) {
	  for (size_t i = stackSize; i < hypos.size(); ++i) {
		  Hypothesis *hypo = const_cast<Hypothesis*>(hypos[i]);
		  recycler.Recycle(hypo);
	  }
	  hypos.resize(stackSize);
  }

  /*
  cerr << "sorted hypos:" << endl;
  for (size_t i = 0; i < hypos.size(); ++i) {
	  const Hypothesis *hypo = hypos[i];
	  cerr << hypo << " " << *hypo << endl;
  }
  cerr << endl;
  */

}

}

}

<|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 "Stock.hxx"
#include "Error.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_stock.hxx"
#include "spawn/Prepared.hxx"
#include "spawn/ChildOptions.hxx"
#include "spawn/JailParams.hxx"
#include "spawn/JailConfig.hxx"
#include "pool/tpool.hxx"
#include "AllocatorPtr.hxx"
#include "event/SocketEvent.hxx"
#include "event/Duration.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/ConstBuffer.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"
#include "util/StringFormat.hxx"

#include <string>

#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#ifdef __linux
#include <sched.h>
#endif

struct FcgiStock final : StockClass, ChildStockClass {
    StockMap hstock;
    ChildStock child_stock;

    FcgiStock(unsigned limit, unsigned max_idle,
              EventLoop &event_loop, SpawnService &spawn_service,
              SocketDescriptor _log_socket) noexcept;

    ~FcgiStock() {
        /* this one must be cleared before #child_stock; FadeAll()
           calls ClearIdle(), so this method is the best match for
           what we want to do (though a kludge) */
        hstock.FadeAll();
    }

    EventLoop &GetEventLoop() {
        return hstock.GetEventLoop();
    }

    SocketDescriptor GetLogSocket() const noexcept {
        return child_stock.GetLogSocket();
    }

    void FadeAll() {
        hstock.FadeAll();
        child_stock.GetStockMap().FadeAll();
    }

    void FadeTag(const char *tag);

    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *info, struct pool &caller_pool,
                CancellablePointer &cancel_ptr) override;

    /* virtual methods from class ChildStockClass */
    const char *GetChildTag(void *info) const noexcept override;
    void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                      PreparedChildProcess &p) override;
};

struct FcgiChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;

    const ChildOptions &options;

    FcgiChildParams(const char *_executable_path,
                    ConstBuffer<const char *> _args,
                    const ChildOptions &_options)
        :executable_path(_executable_path), args(_args),
         options(_options) {}

    const char *GetStockKey(struct pool &pool) const;
};

struct FcgiConnection final : StockItem {
    const LLogger logger;

    std::string jail_home_directory;

    JailConfig jail_config;

    StockItem *child = nullptr;

    UniqueSocketDescriptor fd;
    SocketEvent event;

    /**
     * Is this a fresh connection to the FastCGI child process?
     */
    bool fresh = true;

    /**
     * Shall the FastCGI child process be killed?
     */
    bool kill = false;

    /**
     * Was the current request aborted by the fcgi_client caller?
     */
    bool aborted = false;

    explicit FcgiConnection(EventLoop &event_loop, CreateStockItem c)
        :StockItem(c), logger(GetStockName()),
         event(event_loop, BIND_THIS_METHOD(OnSocketEvent)) {}

    ~FcgiConnection() override;

    gcc_pure
    const char *GetTag() const {
        assert(child != nullptr);

        return child_stock_item_get_tag(*child);
    }

    void SetSite(const char *site) noexcept {
        child_stock_item_set_site(*child, site);
    }

    void SetUri(const char *uri) noexcept {
        child_stock_item_set_uri(*child, uri);
    }

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override;
    bool Release() noexcept override;

private:
    void OnSocketEvent(unsigned events);
};

const char *
FcgiChildParams::GetStockKey(struct pool &pool) const
{
    const char *key = executable_path;

    for (auto i : args)
        key = p_strcat(&pool, key, " ", i, nullptr);

    for (auto i : options.env)
        key = p_strcat(&pool, key, "$", i, nullptr);

    char options_buffer[16384];
    *options.MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(&pool, key, options_buffer, nullptr);

    return key;
}

/*
 * libevent callback
 *
 */

void
FcgiConnection::OnSocketEvent(unsigned events)
{
    if ((events & SocketEvent::TIMEOUT) == 0) {
        char buffer;
        ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
        if (nbytes < 0)
            logger(2, "error on idle FastCGI connection: ", strerror(errno));
        else if (nbytes > 0)
            logger(2, "unexpected data from idle FastCGI connection");
    }

    InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

const char *
FcgiStock::GetChildTag(void *info) const noexcept
{
    const auto &params = *(const FcgiChildParams *)info;

    return params.options.tag;
}

void
FcgiStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                        PreparedChildProcess &p)
{
    auto &params = *(FcgiChildParams *)info;
    const ChildOptions &options = params.options;

    p.SetStdin(std::move(fd));

    /* the FastCGI protocol defines a channel for stderr, so we could
       close its "real" stderr here, but many FastCGI applications
       don't use the FastCGI protocol to send error messages, so we
       just keep it open */

    UniqueFileDescriptor null_fd;
    if (null_fd.Open("/dev/null", O_WRONLY))
        p.SetStdout(std::move(null_fd));

    p.Append(params.executable_path);
    for (auto i : params.args)
        p.Append(i);

    options.CopyTo(p, true, nullptr);
}

/*
 * stock class
 *
 */

void
FcgiStock::Create(CreateStockItem c, void *info,
                  struct pool &caller_pool,
                  gcc_unused CancellablePointer &cancel_ptr)
{
    FcgiChildParams *params = (FcgiChildParams *)info;

    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    auto *connection = new FcgiConnection(GetEventLoop(), c);

    const ChildOptions &options = params->options;
    if (options.jail != nullptr && options.jail->enabled) {
        connection->jail_home_directory = options.jail->home_directory;

        if (!connection->jail_config.Load("/etc/cm4all/jailcgi/jail.conf")) {
            delete connection;
            throw FcgiClientError("Failed to load /etc/cm4all/jailcgi/jail.conf");
        }
    }

    const char *key = c.GetStockName();

    try {
        connection->child = child_stock.GetStockMap().GetNow(caller_pool, key, params);
    } catch (...) {
        delete connection;
        std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to start FastCGI server '%s'",
                                                                 key)));
    }

    try {
        connection->fd = child_stock_item_connect(*connection->child);
    } catch (...) {
        connection->kill = true;
        delete connection;
        std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to connect to FastCGI server '%s'",
                                                                 key)));
    }

    connection->event.Set(connection->fd.Get(), SocketEvent::READ);

    connection->InvokeCreateSuccess();
}

bool
FcgiConnection::Borrow() noexcept
{
    /* check the connection status before using it, just in case the
       FastCGI server has decided to close the connection before
       fcgi_connection_event_callback() got invoked */
    char buffer;
    ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
    if (nbytes > 0) {
        logger(2, "unexpected data from idle FastCGI connection");
        return false;
    } else if (nbytes == 0) {
        /* connection closed (not worth a log message) */
        return false;
    } else if (errno != EAGAIN) {
        logger(2, "error on idle FastCGI connection: ", strerror(errno));
        return false;
    }

    event.Delete();
    aborted = false;
    return true;
}

bool
FcgiConnection::Release() noexcept
{
    fresh = false;
    event.Add(EventDuration<300>::value);
    return true;
}

FcgiConnection::~FcgiConnection()
{
    if (fd.IsDefined()) {
        event.Delete();
        fd.Close();
    }

    if (fresh && aborted)
        /* the fcgi_client caller has aborted the request before the
           first response on a fresh connection was received: better
           kill the child process, it may be failing on us
           completely */
        kill = true;

    if (child != nullptr)
        child->Put(kill);
}

/*
 * interface
 *
 */

inline
FcgiStock::FcgiStock(unsigned limit, unsigned max_idle,
                     EventLoop &event_loop, SpawnService &spawn_service,
                     SocketDescriptor _log_socket) noexcept
    :hstock(event_loop, *this, limit, max_idle),
     child_stock(event_loop, spawn_service,
                 *this,
                 _log_socket,
                 limit, max_idle) {}

void
FcgiStock::FadeTag(const char *tag)
{
    assert(tag != nullptr);

    hstock.FadeIf([tag](const StockItem &item){
            const auto &connection = (const FcgiConnection &)item;
            const char *tag2 = connection.GetTag();
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    child_stock.FadeTag(tag);
}

FcgiStock *
fcgi_stock_new(unsigned limit, unsigned max_idle,
               EventLoop &event_loop, SpawnService &spawn_service,
               SocketDescriptor log_socket)
{
    return new FcgiStock(limit, max_idle, event_loop, spawn_service,
                         log_socket);
}

void
fcgi_stock_free(FcgiStock *fcgi_stock)
{
    delete fcgi_stock;
}

SocketDescriptor
fcgi_stock_get_log_socket(const FcgiStock &fs) noexcept
{
    return fs.GetLogSocket();
}

void
fcgi_stock_fade_all(FcgiStock &fs)
{
    fs.FadeAll();
}

void
fcgi_stock_fade_tag(FcgiStock &fs, const char *tag)
{
    fs.FadeTag(tag);
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock,
               const ChildOptions &options,
               const char *executable_path,
               ConstBuffer<const char *> args)
{
    const AutoRewindPool auto_rewind(*tpool);

    auto params = NewFromPool<FcgiChildParams>(*tpool, executable_path,
                                               args, options);

    return fcgi_stock->hstock.GetNow(*tpool,
                                     params->GetStockKey(*tpool), params);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item)
{
    return AF_UNIX;
}

void
fcgi_stock_item_set_site(StockItem &item, const char *site) noexcept
{
    auto &connection = (FcgiConnection &)item;
    connection.SetSite(site);
}

void
fcgi_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
    auto &connection = (FcgiConnection &)item;
    connection.SetUri(uri);
}

SocketDescriptor
fcgi_stock_item_get(const StockItem &item)
{
    const auto *connection = (const FcgiConnection *)&item;

    assert(connection->fd.IsDefined());

    return connection->fd;
}

const char *
fcgi_stock_translate_path(const StockItem &item,
                          const char *path, AllocatorPtr alloc)
{
    const auto *connection = (const FcgiConnection *)&item;

    if (connection->jail_home_directory.empty())
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = connection->jail_config.TranslatePath(path,
                                                               connection->jail_home_directory.c_str(),
                                                               alloc);
    return jailed != nullptr ? jailed : path;
}

void
fcgi_stock_aborted(StockItem &item)
{
    auto *connection = (FcgiConnection *)&item;

    connection->aborted = true;
}
<commit_msg>fcgi/Stock: separate TimerEvent for the timeout<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 "Stock.hxx"
#include "Error.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_stock.hxx"
#include "spawn/Prepared.hxx"
#include "spawn/ChildOptions.hxx"
#include "spawn/JailParams.hxx"
#include "spawn/JailConfig.hxx"
#include "pool/tpool.hxx"
#include "AllocatorPtr.hxx"
#include "event/SocketEvent.hxx"
#include "event/TimerEvent.hxx"
#include "event/Duration.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/ConstBuffer.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"
#include "util/StringFormat.hxx"

#include <string>

#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#ifdef __linux
#include <sched.h>
#endif

struct FcgiStock final : StockClass, ChildStockClass {
    StockMap hstock;
    ChildStock child_stock;

    FcgiStock(unsigned limit, unsigned max_idle,
              EventLoop &event_loop, SpawnService &spawn_service,
              SocketDescriptor _log_socket) noexcept;

    ~FcgiStock() {
        /* this one must be cleared before #child_stock; FadeAll()
           calls ClearIdle(), so this method is the best match for
           what we want to do (though a kludge) */
        hstock.FadeAll();
    }

    EventLoop &GetEventLoop() {
        return hstock.GetEventLoop();
    }

    SocketDescriptor GetLogSocket() const noexcept {
        return child_stock.GetLogSocket();
    }

    void FadeAll() {
        hstock.FadeAll();
        child_stock.GetStockMap().FadeAll();
    }

    void FadeTag(const char *tag);

    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *info, struct pool &caller_pool,
                CancellablePointer &cancel_ptr) override;

    /* virtual methods from class ChildStockClass */
    const char *GetChildTag(void *info) const noexcept override;
    void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                      PreparedChildProcess &p) override;
};

struct FcgiChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;

    const ChildOptions &options;

    FcgiChildParams(const char *_executable_path,
                    ConstBuffer<const char *> _args,
                    const ChildOptions &_options)
        :executable_path(_executable_path), args(_args),
         options(_options) {}

    const char *GetStockKey(struct pool &pool) const;
};

struct FcgiConnection final : StockItem {
    const LLogger logger;

    std::string jail_home_directory;

    JailConfig jail_config;

    StockItem *child = nullptr;

    UniqueSocketDescriptor fd;
    SocketEvent event;
    TimerEvent idle_timeout_event;

    /**
     * Is this a fresh connection to the FastCGI child process?
     */
    bool fresh = true;

    /**
     * Shall the FastCGI child process be killed?
     */
    bool kill = false;

    /**
     * Was the current request aborted by the fcgi_client caller?
     */
    bool aborted = false;

    explicit FcgiConnection(EventLoop &event_loop, CreateStockItem c)
        :StockItem(c), logger(GetStockName()),
         event(event_loop, BIND_THIS_METHOD(OnSocketEvent)),
         idle_timeout_event(c.stock.GetEventLoop(),
                            BIND_THIS_METHOD(OnIdleTimeout)) {}

    ~FcgiConnection() override;

    gcc_pure
    const char *GetTag() const {
        assert(child != nullptr);

        return child_stock_item_get_tag(*child);
    }

    void SetSite(const char *site) noexcept {
        child_stock_item_set_site(*child, site);
    }

    void SetUri(const char *uri) noexcept {
        child_stock_item_set_uri(*child, uri);
    }

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override;
    bool Release() noexcept override;

private:
    void OnSocketEvent(unsigned events);
    void OnIdleTimeout() noexcept;
};

const char *
FcgiChildParams::GetStockKey(struct pool &pool) const
{
    const char *key = executable_path;

    for (auto i : args)
        key = p_strcat(&pool, key, " ", i, nullptr);

    for (auto i : options.env)
        key = p_strcat(&pool, key, "$", i, nullptr);

    char options_buffer[16384];
    *options.MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(&pool, key, options_buffer, nullptr);

    return key;
}

/*
 * libevent callback
 *
 */

void
FcgiConnection::OnSocketEvent(unsigned)
{
    char buffer;
    ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
    if (nbytes < 0)
        logger(2, "error on idle FastCGI connection: ", strerror(errno));
    else if (nbytes > 0)
        logger(2, "unexpected data from idle FastCGI connection");

    InvokeIdleDisconnect();
}

inline void
FcgiConnection::OnIdleTimeout() noexcept
{
    InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

const char *
FcgiStock::GetChildTag(void *info) const noexcept
{
    const auto &params = *(const FcgiChildParams *)info;

    return params.options.tag;
}

void
FcgiStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                        PreparedChildProcess &p)
{
    auto &params = *(FcgiChildParams *)info;
    const ChildOptions &options = params.options;

    p.SetStdin(std::move(fd));

    /* the FastCGI protocol defines a channel for stderr, so we could
       close its "real" stderr here, but many FastCGI applications
       don't use the FastCGI protocol to send error messages, so we
       just keep it open */

    UniqueFileDescriptor null_fd;
    if (null_fd.Open("/dev/null", O_WRONLY))
        p.SetStdout(std::move(null_fd));

    p.Append(params.executable_path);
    for (auto i : params.args)
        p.Append(i);

    options.CopyTo(p, true, nullptr);
}

/*
 * stock class
 *
 */

void
FcgiStock::Create(CreateStockItem c, void *info,
                  struct pool &caller_pool,
                  gcc_unused CancellablePointer &cancel_ptr)
{
    FcgiChildParams *params = (FcgiChildParams *)info;

    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    auto *connection = new FcgiConnection(GetEventLoop(), c);

    const ChildOptions &options = params->options;
    if (options.jail != nullptr && options.jail->enabled) {
        connection->jail_home_directory = options.jail->home_directory;

        if (!connection->jail_config.Load("/etc/cm4all/jailcgi/jail.conf")) {
            delete connection;
            throw FcgiClientError("Failed to load /etc/cm4all/jailcgi/jail.conf");
        }
    }

    const char *key = c.GetStockName();

    try {
        connection->child = child_stock.GetStockMap().GetNow(caller_pool, key, params);
    } catch (...) {
        delete connection;
        std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to start FastCGI server '%s'",
                                                                 key)));
    }

    try {
        connection->fd = child_stock_item_connect(*connection->child);
    } catch (...) {
        connection->kill = true;
        delete connection;
        std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to connect to FastCGI server '%s'",
                                                                 key)));
    }

    connection->event.Set(connection->fd.Get(), SocketEvent::READ);

    connection->InvokeCreateSuccess();
}

bool
FcgiConnection::Borrow() noexcept
{
    /* check the connection status before using it, just in case the
       FastCGI server has decided to close the connection before
       fcgi_connection_event_callback() got invoked */
    char buffer;
    ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
    if (nbytes > 0) {
        logger(2, "unexpected data from idle FastCGI connection");
        return false;
    } else if (nbytes == 0) {
        /* connection closed (not worth a log message) */
        return false;
    } else if (errno != EAGAIN) {
        logger(2, "error on idle FastCGI connection: ", strerror(errno));
        return false;
    }

    event.Delete();
    idle_timeout_event.Cancel();
    aborted = false;
    return true;
}

bool
FcgiConnection::Release() noexcept
{
    fresh = false;
    event.Add();
    idle_timeout_event.Add(EventDuration<300>::value);
    return true;
}

FcgiConnection::~FcgiConnection()
{
    if (fd.IsDefined()) {
        event.Delete();
        fd.Close();
    }

    if (fresh && aborted)
        /* the fcgi_client caller has aborted the request before the
           first response on a fresh connection was received: better
           kill the child process, it may be failing on us
           completely */
        kill = true;

    if (child != nullptr)
        child->Put(kill);
}

/*
 * interface
 *
 */

inline
FcgiStock::FcgiStock(unsigned limit, unsigned max_idle,
                     EventLoop &event_loop, SpawnService &spawn_service,
                     SocketDescriptor _log_socket) noexcept
    :hstock(event_loop, *this, limit, max_idle),
     child_stock(event_loop, spawn_service,
                 *this,
                 _log_socket,
                 limit, max_idle) {}

void
FcgiStock::FadeTag(const char *tag)
{
    assert(tag != nullptr);

    hstock.FadeIf([tag](const StockItem &item){
            const auto &connection = (const FcgiConnection &)item;
            const char *tag2 = connection.GetTag();
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    child_stock.FadeTag(tag);
}

FcgiStock *
fcgi_stock_new(unsigned limit, unsigned max_idle,
               EventLoop &event_loop, SpawnService &spawn_service,
               SocketDescriptor log_socket)
{
    return new FcgiStock(limit, max_idle, event_loop, spawn_service,
                         log_socket);
}

void
fcgi_stock_free(FcgiStock *fcgi_stock)
{
    delete fcgi_stock;
}

SocketDescriptor
fcgi_stock_get_log_socket(const FcgiStock &fs) noexcept
{
    return fs.GetLogSocket();
}

void
fcgi_stock_fade_all(FcgiStock &fs)
{
    fs.FadeAll();
}

void
fcgi_stock_fade_tag(FcgiStock &fs, const char *tag)
{
    fs.FadeTag(tag);
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock,
               const ChildOptions &options,
               const char *executable_path,
               ConstBuffer<const char *> args)
{
    const AutoRewindPool auto_rewind(*tpool);

    auto params = NewFromPool<FcgiChildParams>(*tpool, executable_path,
                                               args, options);

    return fcgi_stock->hstock.GetNow(*tpool,
                                     params->GetStockKey(*tpool), params);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item)
{
    return AF_UNIX;
}

void
fcgi_stock_item_set_site(StockItem &item, const char *site) noexcept
{
    auto &connection = (FcgiConnection &)item;
    connection.SetSite(site);
}

void
fcgi_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
    auto &connection = (FcgiConnection &)item;
    connection.SetUri(uri);
}

SocketDescriptor
fcgi_stock_item_get(const StockItem &item)
{
    const auto *connection = (const FcgiConnection *)&item;

    assert(connection->fd.IsDefined());

    return connection->fd;
}

const char *
fcgi_stock_translate_path(const StockItem &item,
                          const char *path, AllocatorPtr alloc)
{
    const auto *connection = (const FcgiConnection *)&item;

    if (connection->jail_home_directory.empty())
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = connection->jail_config.TranslatePath(path,
                                                               connection->jail_home_directory.c_str(),
                                                               alloc);
    return jailed != nullptr ? jailed : path;
}

void
fcgi_stock_aborted(StockItem &item)
{
    auto *connection = (FcgiConnection *)&item;

    connection->aborted = true;
}
<|endoftext|>