<commit_before>#include "reloader.h"

#include <algorithm>
#include <cinttypes>
#include <iostream>
#include <ncurses.h>
#include <thread>

#include "controller.h"
#include "curlhandle.h"
#include "dbexception.h"
#include "downloadthread.h"
#include "fmtstrformatter.h"
#include "reloadrangethread.h"
#include "reloadthread.h"
#include "rss/exception.h"
#include "rssfeed.h"
#include "rssparser.h"
#include "scopemeasure.h"
#include "utils.h"
#include "view.h"

namespace newsboat {

Reloader::Reloader(Controller* c, Cache* cc, ConfigContainer* cfg)
	: ctrl(c)
	, rsscache(cc)
	, cfg(cfg)
{
}

void Reloader::spawn_reloadthread()
{
	std::thread t{ReloadThread(ctrl, cfg)};
	t.detach();
}

void Reloader::start_reload_all_thread(std::vector<int>* indexes)
{
	LOG(Level::INFO, "starting reload all thread");
	std::thread t(DownloadThread(*this, indexes));
	t.detach();
}

bool Reloader::trylock_reload_mutex()
{
	if (reload_mutex.try_lock()) {
		LOG(Level::DEBUG, "Reloader::trylock_reload_mutex succeeded");
		return true;
	}
	LOG(Level::DEBUG, "Reloader::trylock_reload_mutex failed");
	return false;
}

void Reloader::reload(unsigned int pos,
	unsigned int max,
	bool unattended,
	CurlHandle* easyhandle)
{
	LOG(Level::DEBUG, "Reloader::reload: pos = %u max = %u", pos, max);
	if (pos < ctrl->get_feedcontainer()->feeds.size()) {
		std::shared_ptr<RssFeed> oldfeed =
			ctrl->get_feedcontainer()->feeds[pos];

		// Query feed reloading should be handled by the calling functions
		// (e.g.  Reloader::reload_all() calling View::prepare_query_feed())
		if (oldfeed->is_query_feed()) {
			LOG(Level::DEBUG, "Reloader::reload: skipping query feed");
			return;
		}

		std::string errmsg;
		if (!unattended) {
			ctrl->get_view()->set_status(
				strprintf::fmt(_("%sLoading %s..."),
					prepare_message(pos + 1, max),
					utils::censor_url(oldfeed->rssurl())));
		}

		const bool ignore_dl =
			(cfg->get_configvalue("ignore-mode") == "download");

		RssParser parser(oldfeed->rssurl(),
			rsscache,
			cfg,
			ignore_dl ? ctrl->get_ignores() : nullptr,
			ctrl->get_api());
		parser.set_easyhandle(easyhandle);
		LOG(Level::DEBUG, "Reloader::reload: created parser");
		try {
			oldfeed->set_status(DlStatus::DURING_DOWNLOAD);
			std::shared_ptr<RssFeed> newfeed = parser.parse();
			if (newfeed != nullptr) {
				ctrl->replace_feed(
					oldfeed, newfeed, pos, unattended);
				if (newfeed->total_item_count() == 0) {
					LOG(Level::DEBUG,
						"Reloader::reload: feed is empty");
				}
			}
			oldfeed->set_status(DlStatus::SUCCESS);
			ctrl->get_view()->set_status("");
		} catch (const DbException& e) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					e.what());
		} catch (const std::string& emsg) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					emsg);
		} catch (rsspp::Exception& e) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					e.what());
		}
		if (!errmsg.empty()) {
			oldfeed->set_status(DlStatus::DL_ERROR);
			ctrl->get_view()->set_status(errmsg);
			LOG(Level::USERERROR, "%s", errmsg);
		}
	} else {
		ctrl->get_view()->show_error(_("Error: invalid feed!"));
	}
}

std::string Reloader::prepare_message(unsigned int pos, unsigned int max)
{
	if (max > 0) {
		return strprintf::fmt("(%u/%u) ", pos, max);
	}
	return "";
}

void Reloader::reload_all(bool unattended)
{
	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	int num_threads = cfg->get_configvalue_as_int("reload-threads");
	time_t t1, t2, dt;

	ctrl->get_feedcontainer()->reset_feeds_status();
	const auto num_feeds = ctrl->get_feedcontainer()->feeds_size();

	// TODO: change to std::clamp in C++17
	const int min_threads = 1;
	const int max_threads = num_feeds;
	num_threads = std::max(min_threads, std::min(num_threads, max_threads));

	t1 = time(nullptr);

	LOG(Level::DEBUG, "Reloader::reload_all: starting with reload all...");
	if (num_threads == 1) {
		reload_range(0, num_feeds - 1, num_feeds, unattended);
	} else {
		std::vector<std::pair<unsigned int, unsigned int>> partitions =
				utils::partition_indexes(0, num_feeds - 1, num_threads);
		std::vector<std::thread> threads;
		LOG(Level::DEBUG,
			"Reloader::reload_all: starting reload threads...");
		for (int i = 0; i < num_threads - 1; i++) {
			threads.push_back(std::thread(ReloadRangeThread(*this,
						partitions[i].first,
						partitions[i].second,
						num_feeds,
						unattended)));
		}
		LOG(Level::DEBUG,
			"Reloader::reload_all: starting my own reload...");
		reload_range(partitions[num_threads - 1].first,
			partitions[num_threads - 1].second,
			num_feeds,
			unattended);
		LOG(Level::DEBUG,
			"Reloader::reload_all: joining other threads...");
		for (size_t i = 0; i < threads.size(); i++) {
			threads[i].join();
		}
	}

	// refresh query feeds (update and sort)
	LOG(Level::DEBUG, "Reloader::reload_all: refresh query feeds");
	for (const auto& feed : ctrl->get_feedcontainer()->feeds) {
		if (feed->is_query_feed()) {
			ctrl->get_view()->prepare_query_feed(feed);
			feed->set_status(DlStatus::SUCCESS);
		}
	}
	ctrl->get_view()->force_redraw();

	ctrl->get_feedcontainer()->sort_feeds(cfg->get_feed_sort_strategy());
	ctrl->update_feedlist();

	t2 = time(nullptr);
	dt = t2 - t1;
	// On GCC, `time_t` is `long int`, which is at least 32 bits. On x86_64,
	// it's 64 bits. Thus, this cast is either a no-op, or an up-cast which are
	// always safe.
	LOG(Level::INFO,
		"Reloader::reload_all: reload took %" PRId64 " seconds",
		static_cast<int64_t>(dt));

	notify_reload_finished(unread_feeds, unread_articles);
}

void Reloader::reload_indexes(const std::vector<int>& indexes, bool unattended)
{
	ScopeMeasure m1("Reloader::reload_indexes");
	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	const auto size = ctrl->get_feedcontainer()->feeds_size();

	for (const auto& idx : indexes) {
		reload(idx, size, unattended);
	}

	notify_reload_finished(unread_feeds, unread_articles);

	if (!unattended) {
		ctrl->get_view()->set_status("");
	}
}

void Reloader::reload_range(unsigned int start,
	unsigned int end,
	unsigned int size,
	bool unattended)
{
	std::vector<unsigned int> v;
	for (unsigned int i = start; i <= end; ++i) {
		v.push_back(i);
	}

	auto extract = [](std::string& s, const std::string& url) {
		size_t p = url.find("//");
		p = (p == std::string::npos) ? 0 : p + 2;
		std::string suff(url.substr(p));
		p = suff.find('/');
		s = suff.substr(0, p);
	};

	std::sort(v.begin(), v.end(), [&](unsigned int a, unsigned int b) {
		std::string domain1, domain2;
		extract(domain1, ctrl->get_feedcontainer()->feeds[a]->rssurl());
		extract(domain2, ctrl->get_feedcontainer()->feeds[b]->rssurl());
		std::reverse(domain1.begin(), domain1.end());
		std::reverse(domain2.begin(), domain2.end());
		return domain1 < domain2;
	});

	CurlHandle easyhandle;

	for (const auto& i : v) {
		LOG(Level::DEBUG,
			"Reloader::reload_range: reloading feed #%u",
			i);
		reload(i, size, unattended, &easyhandle);
	}
}

void Reloader::notify(const std::string& msg)
{
	if (cfg->get_configvalue_as_bool("notify-screen")) {
		LOG(Level::DEBUG, "reloader:notify: notifying screen");
		std::cout << "\033^" << msg << "\033\\";
		std::cout.flush();
	}
	if (cfg->get_configvalue_as_bool("notify-xterm")) {
		LOG(Level::DEBUG, "reloader:notify: notifying xterm");
		std::cout << "\033]2;" << msg << "\033\\";
		std::cout.flush();
	}
	if (cfg->get_configvalue_as_bool("notify-beep")) {
		LOG(Level::DEBUG, "reloader:notify: notifying beep");
		::beep();
	}
	if (cfg->get_configvalue("notify-program").length() > 0) {
		std::string prog = cfg->get_configvalue("notify-program");
		LOG(Level::DEBUG,
			"reloader:notify: notifying external program `%s'",
			prog);
		utils::run_command(prog, msg);
	}
}

void Reloader::notify_reload_finished(unsigned int unread_feeds_before,
	unsigned int unread_articles_before)
{
	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	const bool notify_always = cfg->get_configvalue_as_bool("notify-always");

	if (notify_always || unread_feeds > unread_feeds_before ||
		unread_articles > unread_articles_before) {
		// TODO: Determine what should be done if `unread_articles < unread_articles_before`.
		// It is expected this can happen if the user marks an article as read
		// while a reload is in progress.
		const int article_count = unread_articles - unread_articles_before;
		const int feed_count = unread_feeds - unread_feeds_before;

		LOG(Level::DEBUG, "unread article count: %d", article_count);
		LOG(Level::DEBUG, "unread feed count: %d", feed_count);

		FmtStrFormatter fmt;
		fmt.register_fmt('f', std::to_string(unread_feeds));
		fmt.register_fmt('n', std::to_string(unread_articles));
		fmt.register_fmt('d',
			std::to_string(article_count >= 0 ? article_count : 0));
		fmt.register_fmt(
			'D', std::to_string(feed_count >= 0 ? feed_count : 0));
		notify(fmt.do_format(cfg->get_configvalue("notify-format")));
	}
}

} // namespace newsboat
<commit_msg>Reloader: Use ScopeMeasure instead of calls to `time()` and `LOG()`<commit_after>#include "reloader.h"

#include <algorithm>
#include <cinttypes>
#include <iostream>
#include <ncurses.h>
#include <thread>

#include "controller.h"
#include "curlhandle.h"
#include "dbexception.h"
#include "downloadthread.h"
#include "fmtstrformatter.h"
#include "reloadrangethread.h"
#include "reloadthread.h"
#include "rss/exception.h"
#include "rssfeed.h"
#include "rssparser.h"
#include "scopemeasure.h"
#include "utils.h"
#include "view.h"

namespace newsboat {

Reloader::Reloader(Controller* c, Cache* cc, ConfigContainer* cfg)
	: ctrl(c)
	, rsscache(cc)
	, cfg(cfg)
{
}

void Reloader::spawn_reloadthread()
{
	std::thread t{ReloadThread(ctrl, cfg)};
	t.detach();
}

void Reloader::start_reload_all_thread(std::vector<int>* indexes)
{
	LOG(Level::INFO, "starting reload all thread");
	std::thread t(DownloadThread(*this, indexes));
	t.detach();
}

bool Reloader::trylock_reload_mutex()
{
	if (reload_mutex.try_lock()) {
		LOG(Level::DEBUG, "Reloader::trylock_reload_mutex succeeded");
		return true;
	}
	LOG(Level::DEBUG, "Reloader::trylock_reload_mutex failed");
	return false;
}

void Reloader::reload(unsigned int pos,
	unsigned int max,
	bool unattended,
	CurlHandle* easyhandle)
{
	LOG(Level::DEBUG, "Reloader::reload: pos = %u max = %u", pos, max);
	if (pos < ctrl->get_feedcontainer()->feeds.size()) {
		std::shared_ptr<RssFeed> oldfeed =
			ctrl->get_feedcontainer()->feeds[pos];

		// Query feed reloading should be handled by the calling functions
		// (e.g.  Reloader::reload_all() calling View::prepare_query_feed())
		if (oldfeed->is_query_feed()) {
			LOG(Level::DEBUG, "Reloader::reload: skipping query feed");
			return;
		}

		std::string errmsg;
		if (!unattended) {
			ctrl->get_view()->set_status(
				strprintf::fmt(_("%sLoading %s..."),
					prepare_message(pos + 1, max),
					utils::censor_url(oldfeed->rssurl())));
		}

		const bool ignore_dl =
			(cfg->get_configvalue("ignore-mode") == "download");

		RssParser parser(oldfeed->rssurl(),
			rsscache,
			cfg,
			ignore_dl ? ctrl->get_ignores() : nullptr,
			ctrl->get_api());
		parser.set_easyhandle(easyhandle);
		LOG(Level::DEBUG, "Reloader::reload: created parser");
		try {
			oldfeed->set_status(DlStatus::DURING_DOWNLOAD);
			std::shared_ptr<RssFeed> newfeed = parser.parse();
			if (newfeed != nullptr) {
				ctrl->replace_feed(
					oldfeed, newfeed, pos, unattended);
				if (newfeed->total_item_count() == 0) {
					LOG(Level::DEBUG,
						"Reloader::reload: feed is empty");
				}
			}
			oldfeed->set_status(DlStatus::SUCCESS);
			ctrl->get_view()->set_status("");
		} catch (const DbException& e) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					e.what());
		} catch (const std::string& emsg) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					emsg);
		} catch (rsspp::Exception& e) {
			errmsg = strprintf::fmt(
					_("Error while retrieving %s: %s"),
					utils::censor_url(oldfeed->rssurl()),
					e.what());
		}
		if (!errmsg.empty()) {
			oldfeed->set_status(DlStatus::DL_ERROR);
			ctrl->get_view()->set_status(errmsg);
			LOG(Level::USERERROR, "%s", errmsg);
		}
	} else {
		ctrl->get_view()->show_error(_("Error: invalid feed!"));
	}
}

std::string Reloader::prepare_message(unsigned int pos, unsigned int max)
{
	if (max > 0) {
		return strprintf::fmt("(%u/%u) ", pos, max);
	}
	return "";
}

void Reloader::reload_all(bool unattended)
{
	ScopeMeasure sm("Reloader::reload_all");

	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	int num_threads = cfg->get_configvalue_as_int("reload-threads");

	ctrl->get_feedcontainer()->reset_feeds_status();
	const auto num_feeds = ctrl->get_feedcontainer()->feeds_size();

	// TODO: change to std::clamp in C++17
	const int min_threads = 1;
	const int max_threads = num_feeds;
	num_threads = std::max(min_threads, std::min(num_threads, max_threads));

	LOG(Level::DEBUG, "Reloader::reload_all: starting with reload all...");
	if (num_threads == 1) {
		reload_range(0, num_feeds - 1, num_feeds, unattended);
	} else {
		std::vector<std::pair<unsigned int, unsigned int>> partitions =
				utils::partition_indexes(0, num_feeds - 1, num_threads);
		std::vector<std::thread> threads;
		LOG(Level::DEBUG,
			"Reloader::reload_all: starting reload threads...");
		for (int i = 0; i < num_threads - 1; i++) {
			threads.push_back(std::thread(ReloadRangeThread(*this,
						partitions[i].first,
						partitions[i].second,
						num_feeds,
						unattended)));
		}
		LOG(Level::DEBUG,
			"Reloader::reload_all: starting my own reload...");
		reload_range(partitions[num_threads - 1].first,
			partitions[num_threads - 1].second,
			num_feeds,
			unattended);
		LOG(Level::DEBUG,
			"Reloader::reload_all: joining other threads...");
		for (size_t i = 0; i < threads.size(); i++) {
			threads[i].join();
		}
	}

	// refresh query feeds (update and sort)
	LOG(Level::DEBUG, "Reloader::reload_all: refresh query feeds");
	for (const auto& feed : ctrl->get_feedcontainer()->feeds) {
		if (feed->is_query_feed()) {
			ctrl->get_view()->prepare_query_feed(feed);
			feed->set_status(DlStatus::SUCCESS);
		}
	}
	ctrl->get_view()->force_redraw();

	ctrl->get_feedcontainer()->sort_feeds(cfg->get_feed_sort_strategy());
	ctrl->update_feedlist();

	notify_reload_finished(unread_feeds, unread_articles);
}

void Reloader::reload_indexes(const std::vector<int>& indexes, bool unattended)
{
	ScopeMeasure m1("Reloader::reload_indexes");
	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	const auto size = ctrl->get_feedcontainer()->feeds_size();

	for (const auto& idx : indexes) {
		reload(idx, size, unattended);
	}

	notify_reload_finished(unread_feeds, unread_articles);

	if (!unattended) {
		ctrl->get_view()->set_status("");
	}
}

void Reloader::reload_range(unsigned int start,
	unsigned int end,
	unsigned int size,
	bool unattended)
{
	std::vector<unsigned int> v;
	for (unsigned int i = start; i <= end; ++i) {
		v.push_back(i);
	}

	auto extract = [](std::string& s, const std::string& url) {
		size_t p = url.find("//");
		p = (p == std::string::npos) ? 0 : p + 2;
		std::string suff(url.substr(p));
		p = suff.find('/');
		s = suff.substr(0, p);
	};

	std::sort(v.begin(), v.end(), [&](unsigned int a, unsigned int b) {
		std::string domain1, domain2;
		extract(domain1, ctrl->get_feedcontainer()->feeds[a]->rssurl());
		extract(domain2, ctrl->get_feedcontainer()->feeds[b]->rssurl());
		std::reverse(domain1.begin(), domain1.end());
		std::reverse(domain2.begin(), domain2.end());
		return domain1 < domain2;
	});

	CurlHandle easyhandle;

	for (const auto& i : v) {
		LOG(Level::DEBUG,
			"Reloader::reload_range: reloading feed #%u",
			i);
		reload(i, size, unattended, &easyhandle);
	}
}

void Reloader::notify(const std::string& msg)
{
	if (cfg->get_configvalue_as_bool("notify-screen")) {
		LOG(Level::DEBUG, "reloader:notify: notifying screen");
		std::cout << "\033^" << msg << "\033\\";
		std::cout.flush();
	}
	if (cfg->get_configvalue_as_bool("notify-xterm")) {
		LOG(Level::DEBUG, "reloader:notify: notifying xterm");
		std::cout << "\033]2;" << msg << "\033\\";
		std::cout.flush();
	}
	if (cfg->get_configvalue_as_bool("notify-beep")) {
		LOG(Level::DEBUG, "reloader:notify: notifying beep");
		::beep();
	}
	if (cfg->get_configvalue("notify-program").length() > 0) {
		std::string prog = cfg->get_configvalue("notify-program");
		LOG(Level::DEBUG,
			"reloader:notify: notifying external program `%s'",
			prog);
		utils::run_command(prog, msg);
	}
}

void Reloader::notify_reload_finished(unsigned int unread_feeds_before,
	unsigned int unread_articles_before)
{
	const auto unread_feeds =
		ctrl->get_feedcontainer()->unread_feed_count();
	const auto unread_articles =
		ctrl->get_feedcontainer()->unread_item_count();
	const bool notify_always = cfg->get_configvalue_as_bool("notify-always");

	if (notify_always || unread_feeds > unread_feeds_before ||
		unread_articles > unread_articles_before) {
		// TODO: Determine what should be done if `unread_articles < unread_articles_before`.
		// It is expected this can happen if the user marks an article as read
		// while a reload is in progress.
		const int article_count = unread_articles - unread_articles_before;
		const int feed_count = unread_feeds - unread_feeds_before;

		LOG(Level::DEBUG, "unread article count: %d", article_count);
		LOG(Level::DEBUG, "unread feed count: %d", feed_count);

		FmtStrFormatter fmt;
		fmt.register_fmt('f', std::to_string(unread_feeds));
		fmt.register_fmt('n', std::to_string(unread_articles));
		fmt.register_fmt('d',
			std::to_string(article_count >= 0 ? article_count : 0));
		fmt.register_fmt(
			'D', std::to_string(feed_count >= 0 ? feed_count : 0));
		notify(fmt.do_format(cfg->get_configvalue("notify-format")));
	}
}

} // namespace newsboat
<|endoftext|>


<commit_before>#include "shared.hh"
#include "local-store.hh"
#include "util.hh"
#include "serialise.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "globals.hh"

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>

using namespace nix;

#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif

static FdSource from(STDIN_FILENO);
static FdSink to(STDOUT_FILENO);

bool canSendStderr;

/* This function is called anytime we want to write something to
   stderr.  If we're in a state where the protocol allows it (i.e.,
   when canSendStderr), send the message to the client over the
   socket. */
static void tunnelStderr(const unsigned char * buf, size_t count)
{
    if (canSendStderr) {
        try {
            writeInt(STDERR_NEXT, to);
            writeString(string((char *) buf, count), to);
        } catch (...) {
            /* Write failed; that means that the other side is
               gone. */
            canSendStderr = false;
            throw;
        }
    }
}

/* A SIGPOLL signal is received when data is available on the client
   communication scoket, or when the client has closed its side of the
   socket.  This handler is enabled at precisely those moments in the
   protocol when we're doing work and the client is supposed to be
   quiet.  Thus, if we get a SIGPOLL signal, it means that the client
   has quit.  So we should quit as well.

   Too bad most operating systems don't support the POLL_HUP value for
   si_code in siginfo_t.  That would make most of the SIGPOLL
   complexity unnecessary, i.e., we could just enable SIGPOLL all the
   time and wouldn't have to worry about races. */
static void sigioHandler(int sigNo)
{
    if (!blockInt) {
        _isInterrupted = 1;
        blockInt = 1;
        canSendStderr = false;
        write(STDERR_FILENO, "SIGPOLL\n", 8);
    }
}

static void setSigPollAction(bool ignore)
{
    struct sigaction act, oact;
    act.sa_handler = ignore ? SIG_IGN : sigioHandler;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGPOLL, &act, &oact))
        throw SysError("setting handler for SIGPOLL");
}

/* startWork() means that we're starting an operation for which we
   want to send out stderr to the client. */
static void startWork()
{
    canSendStderr = true;

    /* Handle client death asynchronously. */
    setSigPollAction(false);

    /* Of course, there is a race condition here: the socket could
       have closed between when we last read from / wrote to it, and
       between the time we set the handler for SIGPOLL.  In that case
       we won't get the signal.  So do a non-blocking select() to find
       out if any input is available on the socket.  If there is, it
       has to be the 0-byte read that indicates that the socket has
       closed. */

    struct timeval timeout;
    timeout.tv_sec = timeout.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(from.fd, &fds);

    if (select(from.fd + 1, &fds, 0, 0, &timeout) == -1)
        throw SysError("select()");

    if (FD_ISSET(from.fd, &fds)) {
        char c;
        if (read(from.fd, &c, 1) != 0)
            throw Error("EOF expected (protocol error?)");
        _isInterrupted = 1;
        checkInterrupt();
    }
}

/* stopWork() means that we're done; stop sending stderr to the
   client. */
static void stopWork(bool success = true, const string & msg = "")
{
    /* Stop handling async client death; we're going to a state where
       we're either sending or receiving from the client, so we'll be
       notified of client death anyway. */
    setSigPollAction(true);

    canSendStderr = false;

    if (success)
        writeInt(STDERR_LAST, to);
    else {
        writeInt(STDERR_ERROR, to);
        writeString(msg, to);
    }
}

static void performOp(Source & from, Sink & to, unsigned int op)
{
    switch (op) {

#if 0
    case wopQuit: {
        /* Close the database. */
        store.reset((StoreAPI *) 0);
        writeInt(1, to);
        break;
    }
#endif

    case wopIsValidPath: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->isValidPath(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopHasSubstitutes: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->hasSubstitutes(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopQueryPathHash: {
        Path path = readStorePath(from);
        startWork();
        Hash hash = store->queryPathHash(path);
        stopWork();
        writeString(printHash(hash), to);
        break;
    }

    case wopQueryReferences:
    case wopQueryReferrers: {
        Path path = readStorePath(from);
        startWork();
        PathSet paths;
        if (op == wopQueryReferences)
            store->queryReferences(path, paths);
        else
            store->queryReferrers(path, paths);
        stopWork();
        writeStringSet(paths, to);
        break;
    }

    case wopAddToStore: {
        /* !!! uberquick hack */
        string baseName = readString(from);
        bool fixed = readInt(from) == 1;
        bool recursive = readInt(from) == 1;
        string hashAlgo = readString(from);

        Path tmp = createTempDir();
        Path tmp2 = tmp + "/" + baseName;
        restorePath(tmp2, from);

        startWork();
        Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
        stopWork();

        writeString(path, to);

        deletePath(tmp);
        break;
    }

    case wopAddTextToStore: {
        string suffix = readString(from);
        string s = readString(from);
        PathSet refs = readStorePaths(from);
        startWork();
        Path path = store->addTextToStore(suffix, s, refs);
        stopWork();
        writeString(path, to);
        break;
    }

    case wopBuildDerivations: {
        PathSet drvs = readStorePaths(from);
        startWork();
        store->buildDerivations(drvs);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopEnsurePath: {
        Path path = readStorePath(from);
        startWork();
        store->ensurePath(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddTempRoot: {
        Path path = readStorePath(from);
        startWork();
        store->addTempRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddIndirectRoot: {
        Path path = absPath(readString(from));
        startWork();
        store->addIndirectRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopSyncWithGC: {
        startWork();
        store->syncWithGC();
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopFindRoots: {
        startWork();
        Roots roots = store->findRoots();
        stopWork();
        writeInt(roots.size(), to);
        for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
            writeString(i->first, to);
            writeString(i->second, to);
        }
        break;
    }

    case wopCollectGarbage: {
        GCAction action = (GCAction) readInt(from);
        PathSet pathsToDelete = readStorePaths(from);
        bool ignoreLiveness = readInt(from);

        PathSet result;
        unsigned long long bytesFreed;

        startWork();
        if (ignoreLiveness)
            throw Error("you are not allowed to ignore liveness");
        store->collectGarbage(action, pathsToDelete, ignoreLiveness,
            result, bytesFreed);
        stopWork();

        writeStringSet(result, to);
        writeInt(bytesFreed & 0xffffffff, to);
        writeInt(bytesFreed >> 32, to);

        break;
    }

    default:
        throw Error(format("invalid operation %1%") % op);
    }
}

static void processConnection()
{
    canSendStderr = false;
    writeToStderr = tunnelStderr;

    /* Allow us to receive SIGPOLL for events on the client socket. */
    setSigPollAction(true);
    if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
        throw SysError("F_SETOWN");
    if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | FASYNC) == -1)
        throw SysError("F_SETFL");

    /* Exchange the greeting. */
    unsigned int magic = readInt(from);
    if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
    verbosity = (Verbosity) readInt(from);
    writeInt(WORKER_MAGIC_2, to);

    /* Send startup error messages to the client. */
    startWork();

    try {

        /* Prevent users from doing something very dangerous. */
        if (setuidMode && geteuid() == 0 &&
            querySetting("build-users", Strings()).size() == 0)
            throw Error("if you run `nix-worker' setuid root, then you MUST set `build-users'!");

        /* Open the store. */
        store = boost::shared_ptr<StoreAPI>(new LocalStore(true));

        stopWork();

    } catch (Error & e) {
        stopWork(false, e.msg());
        return;
    }

    /* Process client requests. */
    unsigned int opCount = 0;

    while (true) {
        WorkerOp op;
        try {
            op = (WorkerOp) readInt(from);
        } catch (EndOfFile & e) {
            break;
        }

        opCount++;

        try {
            performOp(from, to, op);
        } catch (Error & e) {
            stopWork(false, e.msg());
        }

        assert(!canSendStderr);
    };

    printMsg(lvlError, format("%1% worker operations") % opCount);
}

static void setSigChldAction(bool ignore)
{
    struct sigaction act, oact;
    act.sa_handler = ignore ? SIG_IGN : SIG_DFL;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, &oact))
        throw SysError("setting SIGCHLD handler");
}

static void daemonLoop()
{
    /* Get rid of children automatically; don't let them become
       zombies. */
    setSigChldAction(true);

    /* Create and bind to a Unix domain socket. */
    AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
    if (fdSocket == -1)
        throw SysError("cannot create Unix domain socket");

    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    if (socketPath.size() >= sizeof(addr.sun_path))
        throw Error(format("socket path `%1%' is too long") % socketPath);
    strcpy(addr.sun_path, socketPath.c_str());

    unlink(socketPath.c_str());

    /* Make sure that the socket is created with 0666 permission
       (everybody can connect). */
    mode_t oldMode = umask(0111);
    int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
    umask(oldMode);
    if (res == -1)
        throw SysError(format("cannot bind to socket `%1%'") % socketPath);

    if (listen(fdSocket, 5) == -1)
        throw SysError(format("cannot listen on socket `%1%'") % socketPath);

    /* Loop accepting connections. */
    while (1) {

        try {
            /* Important: the server process *cannot* open the
               Berkeley DB environment, because it doesn't like forks
               very much. */
            assert(!store);

            /* Accept a connection. */
            struct sockaddr_un remoteAddr;
            socklen_t remoteAddrLen = sizeof(remoteAddr);

            AutoCloseFD remote = accept(fdSocket,
                (struct sockaddr *) &remoteAddr, &remoteAddrLen);
            checkInterrupt();
            if (remote == -1)
                throw SysError("accepting connection");

            printMsg(lvlInfo, format("accepted connection %1%") % remote);

            /* Fork a child to handle the connection. */
            pid_t child;
            child = fork();

            switch (child) {

            case -1:
                throw SysError("unable to fork");

            case 0:
                try { /* child */

                    /* Background the worker. */
                    if (setsid() == -1)
                        throw SysError(format("creating a new session"));

                    /* Restore normal handling of SIGCHLD. */
                    setSigChldAction(false);

                    /* Handle the connection. */
                    from.fd = remote;
                    to.fd = remote;
                    processConnection();

                } catch (std::exception & e) {
                    std::cerr << format("child error: %1%\n") % e.what();
                }
                exit(0);
            }

        } catch (Interrupted & e) {
            throw;
        } catch (Error & e) {
            printMsg(lvlError, format("error processing connection: %1%") % e.msg());
        }
    }
}

void run(Strings args)
{
    bool slave = false;
    bool daemon = false;

    for (Strings::iterator i = args.begin(); i != args.end(); ) {
        string arg = *i++;
        if (arg == "--slave") slave = true;
        if (arg == "--daemon") daemon = true;
    }

    if (slave) {
        /* This prevents us from receiving signals from the terminal
           when we're running in setuid mode. */
        if (setsid() == -1)
            throw SysError(format("creating a new session"));

        processConnection();
    }

    else if (daemon) {
        if (setuidMode)
            throw Error("daemon cannot be started in setuid mode");
        chdir("/");
        daemonLoop();
    }

    else
        throw Error("must be run in either --slave or --daemon mode");
}

#include "help.txt.hh"

void printHelp()
{
    std::cout << string((char *) helpText, sizeof helpText);
}

string programId = "nix-worker";
<commit_msg>* Some renaming.<commit_after>#include "shared.hh"
#include "local-store.hh"
#include "util.hh"
#include "serialise.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "globals.hh"

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>

using namespace nix;

#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif

static FdSource from(STDIN_FILENO);
static FdSink to(STDOUT_FILENO);

bool canSendStderr;

/* This function is called anytime we want to write something to
   stderr.  If we're in a state where the protocol allows it (i.e.,
   when canSendStderr), send the message to the client over the
   socket. */
static void tunnelStderr(const unsigned char * buf, size_t count)
{
    if (canSendStderr) {
        try {
            writeInt(STDERR_NEXT, to);
            writeString(string((char *) buf, count), to);
        } catch (...) {
            /* Write failed; that means that the other side is
               gone. */
            canSendStderr = false;
            throw;
        }
    }
}

/* A SIGPOLL signal is received when data is available on the client
   communication scoket, or when the client has closed its side of the
   socket.  This handler is enabled at precisely those moments in the
   protocol when we're doing work and the client is supposed to be
   quiet.  Thus, if we get a SIGPOLL signal, it means that the client
   has quit.  So we should quit as well.

   Too bad most operating systems don't support the POLL_HUP value for
   si_code in siginfo_t.  That would make most of the SIGPOLL
   complexity unnecessary, i.e., we could just enable SIGPOLL all the
   time and wouldn't have to worry about races. */
static void sigPollHandler(int sigNo)
{
    if (!blockInt) {
        _isInterrupted = 1;
        blockInt = 1;
        canSendStderr = false;
        write(STDERR_FILENO, "SIGPOLL\n", 8);
    }
}

static void setSigPollAction(bool enable)
{
    struct sigaction act, oact;
    act.sa_handler = enable ? sigPollHandler : SIG_IGN;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGPOLL, &act, &oact))
        throw SysError("setting handler for SIGPOLL");
}

/* startWork() means that we're starting an operation for which we
   want to send out stderr to the client. */
static void startWork()
{
    canSendStderr = true;

    /* Handle client death asynchronously. */
    setSigPollAction(true);

    /* Of course, there is a race condition here: the socket could
       have closed between when we last read from / wrote to it, and
       between the time we set the handler for SIGPOLL.  In that case
       we won't get the signal.  So do a non-blocking select() to find
       out if any input is available on the socket.  If there is, it
       has to be the 0-byte read that indicates that the socket has
       closed. */

    struct timeval timeout;
    timeout.tv_sec = timeout.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(from.fd, &fds);

    if (select(from.fd + 1, &fds, 0, 0, &timeout) == -1)
        throw SysError("select()");

    if (FD_ISSET(from.fd, &fds)) {
        char c;
        if (read(from.fd, &c, 1) != 0)
            throw Error("EOF expected (protocol error?)");
        _isInterrupted = 1;
        checkInterrupt();
    }
}

/* stopWork() means that we're done; stop sending stderr to the
   client. */
static void stopWork(bool success = true, const string & msg = "")
{
    /* Stop handling async client death; we're going to a state where
       we're either sending or receiving from the client, so we'll be
       notified of client death anyway. */
    setSigPollAction(false);

    canSendStderr = false;

    if (success)
        writeInt(STDERR_LAST, to);
    else {
        writeInt(STDERR_ERROR, to);
        writeString(msg, to);
    }
}

static void performOp(Source & from, Sink & to, unsigned int op)
{
    switch (op) {

#if 0
    case wopQuit: {
        /* Close the database. */
        store.reset((StoreAPI *) 0);
        writeInt(1, to);
        break;
    }
#endif

    case wopIsValidPath: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->isValidPath(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopHasSubstitutes: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->hasSubstitutes(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopQueryPathHash: {
        Path path = readStorePath(from);
        startWork();
        Hash hash = store->queryPathHash(path);
        stopWork();
        writeString(printHash(hash), to);
        break;
    }

    case wopQueryReferences:
    case wopQueryReferrers: {
        Path path = readStorePath(from);
        startWork();
        PathSet paths;
        if (op == wopQueryReferences)
            store->queryReferences(path, paths);
        else
            store->queryReferrers(path, paths);
        stopWork();
        writeStringSet(paths, to);
        break;
    }

    case wopAddToStore: {
        /* !!! uberquick hack */
        string baseName = readString(from);
        bool fixed = readInt(from) == 1;
        bool recursive = readInt(from) == 1;
        string hashAlgo = readString(from);

        Path tmp = createTempDir();
        Path tmp2 = tmp + "/" + baseName;
        restorePath(tmp2, from);

        startWork();
        Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
        stopWork();

        writeString(path, to);

        deletePath(tmp);
        break;
    }

    case wopAddTextToStore: {
        string suffix = readString(from);
        string s = readString(from);
        PathSet refs = readStorePaths(from);
        startWork();
        Path path = store->addTextToStore(suffix, s, refs);
        stopWork();
        writeString(path, to);
        break;
    }

    case wopBuildDerivations: {
        PathSet drvs = readStorePaths(from);
        startWork();
        store->buildDerivations(drvs);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopEnsurePath: {
        Path path = readStorePath(from);
        startWork();
        store->ensurePath(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddTempRoot: {
        Path path = readStorePath(from);
        startWork();
        store->addTempRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddIndirectRoot: {
        Path path = absPath(readString(from));
        startWork();
        store->addIndirectRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopSyncWithGC: {
        startWork();
        store->syncWithGC();
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopFindRoots: {
        startWork();
        Roots roots = store->findRoots();
        stopWork();
        writeInt(roots.size(), to);
        for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
            writeString(i->first, to);
            writeString(i->second, to);
        }
        break;
    }

    case wopCollectGarbage: {
        GCAction action = (GCAction) readInt(from);
        PathSet pathsToDelete = readStorePaths(from);
        bool ignoreLiveness = readInt(from);

        PathSet result;
        unsigned long long bytesFreed;

        startWork();
        if (ignoreLiveness)
            throw Error("you are not allowed to ignore liveness");
        store->collectGarbage(action, pathsToDelete, ignoreLiveness,
            result, bytesFreed);
        stopWork();

        writeStringSet(result, to);
        writeInt(bytesFreed & 0xffffffff, to);
        writeInt(bytesFreed >> 32, to);

        break;
    }

    default:
        throw Error(format("invalid operation %1%") % op);
    }
}

static void processConnection()
{
    canSendStderr = false;
    writeToStderr = tunnelStderr;

    /* Allow us to receive SIGPOLL for events on the client socket. */
    setSigPollAction(false);
    if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
        throw SysError("F_SETOWN");
    if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | FASYNC) == -1)
        throw SysError("F_SETFL");

    /* Exchange the greeting. */
    unsigned int magic = readInt(from);
    if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
    verbosity = (Verbosity) readInt(from);
    writeInt(WORKER_MAGIC_2, to);

    /* Send startup error messages to the client. */
    startWork();

    try {

        /* Prevent users from doing something very dangerous. */
        if (setuidMode && geteuid() == 0 &&
            querySetting("build-users", Strings()).size() == 0)
            throw Error("if you run `nix-worker' setuid root, then you MUST set `build-users'!");

        /* Open the store. */
        store = boost::shared_ptr<StoreAPI>(new LocalStore(true));

        stopWork();

    } catch (Error & e) {
        stopWork(false, e.msg());
        return;
    }

    /* Process client requests. */
    unsigned int opCount = 0;

    while (true) {
        WorkerOp op;
        try {
            op = (WorkerOp) readInt(from);
        } catch (EndOfFile & e) {
            break;
        }

        opCount++;

        try {
            performOp(from, to, op);
        } catch (Error & e) {
            stopWork(false, e.msg());
        }

        assert(!canSendStderr);
    };

    printMsg(lvlError, format("%1% worker operations") % opCount);
}

static void setSigChldAction(bool ignore)
{
    struct sigaction act, oact;
    act.sa_handler = ignore ? SIG_IGN : SIG_DFL;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, &oact))
        throw SysError("setting SIGCHLD handler");
}

static void daemonLoop()
{
    /* Get rid of children automatically; don't let them become
       zombies. */
    setSigChldAction(true);

    /* Create and bind to a Unix domain socket. */
    AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
    if (fdSocket == -1)
        throw SysError("cannot create Unix domain socket");

    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    if (socketPath.size() >= sizeof(addr.sun_path))
        throw Error(format("socket path `%1%' is too long") % socketPath);
    strcpy(addr.sun_path, socketPath.c_str());

    unlink(socketPath.c_str());

    /* Make sure that the socket is created with 0666 permission
       (everybody can connect). */
    mode_t oldMode = umask(0111);
    int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
    umask(oldMode);
    if (res == -1)
        throw SysError(format("cannot bind to socket `%1%'") % socketPath);

    if (listen(fdSocket, 5) == -1)
        throw SysError(format("cannot listen on socket `%1%'") % socketPath);

    /* Loop accepting connections. */
    while (1) {

        try {
            /* Important: the server process *cannot* open the
               Berkeley DB environment, because it doesn't like forks
               very much. */
            assert(!store);

            /* Accept a connection. */
            struct sockaddr_un remoteAddr;
            socklen_t remoteAddrLen = sizeof(remoteAddr);

            AutoCloseFD remote = accept(fdSocket,
                (struct sockaddr *) &remoteAddr, &remoteAddrLen);
            checkInterrupt();
            if (remote == -1)
                throw SysError("accepting connection");

            printMsg(lvlInfo, format("accepted connection %1%") % remote);

            /* Fork a child to handle the connection. */
            pid_t child;
            child = fork();

            switch (child) {

            case -1:
                throw SysError("unable to fork");

            case 0:
                try { /* child */

                    /* Background the worker. */
                    if (setsid() == -1)
                        throw SysError(format("creating a new session"));

                    /* Restore normal handling of SIGCHLD. */
                    setSigChldAction(false);

                    /* Handle the connection. */
                    from.fd = remote;
                    to.fd = remote;
                    processConnection();

                } catch (std::exception & e) {
                    std::cerr << format("child error: %1%\n") % e.what();
                }
                exit(0);
            }

        } catch (Interrupted & e) {
            throw;
        } catch (Error & e) {
            printMsg(lvlError, format("error processing connection: %1%") % e.msg());
        }
    }
}

void run(Strings args)
{
    bool slave = false;
    bool daemon = false;

    for (Strings::iterator i = args.begin(); i != args.end(); ) {
        string arg = *i++;
        if (arg == "--slave") slave = true;
        if (arg == "--daemon") daemon = true;
    }

    if (slave) {
        /* This prevents us from receiving signals from the terminal
           when we're running in setuid mode. */
        if (setsid() == -1)
            throw SysError(format("creating a new session"));

        processConnection();
    }

    else if (daemon) {
        if (setuidMode)
            throw Error("daemon cannot be started in setuid mode");
        chdir("/");
        daemonLoop();
    }

    else
        throw Error("must be run in either --slave or --daemon mode");
}

#include "help.txt.hh"

void printHelp()
{
    std::cout << string((char *) helpText, sizeof helpText);
}

string programId = "nix-worker";
<|endoftext|>


<commit_before>#include "ParticleSystem.h"

ParticleSystem::ParticleSystem(sgct::Engine* engine)
{
	mEngine = engine;
	mInitialized = false;
	mBillBoardVB = 0;
	mParticlePositionBuffer = 0;
}

void ParticleSystem::initialize()
{
	static const GLfloat vertexBufferData[] = {
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		-0.5f, 0.5f, 0.0f,
		0.5f, 0.5f, 0.0f
	};

	glGenBuffers(1, &mBillBoardVB);
	glBindBuffer(GL_ARRAY_BUFFER, mBillBoardVB);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexBufferData),
				 vertexBufferData, GL_STATIC_DRAW);

	glGenBuffers(1, &mParticlePositionBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, mParticlePositionBuffer);
	glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES*4*sizeof(GLubyte),
				 NULL, GL_STREAM_DRAW);

	mInitialized = true;
}

void ParticleSystem::draw()
{
	if(mInitialized)
	{
		// cout << "Drawing all them pritty particles\n";
	}
}

void ParticleSystem::destroy()
{
	if(mInitialized)
	{
		glDeleteBuffers(1, &mBillBoardVB);
		glDeleteBuffers(1, &mParticlePositionBuffer);
		mInitialized = false;
	}
}

void ParticleSystem::move()
{
	// do gravity and shit
	for (int i = 0; i < MAX_PARTICLES; i++)
	{
		Particle& p = mParticles[i];
		// pull it down!
		p.mSpeed += glm::vec3(0.0f, -9.81f, 0.0f);
		p.mPosition += p.mSpeed;
		// kill it? Has it passed some sort of boundary?
		if(p.mPosition.y < 0)
		{
			p.mLife -= 0.01f; // reduce life
		}
	}
}
<commit_msg>Added a nice delta var<commit_after>#include "ParticleSystem.h"

ParticleSystem::ParticleSystem(sgct::Engine* engine)
{
	mEngine = engine;
	mInitialized = false;
	mBillBoardVB = 0;
	mParticlePositionBuffer = 0;
}

void ParticleSystem::initialize()
{
	static const GLfloat vertexBufferData[] = {
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		-0.5f, 0.5f, 0.0f,
		0.5f, 0.5f, 0.0f
	};

	glGenBuffers(1, &mBillBoardVB);
	glBindBuffer(GL_ARRAY_BUFFER, mBillBoardVB);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexBufferData),
				 vertexBufferData, GL_STATIC_DRAW);

	glGenBuffers(1, &mParticlePositionBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, mParticlePositionBuffer);
	glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES*4*sizeof(GLubyte),
				 NULL, GL_STREAM_DRAW);

	mInitialized = true;
}

void ParticleSystem::draw()
{
	if(mInitialized)
	{
		// cout << "Drawing all them pritty particles\n";
	}
}

void ParticleSystem::destroy()
{
	if(mInitialized)
	{
		glDeleteBuffers(1, &mBillBoardVB);
		glDeleteBuffers(1, &mParticlePositionBuffer);
		mInitialized = false;
	}
}

void ParticleSystem::move()
{
	// do gravity and shit
	for (int i = 0; i < MAX_PARTICLES; i++)
	{
		float delta = 0.01;
		Particle& p = mParticles[i];
		// pull it down!
		p.mSpeed += glm::vec3(0.0f, -9.81f, 0.0f) * delta;
		p.mPosition += p.mSpeed;
		// kill it? Has it passed some sort of boundary?
		if(p.mPosition.y < 0)
		{
			p.mLife -= 0.01f; // reduce life
		}
	}
}
<|endoftext|>


<commit_before>/*********************************************************************
** Author: Zach Colbert
** Date: 22 November 2017
** Description:
*********************************************************************/<commit_msg>First draft<commit_after>/*********************************************************************
** Author: Zach Colbert
** Date: 22 November 2017
** Description:
*********************************************************************/

/*
Write a class called T3Reader that uses the Board class to re-run a game of TicTacToe from moves that it reads from a text file. This class will have a field for a Board object and a field to keep track of which player's turn it is.

It should have a constructor that takes a char parameter that specifies whether 'x' or 'o' should have the first move.

It should have a method called readGameFile that takes a string parameter that gives the name of the game file. The readGameFile method should keep looping, reading a move from the file, and sending it to the board (with makeMove).  The readGameFile method should return false if any of the moves is for a square that was already occupied, or if there are still additional moves in the file after the game has finished.  Otherwise it should return true.

Make sure you close the input file in every case.
*/

// INCLUDES GO HERE
#include "T3Reader.hpp"
#include <iostream>
#include <fstream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;

// ADD DOCUMENTATION!
T3Reader::T3Reader(char firstTurn)
{
    nextTurn = firstTurn;
}

// ADD DOCUMENTATION!
bool T3Reader::readGameFile(string fileName)
{
    // Initialize variables to store the coords and results for each move
    int xCoord, yCoord;
    bool moveResult;

    // Open the input file
    std::ifstream inFile(fileName);

    // Loop until the end of the file
    while (inFile >> xCoord >> yCoord)
    {
        // If the game is finished, return false
        if (gameBoard.gameState() != 3)
        {
            // Make sure to close the file!
            inFile.close();
            return false;
        }

        // Otherwise, record the result of each move
        moveResult = gameBoard.makeMove(xCoord, yCoord, nextTurn);

        // Return false if the move failed
        if (moveResult == false)
        {
            // Make sure to close the file!
            inFile.close();
            return false;
        }

        // Toggle the next player
        if (nextTurn == 'x')
        {
            nextTurn = 'o';
        }
        else
        {
            nextTurn = 'x';
        }
    }

    // If you got this far, it worked! Close the file and return true.
    inFile.close();
    return true;
}<|endoftext|>


<commit_before>// Copyright (c) 2015-2017 William W. Fisher (at gmail dot com)
// This file is distributed under the MIT License.

#include "ofp/sys/engine.h"
#include "ofp/log.h"
#include "ofp/sys/tcp_connection.h"
#include "ofp/sys/tcp_server.h"
#include "ofp/sys/udp_server.h"

using namespace ofp;
using namespace ofp::sys;

Engine::Engine(Driver *driver)
    : driver_{driver}, signals_{io_}, stopTimer_{io_}, idleTimer_{io_} {
  log_debug("Engine ready");
}

Engine::~Engine() {
  // Shutdown all existing connections and servers.
  (void)close(0);
  log_debug("Engine shutting down");
}

UInt64 Engine::listen(ChannelOptions options, UInt64 securityId,
                      const IPv6Endpoint &localEndpoint,
                      ProtocolVersions versions,
                      ChannelListener::Factory listenerFactory,
                      std::error_code &error) {
  // Verify the channel options.
  if (!AreChannelOptionsValid(options)) {
    error = std::make_error_code(std::errc::invalid_argument);
    return 0;
  }

  auto svrPtr = TCP_Server::create(this, options, securityId, localEndpoint,
                                   versions, listenerFactory, error);
  if (error)
    return 0;

  // If there's no error, the TCP_Server has registered itself with the engine.
  UInt64 connId = svrPtr->connectionId();
  assert(connId > 0);

  return connId;
}

UInt64 Engine::connect(
    ChannelOptions options, UInt64 securityId,
    const IPv6Endpoint &remoteEndpoint, ProtocolVersions versions,
    ChannelListener::Factory listenerFactory,
    std::function<void(Channel *, std::error_code)> resultHandler) {
  // Verify the channel options.
  if (!AreChannelOptionsValid(options)) {
    resultHandler(nullptr, std::make_error_code(std::errc::invalid_argument));
    return 0;
  }

  UInt64 connId = 0;

  // Check for CONNECT_UDP option.
  if ((options & ChannelOptions::CONNECT_UDP) != 0) {
    std::error_code error;
    connId = connectUDP(securityId, remoteEndpoint, listenerFactory, error);
    resultHandler(nullptr, error);
    return connId;
  }

  if (securityId != 0) {
    connId = TCP_AsyncConnect<EncryptedSocket>(this, options, securityId,
                                               versions, listenerFactory,
                                               remoteEndpoint, resultHandler);

  } else {
    connId = TCP_AsyncConnect<PlaintextSocket>(this, options, securityId,
                                               versions, listenerFactory,
                                               remoteEndpoint, resultHandler);
  }

  return connId;
}

UInt64 Engine::connectUDP(UInt64 securityId, const IPv6Endpoint &remoteEndpoint,
                          ChannelListener::Factory listenerFactory,
                          std::error_code &error) {
  UInt64 connId = 0;

  if (!udpConnect_) {
    udpConnect_ = UDP_Server::create(this, error);
    if (error) {
      udpConnect_.reset();
    }
  }

  if (udpConnect_) {
    connId = udpConnect_->connect(remoteEndpoint, securityId, listenerFactory,
                                  error);
  }

  return connId;
}

size_t Engine::close(UInt64 connId) {
  if (connId != 0) {
    // Close a specific server or connection.
    TCP_Server *server = findTCPServer(
        [connId](TCP_Server *svr) { return svr->connectionId() == connId; });

    if (server) {
      server->shutdown();
      return 1;
    }

    Connection *conn = findConnId(connId);
    if (conn) {
      conn->shutdown();
      if (conn->flags() & Connection::kManualDelete) {
        delete conn;
      }
      return 1;
    }

    return 0;
  }

  // Close all servers and connections.
  size_t result = serverList_.size() + connList_.size() + (udpConnect_ ? 1 : 0);
  if (result == 0)
    return result;

  log_info("Close all servers and connections");

  std::vector<TCP_Server *> servers;
  servers.swap(serverList_);
  for (auto svr : servers) {
    svr->shutdown();
  }

  std::vector<Connection *> conns;
  conns.swap(connList_);
  for (auto conn : conns) {
    conn->shutdown();
    if (conn->flags() & Connection::kManualDelete) {
      delete conn;
    }
  }

  if (udpConnect_) {
    udpConnect_->shutdown();
    udpConnect_.reset();
  }

  return result;
}

#if LIBOFP_ENABLE_OPENSSL

UInt64 Engine::addIdentity(const std::string &certData,
                           const std::string &keyPassphrase,
                           const std::string &verifier,
                           std::error_code &error) {
  auto idPtr =
      MakeUniquePtr<Identity>(certData, keyPassphrase, verifier, error);
  if (error)
    return 0;

  UInt64 secId = assignSecurityId();
  assert(secId > 0);
  idPtr->setSecurityId(secId);

  log_info("Add TLS identity:", idPtr->subjectName(),
           std::make_pair("tlsid", secId));

  identities_.push_back(std::move(idPtr));

  return secId;
}

Identity *Engine::findIdentity(UInt64 securityId) {
  if (securityId == 0) {
    return nullptr;
  }

  auto iter = std::find_if(identities_.begin(), identities_.end(),
                           [securityId](std::unique_ptr<Identity> &identity) {
                             return identity->securityId() == securityId;
                           });

  return iter != identities_.end() ? iter->get() : nullptr;
}

UInt64 Engine::assignSecurityId() {
  if (++lastSecurityId_ == 0) {
    static_assert(sizeof(lastSecurityId_) == 8, "Expected 64-bit integer");
    log::fatal("Engine::assignSecurityId: Ran out of 64-bit securityIds");
  }
  return lastSecurityId_;
}

#endif  // LIBOFP_ENABLE_OPENSSL

void Engine::run() {
  if (!isRunning_) {
    isRunning_ = true;

    // Set isRunning_ to true when we are in io.run(). This guards against
    // re-entry and provides a flag to test when shutting down.

    asyncIdle();

    io_.run();

    idleTimer_.cancel();

    isRunning_ = false;
  }
}

void Engine::stop(Milliseconds timeout) {
  if (timeout == 0_ms) {
    io_.stop();
  } else {
    asio::error_code error;
    stopTimer_.expires_after(timeout, error);
    if (error) {
      io_.stop();
      return;
    }

    stopTimer_.async_wait([this](const asio::error_code &err) {
      if (err != asio::error::operation_aborted) {
        io_.stop();
      }
    });
  }
}

bool Engine::registerDatapath(Connection *channel) {
  DatapathID dpid = channel->datapathId();
  UInt8 auxID = channel->auxiliaryId();

  if (auxID == 0) {
    assert(!IsChannelTransportUDP(channel->transport()));

    // Insert main connection's datapathID into the dpidMap, if not present
    // already.
    auto pair = dpidMap_.insert({dpid, channel});
    if (!pair.second) {
      // DatapathID already exists in map. If the existing channel is
      // different, close it and replace it with the new one.
      auto item = pair.first;
      if (item->second != channel) {
        log_error("Datapath conflict between main connections detected:", dpid,
                  std::make_pair("prev_conn_id", item->second->connectionId()),
                  std::make_pair("conn_id", channel->connectionId()));
        Connection *old = item->second;
        item->second = channel;
        old->shutdown(true);  // force immediate reset

      } else {
        log_warning("Datapath is already registered:", dpid,
                    std::make_pair("conn_id", channel->connectionId()));
      }
    }

  } else {
    assert(auxID != 0);

    // Look up main connection and assign auxiliary connection to it. If we
    // don't find a main connection, close the auxiliary channel.
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      Connection *parent = item->second;
      if (parent->flags() & Connection::kPermitsAuxiliary) {
        channel->setMainConnection(parent, auxID);
      } else {
        log_warning(
            "registerDatapath: Auxiliary connection not permitted for datapath",
            dpid, "aux", static_cast<int>(auxID),
            std::make_pair("conn_id", channel->connectionId()));
        return false;
      }

    } else {
      log_warning("registerDatapath: Main connection not found for datapath",
                  dpid, "aux", static_cast<int>(auxID),
                  std::make_pair("conn_id", channel->connectionId()));
      return false;
    }
  }

  return true;
}

void Engine::releaseDatapath(Connection *channel) {
  DatapathID dpid = channel->datapathId();
  UInt8 auxID = channel->auxiliaryId();

  // We only need to release the datapath for main connections.
  if (auxID == 0) {
    // When releasing the datapathID for a main connection, we need to
    // verify that the datapathID _is_ registered to this connection.
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      if (item->second == channel)
        dpidMap_.erase(item);
    }
  }
}

UInt64 Engine::registerServer(TCP_Server *server) {
  assert(!serverListLock_);
  serverList_.push_back(server);
  return assignConnectionId();
}

void Engine::releaseServer(TCP_Server *server) {
  assert(!serverListLock_);
  auto iter = std::find(serverList_.begin(), serverList_.end(), server);
  if (iter != serverList_.end()) {
    assert(*iter == server);
    serverList_.erase(iter);
  }
}

UInt64 Engine::registerConnection(Connection *connection) {
  assert(!connListLock_);
  connList_.push_back(connection);
  return assignConnectionId();
}

void Engine::releaseConnection(Connection *connection) {
  assert(!connListLock_);
  auto iter = std::find(connList_.begin(), connList_.end(), connection);
  if (iter != connList_.end()) {
    assert(*iter == connection);
    connList_.erase(iter);
  }
}

void Engine::installSignalHandlers(std::function<void()> callback) {
  log_debug("Install signal handlers");

  std::error_code ignore;
  signals_.cancel(ignore);
  signals_.clear(ignore);
  signals_.add(SIGINT);
  signals_.add(SIGTERM);

  OFP_BEGIN_IGNORE_PADDING

  signals_.async_wait(
      [this, callback](const asio::error_code &error, int signum) {
        if (!error) {
          const char *signame = (signum == SIGTERM)
                                    ? "SIGTERM"
                                    : (signum == SIGINT) ? "SIGINT" : "???";
          log_info("Signal received:", signame);

          signals_.cancel();
          idleTimer_.cancel();
          (void)this->close(0);

          if (callback)
            callback();

        } else {
          log_error("Signal error", signum, error);
        }
      });

  OFP_END_IGNORE_PADDING
}

UInt64 Engine::assignConnectionId() {
  if (++lastConnId_ == 0) {
    static_assert(sizeof(lastConnId_) == 8, "Expected 64-bit integer");
    log::fatal("Engine::assignConnectionId: Ran out of 64-bit connectionIds");
  }
  return lastConnId_;
}

Connection *Engine::findDatapath(UInt64 connId, const DatapathID &dpid) const {
  // Use the connectionId, it it's non-zero.
  if (connId != 0) {
    return findConnId(connId);
  }

  // If datapath ID is not all zeros, use it to look up the connection.
  if (!dpid.empty()) {
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      return item->second;
    }
    return nullptr;
  }

  assert(dpid.empty() && connId == 0);

  return nullptr;
}

Connection *Engine::findConnId(UInt64 connId) const {
  assert(connId != 0);
  // assert(std::is_sorted(connList_.begin(), connList_.end(),
  //  [](Connection *lhs, Connection *rhs) {
  //  return lhs->connectionId() < rhs->connectionId();
  //}));

  // Use binary search to locate connection with connID.
  auto iter = std::lower_bound(
      connList_.begin(), connList_.end(), connId,
      [](Connection *conn, UInt64 cid) { return conn->connectionId() < cid; });

  if (iter != connList_.end()) {
    return *iter;
  }

  return nullptr;
}

void Engine::asyncIdle() {
  asio::error_code error;

  idleTimer_.expires_after(1000_ms, error);
  idleTimer_.async_wait([this](const asio::error_code &err) {
    if (!err) {
      TimePoint now = TimeClock::now();
      forEachConnection([this, &now](Connection *conn) {
        if (conn->flags() & Connection::kConnectionUp) {
          conn->tickle(now);
        }
      });
      asyncIdle();
    }
  });
}

void Engine::setAlertCallback(AlertCallback callback, void *context) {
  alertCallback_ = callback;
  alertContext_ = context;
}

void Engine::alert(Channel *conn, const std::string &alert,
                   const ByteRange &data) {
  if (alertCallback_) {
    alertCallback_(conn, alert, data, alertContext_);
  }
}
<commit_msg>Verify conn_id of connection found with binary search.<commit_after>// Copyright (c) 2015-2017 William W. Fisher (at gmail dot com)
// This file is distributed under the MIT License.

#include "ofp/sys/engine.h"
#include "ofp/log.h"
#include "ofp/sys/tcp_connection.h"
#include "ofp/sys/tcp_server.h"
#include "ofp/sys/udp_server.h"

using namespace ofp;
using namespace ofp::sys;

Engine::Engine(Driver *driver)
    : driver_{driver}, signals_{io_}, stopTimer_{io_}, idleTimer_{io_} {
  log_debug("Engine ready");
}

Engine::~Engine() {
  // Shutdown all existing connections and servers.
  (void)close(0);
  log_debug("Engine shutting down");
}

UInt64 Engine::listen(ChannelOptions options, UInt64 securityId,
                      const IPv6Endpoint &localEndpoint,
                      ProtocolVersions versions,
                      ChannelListener::Factory listenerFactory,
                      std::error_code &error) {
  // Verify the channel options.
  if (!AreChannelOptionsValid(options)) {
    error = std::make_error_code(std::errc::invalid_argument);
    return 0;
  }

  auto svrPtr = TCP_Server::create(this, options, securityId, localEndpoint,
                                   versions, listenerFactory, error);
  if (error)
    return 0;

  // If there's no error, the TCP_Server has registered itself with the engine.
  UInt64 connId = svrPtr->connectionId();
  assert(connId > 0);

  return connId;
}

UInt64 Engine::connect(
    ChannelOptions options, UInt64 securityId,
    const IPv6Endpoint &remoteEndpoint, ProtocolVersions versions,
    ChannelListener::Factory listenerFactory,
    std::function<void(Channel *, std::error_code)> resultHandler) {
  // Verify the channel options.
  if (!AreChannelOptionsValid(options)) {
    resultHandler(nullptr, std::make_error_code(std::errc::invalid_argument));
    return 0;
  }

  UInt64 connId = 0;

  // Check for CONNECT_UDP option.
  if ((options & ChannelOptions::CONNECT_UDP) != 0) {
    std::error_code error;
    connId = connectUDP(securityId, remoteEndpoint, listenerFactory, error);
    resultHandler(nullptr, error);
    return connId;
  }

  if (securityId != 0) {
    connId = TCP_AsyncConnect<EncryptedSocket>(this, options, securityId,
                                               versions, listenerFactory,
                                               remoteEndpoint, resultHandler);

  } else {
    connId = TCP_AsyncConnect<PlaintextSocket>(this, options, securityId,
                                               versions, listenerFactory,
                                               remoteEndpoint, resultHandler);
  }

  return connId;
}

UInt64 Engine::connectUDP(UInt64 securityId, const IPv6Endpoint &remoteEndpoint,
                          ChannelListener::Factory listenerFactory,
                          std::error_code &error) {
  UInt64 connId = 0;

  if (!udpConnect_) {
    udpConnect_ = UDP_Server::create(this, error);
    if (error) {
      udpConnect_.reset();
    }
  }

  if (udpConnect_) {
    connId = udpConnect_->connect(remoteEndpoint, securityId, listenerFactory,
                                  error);
  }

  return connId;
}

size_t Engine::close(UInt64 connId) {
  if (connId != 0) {
    // Close a specific server or connection.
    TCP_Server *server = findTCPServer(
        [connId](TCP_Server *svr) { return svr->connectionId() == connId; });

    if (server) {
      server->shutdown();
      return 1;
    }

    Connection *conn = findConnId(connId);
    if (conn) {
      conn->shutdown();
      if (conn->flags() & Connection::kManualDelete) {
        delete conn;
      }
      return 1;
    }

    return 0;
  }

  // Close all servers and connections.
  size_t result = serverList_.size() + connList_.size() + (udpConnect_ ? 1 : 0);
  if (result == 0)
    return result;

  log_info("Close all servers and connections");

  std::vector<TCP_Server *> servers;
  servers.swap(serverList_);
  for (auto svr : servers) {
    svr->shutdown();
  }

  std::vector<Connection *> conns;
  conns.swap(connList_);
  for (auto conn : conns) {
    conn->shutdown();
    if (conn->flags() & Connection::kManualDelete) {
      delete conn;
    }
  }

  if (udpConnect_) {
    udpConnect_->shutdown();
    udpConnect_.reset();
  }

  return result;
}

#if LIBOFP_ENABLE_OPENSSL

UInt64 Engine::addIdentity(const std::string &certData,
                           const std::string &keyPassphrase,
                           const std::string &verifier,
                           std::error_code &error) {
  auto idPtr =
      MakeUniquePtr<Identity>(certData, keyPassphrase, verifier, error);
  if (error)
    return 0;

  UInt64 secId = assignSecurityId();
  assert(secId > 0);
  idPtr->setSecurityId(secId);

  log_info("Add TLS identity:", idPtr->subjectName(),
           std::make_pair("tlsid", secId));

  identities_.push_back(std::move(idPtr));

  return secId;
}

Identity *Engine::findIdentity(UInt64 securityId) {
  if (securityId == 0) {
    return nullptr;
  }

  auto iter = std::find_if(identities_.begin(), identities_.end(),
                           [securityId](std::unique_ptr<Identity> &identity) {
                             return identity->securityId() == securityId;
                           });

  return iter != identities_.end() ? iter->get() : nullptr;
}

UInt64 Engine::assignSecurityId() {
  if (++lastSecurityId_ == 0) {
    static_assert(sizeof(lastSecurityId_) == 8, "Expected 64-bit integer");
    log::fatal("Engine::assignSecurityId: Ran out of 64-bit securityIds");
  }
  return lastSecurityId_;
}

#endif  // LIBOFP_ENABLE_OPENSSL

void Engine::run() {
  if (!isRunning_) {
    isRunning_ = true;

    // Set isRunning_ to true when we are in io.run(). This guards against
    // re-entry and provides a flag to test when shutting down.

    asyncIdle();

    io_.run();

    idleTimer_.cancel();

    isRunning_ = false;
  }
}

void Engine::stop(Milliseconds timeout) {
  if (timeout == 0_ms) {
    io_.stop();
  } else {
    asio::error_code error;
    stopTimer_.expires_after(timeout, error);
    if (error) {
      io_.stop();
      return;
    }

    stopTimer_.async_wait([this](const asio::error_code &err) {
      if (err != asio::error::operation_aborted) {
        io_.stop();
      }
    });
  }
}

bool Engine::registerDatapath(Connection *channel) {
  DatapathID dpid = channel->datapathId();
  UInt8 auxID = channel->auxiliaryId();

  if (auxID == 0) {
    assert(!IsChannelTransportUDP(channel->transport()));

    // Insert main connection's datapathID into the dpidMap, if not present
    // already.
    auto pair = dpidMap_.insert({dpid, channel});
    if (!pair.second) {
      // DatapathID already exists in map. If the existing channel is
      // different, close it and replace it with the new one.
      auto item = pair.first;
      if (item->second != channel) {
        log_error("Datapath conflict between main connections detected:", dpid,
                  std::make_pair("prev_conn_id", item->second->connectionId()),
                  std::make_pair("conn_id", channel->connectionId()));
        Connection *old = item->second;
        item->second = channel;
        old->shutdown(true);  // force immediate reset

      } else {
        log_warning("Datapath is already registered:", dpid,
                    std::make_pair("conn_id", channel->connectionId()));
      }
    }

  } else {
    assert(auxID != 0);

    // Look up main connection and assign auxiliary connection to it. If we
    // don't find a main connection, close the auxiliary channel.
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      Connection *parent = item->second;
      if (parent->flags() & Connection::kPermitsAuxiliary) {
        channel->setMainConnection(parent, auxID);
      } else {
        log_warning(
            "registerDatapath: Auxiliary connection not permitted for datapath",
            dpid, "aux", static_cast<int>(auxID),
            std::make_pair("conn_id", channel->connectionId()));
        return false;
      }

    } else {
      log_warning("registerDatapath: Main connection not found for datapath",
                  dpid, "aux", static_cast<int>(auxID),
                  std::make_pair("conn_id", channel->connectionId()));
      return false;
    }
  }

  return true;
}

void Engine::releaseDatapath(Connection *channel) {
  DatapathID dpid = channel->datapathId();
  UInt8 auxID = channel->auxiliaryId();

  // We only need to release the datapath for main connections.
  if (auxID == 0) {
    // When releasing the datapathID for a main connection, we need to
    // verify that the datapathID _is_ registered to this connection.
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      if (item->second == channel)
        dpidMap_.erase(item);
    }
  }
}

UInt64 Engine::registerServer(TCP_Server *server) {
  assert(!serverListLock_);
  serverList_.push_back(server);
  return assignConnectionId();
}

void Engine::releaseServer(TCP_Server *server) {
  assert(!serverListLock_);
  auto iter = std::find(serverList_.begin(), serverList_.end(), server);
  if (iter != serverList_.end()) {
    assert(*iter == server);
    serverList_.erase(iter);
  }
}

UInt64 Engine::registerConnection(Connection *connection) {
  assert(!connListLock_);
  connList_.push_back(connection);
  return assignConnectionId();
}

void Engine::releaseConnection(Connection *connection) {
  assert(!connListLock_);
  auto iter = std::find(connList_.begin(), connList_.end(), connection);
  if (iter != connList_.end()) {
    assert(*iter == connection);
    connList_.erase(iter);
  }
}

void Engine::installSignalHandlers(std::function<void()> callback) {
  log_debug("Install signal handlers");

  std::error_code ignore;
  signals_.cancel(ignore);
  signals_.clear(ignore);
  signals_.add(SIGINT);
  signals_.add(SIGTERM);

  OFP_BEGIN_IGNORE_PADDING

  signals_.async_wait(
      [this, callback](const asio::error_code &error, int signum) {
        if (!error) {
          const char *signame = (signum == SIGTERM)
                                    ? "SIGTERM"
                                    : (signum == SIGINT) ? "SIGINT" : "???";
          log_info("Signal received:", signame);

          signals_.cancel();
          idleTimer_.cancel();
          (void)this->close(0);

          if (callback)
            callback();

        } else {
          log_error("Signal error", signum, error);
        }
      });

  OFP_END_IGNORE_PADDING
}

UInt64 Engine::assignConnectionId() {
  if (++lastConnId_ == 0) {
    static_assert(sizeof(lastConnId_) == 8, "Expected 64-bit integer");
    log::fatal("Engine::assignConnectionId: Ran out of 64-bit connectionIds");
  }
  return lastConnId_;
}

Connection *Engine::findDatapath(UInt64 connId, const DatapathID &dpid) const {
  // Use the connectionId, it it's non-zero.
  if (connId != 0) {
    return findConnId(connId);
  }

  // If datapath ID is not all zeros, use it to look up the connection.
  if (!dpid.empty()) {
    auto item = dpidMap_.find(dpid);
    if (item != dpidMap_.end()) {
      return item->second;
    }
    return nullptr;
  }

  assert(dpid.empty() && connId == 0);

  return nullptr;
}

Connection *Engine::findConnId(UInt64 connId) const {
  assert(connId != 0);
  assert(std::is_sorted(connList_.begin(), connList_.end(),
    [](Connection *lhs, Connection *rhs) {
    return lhs->connectionId() < rhs->connectionId();
  }));

  // Use binary search to locate connection with connID.
  auto iter = std::lower_bound(
      connList_.begin(), connList_.end(), connId,
      [](Connection *conn, UInt64 cid) { return conn->connectionId() < cid; });

  if (iter != connList_.end()) {
    Connection *conn = *iter;
    if (conn->connectionId() == connId)
      return conn;
  }

  return nullptr;
}

void Engine::asyncIdle() {
  asio::error_code error;

  idleTimer_.expires_after(1000_ms, error);
  idleTimer_.async_wait([this](const asio::error_code &err) {
    if (!err) {
      TimePoint now = TimeClock::now();
      forEachConnection([this, &now](Connection *conn) {
        if (conn->flags() & Connection::kConnectionUp) {
          conn->tickle(now);
        }
      });
      asyncIdle();
    }
  });
}

void Engine::setAlertCallback(AlertCallback callback, void *context) {
  alertCallback_ = callback;
  alertContext_ = context;
}

void Engine::alert(Channel *conn, const std::string &alert,
                   const ByteRange &data) {
  if (alertCallback_) {
    alertCallback_(conn, alert, data, alertContext_);
  }
}
<|endoftext|>


<commit_before>#include "selectors.hh"

#include <boost/regex.hpp>
#include <algorithm>

namespace Kakoune
{

static bool is_eol(char c)
{
    return c == '\n';
}

static bool is_blank(char c)
{
    return c == ' ' or c == '\t';
}

static bool is_word(char c)
{
    if (c >= '0' and c <= '9')
        return true;
    if (c >= 'a' and c <= 'z')
        return true;
    if (c >= 'A' and c <= 'Z')
        return true;
    if (c == '_')
        return true;
    return false;
}

static bool is_punctuation(char c)
{
    return not (is_word(c) or is_blank(c) or is_eol(c));
}

enum class CharCategories
{
    Blank,
    EndOfLine,
    Word,
    Punctuation,
};

template<bool punctuation_is_not_word = true>
static CharCategories categorize(char c)
{
    if (is_word(c))
        return CharCategories::Word;
    if (is_eol(c))
        return CharCategories::EndOfLine;
    if (is_blank(c))
        return CharCategories::Blank;
    return punctuation_is_not_word ? CharCategories::Punctuation
                                   : CharCategories::Word;
}

template<typename T>
void skip_while(BufferIterator& it, T condition)
{
    while (not it.is_end() and condition(*it))
        ++it;
}

template<typename T>
void skip_while_reverse(BufferIterator& it, T condition)
{
    while (not it.is_begin() and condition(*it))
        --it;
}

Selection select_to_next_word(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize(*begin) != categorize(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    if (is_punctuation(*begin))
        skip_while(end, is_punctuation);
    else if (is_word(*begin))
        skip_while(end, is_word);

    skip_while(end, is_blank);

    return Selection(begin, end-1);
}

Selection select_to_next_word_end(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize(*begin) != categorize(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);
    BufferIterator end = begin;
    skip_while(end, is_blank);

    if (is_punctuation(*end))
        skip_while(end, is_punctuation);
    else if (is_word(*end))
        skip_while(end, is_word);

    return Selection(begin, end-1);
}

Selection select_to_previous_word(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;

    if (categorize(*begin) != categorize(*(begin-1)))
        --begin;

    skip_while_reverse(begin, is_eol);
    BufferIterator end = begin;
    skip_while_reverse(end, is_blank);

    if (is_punctuation(*end))
        skip_while_reverse(end, is_punctuation);
    else if (is_word(*end))
        skip_while_reverse(end, is_word);

    return Selection(begin, end+1);
}

Selection select_to_next_WORD(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    skip_while(end, [] (char c) { return !is_blank(c) and !is_eol(c); });
    skip_while(end, is_blank);

    return Selection(begin, end-1);
}

Selection select_to_next_WORD_end(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    skip_while(end, is_blank);
    skip_while(end, [] (char c) { return !is_blank(c) and !is_eol(c); });

    return Selection(begin, end-1);
}

Selection select_to_previous_WORD(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin-1)))
        --begin;

    skip_while_reverse(begin, is_eol);
    BufferIterator end = begin;
    skip_while_reverse(end, is_blank);
    skip_while_reverse(end, [] (char c) { return !is_blank(c) and !is_eol(c); });

    return Selection(begin, end+1);
}

Selection select_line(const BufferIterator& cursor)
{
    BufferIterator first = cursor;
    if (*first == '\n' and not first.is_end())
        ++first;

    while (not first.is_begin() and *(first - 1) != '\n')
        --first;

    BufferIterator last = first;
    while (not (last + 1).is_end() and *last != '\n')
        ++last;
    return Selection(first, last);
}

Selection select_matching(const BufferIterator& cursor)
{
    std::vector<char> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
    BufferIterator it = cursor;
    std::vector<char>::iterator match = matching_pairs.end();
    while (not is_eol(*it))
    {
        match = std::find(matching_pairs.begin(), matching_pairs.end(), *it);
        if (match != matching_pairs.end())
            break;
        ++it;
    }
    if (match == matching_pairs.end())
        return Selection(cursor, cursor);

    BufferIterator begin = it;

    if (((match - matching_pairs.begin()) % 2) == 0)
    {
        int level = 0;
        const char opening = *match;
        const char closing = *(match+1);
        while (not it.is_end())
        {
            if (*it == opening)
                ++level;
            else if (*it == closing and --level == 0)
                return Selection(begin, it);

            ++it;
        }
    }
    else
    {
        int level = 0;
        const char opening = *(match-1);
        const char closing = *match;
        while (not it.is_begin())
        {
            if (*it == closing)
                ++level;
            else if (*it == opening and --level == 0)
                return Selection(begin, it);
            --it;
        }
    }
    return Selection(cursor, cursor);
}

Selection select_to(const BufferIterator& cursor, char c, int count, bool inclusive)
{
    BufferIterator end = cursor;
    do
    {
        ++end;
        skip_while(end, [c](char cur) { return not is_eol(cur) and cur != c; });
        if (end.is_end() or is_eol(*end))
            return Selection(cursor, cursor);
    }
    while (--count > 0);

    return Selection(cursor, inclusive ? end : end-1);
}

Selection select_to_reverse(const BufferIterator& cursor, char c, int count, bool inclusive)
{
    BufferIterator end = cursor;
    do
    {
        --end;
        skip_while_reverse(end, [c](char cur) { return not is_eol(cur) and cur != c; });
        if (end.is_begin() or is_eol(*end))
            return Selection(cursor, cursor);
    }
    while (--count > 0);

    return Selection(cursor, inclusive ? end : end+1);
}

Selection select_to_eol(const BufferIterator& cursor)
{
    BufferIterator end = cursor + 1;
    skip_while(end, [](char cur) { return not is_eol(cur); });
    return Selection(cursor, end-1);
}

Selection select_to_eol_reverse(const BufferIterator& cursor)
{
    BufferIterator end = cursor - 1;
    skip_while_reverse(end, [](char cur) { return not is_eol(cur); });
    return Selection(cursor, end.is_begin() ? end : end+1);
}

SelectionList select_whole_lines(const Selection& selection)
{
     BufferIterator first = selection.first();
     BufferIterator last =  selection.last();
     BufferIterator& to_line_start = first <= last ? first : last;
     BufferIterator& to_line_end = first <= last ? last : first;

     skip_while_reverse(to_line_start, [](char cur) { return not is_eol(cur); });
     skip_while(to_line_end, [](char cur) { return not is_eol(cur); });

     if (to_line_start != to_line_end)
         ++to_line_start;

     SelectionList result;
     result.push_back(Selection(first, last));
     return result;
}

Selection select_next_match(const BufferIterator& cursor,
                            const std::string& regex)
{
    boost::regex ex(regex);

    BufferIterator begin = cursor;
    BufferIterator end = cursor;
    Selection::CaptureList captures;

    try
    {
        boost::match_results<BufferIterator> matches;

        if (boost::regex_search(cursor, cursor.buffer().end(), matches,
                                ex))
        {
            begin = matches[0].first;
            end   = matches[0].second;
            std::copy(matches.begin(), matches.end(),
                      std::back_inserter(captures));
        }
        else if (boost::regex_search(cursor.buffer().begin(), cursor, matches,
                                     ex))
        {
            begin = matches[0].first;
            end   = matches[0].second;
            std::copy(matches.begin(), matches.end(),
                      std::back_inserter(captures));
        }
    }
    catch (boost::regex_error& err)
    {
        throw runtime_error("regex error");
    }

    if (begin == end)
        ++end;

    return Selection(begin, end - 1, std::move(captures));
}

typedef boost::regex_iterator<BufferIterator> RegexIterator;

SelectionList select_all_matches(const Selection& selection,
                                 const std::string& regex)
{
    boost::regex ex(regex);
    RegexIterator re_it(selection.begin(), selection.end(), ex);
    RegexIterator re_end;

    SelectionList result;
    for (; re_it != re_end; ++re_it)
    {
        BufferIterator begin = (*re_it)[0].first;
        BufferIterator end   = (*re_it)[0].second;

        Selection::CaptureList captures(re_it->begin(), re_it->end());

        result.push_back(Selection(begin, begin == end ? end : end-1,
                                   std::move(captures)));
    }
    return result;
}

SelectionList split_selection(const Selection& selection,
                              const std::string& separator_regex)
{
    boost::regex ex(separator_regex);
    RegexIterator re_it(selection.begin(), selection.end(), ex,
                        boost::regex_constants::match_nosubs);
    RegexIterator re_end;

    SelectionList result;
    BufferIterator begin = selection.begin();
    for (; re_it != re_end; ++re_it)
    {
        BufferIterator end = (*re_it)[0].first;

        result.push_back(Selection(begin, (begin == end) ? end : end-1));
        begin = (*re_it)[0].second;
    }
    result.push_back(Selection(begin, selection.last()));
    return result;
}

}
<commit_msg>Selectors: fix select_whole_lines<commit_after>#include "selectors.hh"

#include <boost/regex.hpp>
#include <algorithm>

namespace Kakoune
{

static bool is_eol(char c)
{
    return c == '\n';
}

static bool is_blank(char c)
{
    return c == ' ' or c == '\t';
}

static bool is_word(char c)
{
    if (c >= '0' and c <= '9')
        return true;
    if (c >= 'a' and c <= 'z')
        return true;
    if (c >= 'A' and c <= 'Z')
        return true;
    if (c == '_')
        return true;
    return false;
}

static bool is_punctuation(char c)
{
    return not (is_word(c) or is_blank(c) or is_eol(c));
}

enum class CharCategories
{
    Blank,
    EndOfLine,
    Word,
    Punctuation,
};

template<bool punctuation_is_not_word = true>
static CharCategories categorize(char c)
{
    if (is_word(c))
        return CharCategories::Word;
    if (is_eol(c))
        return CharCategories::EndOfLine;
    if (is_blank(c))
        return CharCategories::Blank;
    return punctuation_is_not_word ? CharCategories::Punctuation
                                   : CharCategories::Word;
}

template<typename T>
void skip_while(BufferIterator& it, T condition)
{
    while (not it.is_end() and condition(*it))
        ++it;
}

template<typename T>
void skip_while_reverse(BufferIterator& it, T condition)
{
    while (not it.is_begin() and condition(*it))
        --it;
}

Selection select_to_next_word(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize(*begin) != categorize(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    if (is_punctuation(*begin))
        skip_while(end, is_punctuation);
    else if (is_word(*begin))
        skip_while(end, is_word);

    skip_while(end, is_blank);

    return Selection(begin, end-1);
}

Selection select_to_next_word_end(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize(*begin) != categorize(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);
    BufferIterator end = begin;
    skip_while(end, is_blank);

    if (is_punctuation(*end))
        skip_while(end, is_punctuation);
    else if (is_word(*end))
        skip_while(end, is_word);

    return Selection(begin, end-1);
}

Selection select_to_previous_word(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;

    if (categorize(*begin) != categorize(*(begin-1)))
        --begin;

    skip_while_reverse(begin, is_eol);
    BufferIterator end = begin;
    skip_while_reverse(end, is_blank);

    if (is_punctuation(*end))
        skip_while_reverse(end, is_punctuation);
    else if (is_word(*end))
        skip_while_reverse(end, is_word);

    return Selection(begin, end+1);
}

Selection select_to_next_WORD(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    skip_while(end, [] (char c) { return !is_blank(c) and !is_eol(c); });
    skip_while(end, is_blank);

    return Selection(begin, end-1);
}

Selection select_to_next_WORD_end(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin+1)))
        ++begin;

    skip_while(begin, is_eol);

    BufferIterator end = begin+1;

    skip_while(end, is_blank);
    skip_while(end, [] (char c) { return !is_blank(c) and !is_eol(c); });

    return Selection(begin, end-1);
}

Selection select_to_previous_WORD(const BufferIterator& cursor)
{
    BufferIterator begin = cursor;
    if (categorize<false>(*begin) != categorize<false>(*(begin-1)))
        --begin;

    skip_while_reverse(begin, is_eol);
    BufferIterator end = begin;
    skip_while_reverse(end, is_blank);
    skip_while_reverse(end, [] (char c) { return !is_blank(c) and !is_eol(c); });

    return Selection(begin, end+1);
}

Selection select_line(const BufferIterator& cursor)
{
    BufferIterator first = cursor;
    if (*first == '\n' and not first.is_end())
        ++first;

    while (not first.is_begin() and *(first - 1) != '\n')
        --first;

    BufferIterator last = first;
    while (not (last + 1).is_end() and *last != '\n')
        ++last;
    return Selection(first, last);
}

Selection select_matching(const BufferIterator& cursor)
{
    std::vector<char> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
    BufferIterator it = cursor;
    std::vector<char>::iterator match = matching_pairs.end();
    while (not is_eol(*it))
    {
        match = std::find(matching_pairs.begin(), matching_pairs.end(), *it);
        if (match != matching_pairs.end())
            break;
        ++it;
    }
    if (match == matching_pairs.end())
        return Selection(cursor, cursor);

    BufferIterator begin = it;

    if (((match - matching_pairs.begin()) % 2) == 0)
    {
        int level = 0;
        const char opening = *match;
        const char closing = *(match+1);
        while (not it.is_end())
        {
            if (*it == opening)
                ++level;
            else if (*it == closing and --level == 0)
                return Selection(begin, it);

            ++it;
        }
    }
    else
    {
        int level = 0;
        const char opening = *(match-1);
        const char closing = *match;
        while (not it.is_begin())
        {
            if (*it == closing)
                ++level;
            else if (*it == opening and --level == 0)
                return Selection(begin, it);
            --it;
        }
    }
    return Selection(cursor, cursor);
}

Selection select_to(const BufferIterator& cursor, char c, int count, bool inclusive)
{
    BufferIterator end = cursor;
    do
    {
        ++end;
        skip_while(end, [c](char cur) { return not is_eol(cur) and cur != c; });
        if (end.is_end() or is_eol(*end))
            return Selection(cursor, cursor);
    }
    while (--count > 0);

    return Selection(cursor, inclusive ? end : end-1);
}

Selection select_to_reverse(const BufferIterator& cursor, char c, int count, bool inclusive)
{
    BufferIterator end = cursor;
    do
    {
        --end;
        skip_while_reverse(end, [c](char cur) { return not is_eol(cur) and cur != c; });
        if (end.is_begin() or is_eol(*end))
            return Selection(cursor, cursor);
    }
    while (--count > 0);

    return Selection(cursor, inclusive ? end : end+1);
}

Selection select_to_eol(const BufferIterator& cursor)
{
    BufferIterator end = cursor + 1;
    skip_while(end, [](char cur) { return not is_eol(cur); });
    return Selection(cursor, end-1);
}

Selection select_to_eol_reverse(const BufferIterator& cursor)
{
    BufferIterator end = cursor - 1;
    skip_while_reverse(end, [](char cur) { return not is_eol(cur); });
    return Selection(cursor, end.is_begin() ? end : end+1);
}

SelectionList select_whole_lines(const Selection& selection)
{
     BufferIterator first = selection.first();
     BufferIterator last =  selection.last();
     BufferIterator& to_line_start = first <= last ? first : last;
     BufferIterator& to_line_end = first <= last ? last : first;

     --to_line_start;
     skip_while_reverse(to_line_start, [](char cur) { return not is_eol(cur); });
     ++to_line_start;

     skip_while(to_line_end, [](char cur) { return not is_eol(cur); });

     SelectionList result;
     result.push_back(Selection(first, last));
     return result;
}

Selection select_next_match(const BufferIterator& cursor,
                            const std::string& regex)
{
    boost::regex ex(regex);

    BufferIterator begin = cursor;
    BufferIterator end = cursor;
    Selection::CaptureList captures;

    try
    {
        boost::match_results<BufferIterator> matches;

        if (boost::regex_search(cursor, cursor.buffer().end(), matches,
                                ex))
        {
            begin = matches[0].first;
            end   = matches[0].second;
            std::copy(matches.begin(), matches.end(),
                      std::back_inserter(captures));
        }
        else if (boost::regex_search(cursor.buffer().begin(), cursor, matches,
                                     ex))
        {
            begin = matches[0].first;
            end   = matches[0].second;
            std::copy(matches.begin(), matches.end(),
                      std::back_inserter(captures));
        }
    }
    catch (boost::regex_error& err)
    {
        throw runtime_error("regex error");
    }

    if (begin == end)
        ++end;

    return Selection(begin, end - 1, std::move(captures));
}

typedef boost::regex_iterator<BufferIterator> RegexIterator;

SelectionList select_all_matches(const Selection& selection,
                                 const std::string& regex)
{
    boost::regex ex(regex);
    RegexIterator re_it(selection.begin(), selection.end(), ex);
    RegexIterator re_end;

    SelectionList result;
    for (; re_it != re_end; ++re_it)
    {
        BufferIterator begin = (*re_it)[0].first;
        BufferIterator end   = (*re_it)[0].second;

        Selection::CaptureList captures(re_it->begin(), re_it->end());

        result.push_back(Selection(begin, begin == end ? end : end-1,
                                   std::move(captures)));
    }
    return result;
}

SelectionList split_selection(const Selection& selection,
                              const std::string& separator_regex)
{
    boost::regex ex(separator_regex);
    RegexIterator re_it(selection.begin(), selection.end(), ex,
                        boost::regex_constants::match_nosubs);
    RegexIterator re_end;

    SelectionList result;
    BufferIterator begin = selection.begin();
    for (; re_it != re_end; ++re_it)
    {
        BufferIterator end = (*re_it)[0].first;

        result.push_back(Selection(begin, (begin == end) ? end : end-1));
        begin = (*re_it)[0].second;
    }
    result.push_back(Selection(begin, selection.last()));
    return result;
}

}
<|endoftext|>