<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/request.h"
#include "tpn/core.h"
#include "tpn/user.h"
#include "tpn/addressbook.h"
#include "tpn/store.h"
#include "tpn/stripedfile.h"
#include "tpn/yamlserializer.h"

namespace tpn
{

Request::Request(const String &target, bool data) :
		mId(0),				// 0 = invalid id
		mResponseSender(NULL),
		mContentSink(NULL)
{
	setTarget(target, data);
}

Request::~Request(void)
{
	cancel();

	for(int i=0; i<mResponses.size(); ++i)
		delete mResponses[i];
}

unsigned Request::id(void) const
{
	Synchronize(this);
	return mId;
}

String Request::target(void) const
{
	Synchronize(this);
	return mTarget;
}

void Request::setContentSink(ByteStream *bs)
{
	Synchronize(this);
	mContentSink = bs;
}

void Request::setTarget(const String &target, bool data)
{
	Synchronize(this);
	mTarget = target;
	mIsData = data;
}

void Request::setParameters(StringMap &params)
{
	Synchronize(this);
	mParameters = params;
}

void Request::setParameter(const String &name, const String &value)
{
	Synchronize(this);
	mParameters.insert(name, value);
}

void Request::submit(void)
{
	if(!mId)
	{
		Synchronize(this);
		mId = Core::Instance->addRequest(this);
	}
}

void Request::submit(const Identifier &receiver)
{
	if(!mId)
	{
		Synchronize(this);
		mReceiver = receiver;
		mId = Core::Instance->addRequest(this);
	}
}

void Request::cancel(void)
{
	if(mId)
	{
		Synchronize(this);
		Core::Instance->removeRequest(mId);
	}
}

bool Request::execute(User *user)
{
	Assert(user);
	Synchronize(this);

	AddressBook *addressBook = user->addressBook();
	Store *store = user->store();

	StringMap parameters = mParameters;
	String command;
	String argument;
	int pos = mTarget.find(':');
	if(pos != String::NotFound)
	{
		command  = mTarget.substr(0,pos);
		argument = mTarget.substr(pos+1);
	}
	else {
		if(mTarget.contains('/')) command  = "file";
		else command == "digest";
		argument = mTarget;
	}

	if(mParameters.contains("instance") && mParameters["instance"] != Core::Instance->getName())
	{
		addResponse(new Response(Response::NotFound));
		return false;
	}

	if(command.empty() || command == "digest" || command == "peer")
	{
		Identifier identifier;
		try { argument >> identifier; }
		catch(const Exception &e) { identifier.clear(); }

		if(!identifier.empty())
		{
			if(command == "peer")
			{
				LogDebug("Request::execute", "Got request for peer (" + identifier.getName()+")");

				if(!mParameters.contains("adresses"))
					throw Exception("Missing addresses in peer request");

				AddressBook::Contact *contact = addressBook->getContact(identifier);
				const String &instance = identifier.getName();
				if(contact && !contact->isConnected(instance))
				{
					List<String> list;
					mParameters.get("adresses").explode(list, ',');

					if(mParameters.contains("port") && !mRemoteAddr.isNull())
						list.push_back(mRemoteAddr.host() + ':' + mParameters.get("port"));

					for(	List<String>::iterator it = list.begin();
						it != list.end();
						++it)
					try {
						Address addr(*it);
						Socket *sock = new Socket(addr, 1000);	// TODO: timeout
						Core::Instance->addPeer(sock, identifier, true);	// async
						LogDebug("Request::execute", "Socket connected to peer");
						StringMap parameters;
						parameters["remote"] = contact->remotePeering().toString();
						Response *response = new Response(Response::Success, parameters);
						addResponse(response);
						return true;
					}
					catch(...)
					{

					}

					addResponse(new Response(Response::Failed));
					return false;
				}
			}
			else {
				Resource::Query query;
				query.setDigest(identifier);

				Resource resource;
				if(query.submitLocal(resource, store))
				{
					addResponse(createResponse(resource, parameters, store));
					return true;
				}
			}
		}
	}
	else if(command == "file")
	{
		Resource::Query query(argument);
		Set<Resource> resources;
		if(query.submitLocal(resources, store))
		{
			if(resources.empty())
			{
				addResponse(new Response(Response::Empty));
			}
			else for(Set<Resource>::iterator it = resources.begin();
				it != resources.end();
				++it)
			{
				addResponse(createResponse(*it, parameters, store));
			}

			return true;
		}
	}
	else if(command == "search")
	{
		Resource::Query query;
		query.deserialize(parameters);
		if(!argument.empty() || argument != "*") query.setMatch(argument);

		Set<Resource> resources;
		if(query.submitLocal(resources, store) && !resources.empty())
		{
			for(Set<Resource>::iterator it = resources.begin();
				it != resources.end();
				++it)
			{
				addResponse(createResponse(*it, parameters, store));
			}

			return true;
		}
	}

	addResponse(new Response(Response::NotFound));
	LogDebug("Request", "Target not Found");
	return false;
}

bool Request::executeDummy(void)
{
	Synchronize(this);
	addResponse(new Response(Response::NotFound));
	return false;
}

Request::Response *Request::createResponse(const Resource &resource, const StringMap &parameters, Store *store)
{
	StringMap rparameters;
	resource.serialize(rparameters);

	// TODO: Backward compatibility, should be removed (08/2013)
	if(rparameters.contains("digest"))
	{
		rparameters["hash"] = rparameters["digest"];	// hash in parameters is digest in store
		rparameters.erase("digest");
	}
	//

	if(!mIsData) return new Response(Response::Success, rparameters, NULL);

	if(!resource.isDirectory())
	{
		rparameters["processing"] = "none";
		rparameters["formatting"] = "YAML";

		Response *response = new Response(Response::Success, rparameters, new ByteString);

		Assert(store);
		Assert(response->content());

		// The trailing '/' means it's a directory listing
		String url = resource.url();
		if(url.empty() || url[url.size()-1] != '/') url+= '/';

		SerializableSet<Resource> resources;
		if(store->query(Resource::Query(url), resources))
		{
			YamlSerializer serializer(response->content());
			serializer.output(resources);
		}

		response->content()->close();	// no more content
		return response;
	}

	ByteStream *content = NULL;
	if(!parameters.contains("stripe"))
	{
		content = resource.accessor();
		resource.dissociateAccessor();

		if(parameters.contains("position"))
		{
			int64_t position = 0;
			parameters.get("position").extract(position);
			content->seekRead(position);
			rparameters["position"] << position;
		}

		rparameters["processing"] = "none";
	}
	else {
		size_t blockSize;
		int stripesCount, stripe;
		parameters.get("block-size").extract(blockSize);
		parameters.get("stripes-count").extract(stripesCount);
		parameters.get("stripe").extract(stripe);

		Assert(blockSize > 0);
		Assert(stripesCount > 0);
		Assert(stripe >= 0);

		File *file = NULL;
		StripedFile *stripedFile = NULL;

		try {
			// TODO: Request should not be Resource's friend
			file = new File(resource.mPath, File::Read);
			stripedFile = new StripedFile(file, blockSize, stripesCount, stripe);

			size_t block = 0;
			size_t offset = 0;
			if(parameters.contains("block")) parameters.get("block").extract(block);
			if(parameters.contains("offset")) parameters.get("offset").extract(offset);
			stripedFile->seekRead(block, offset);

			content = stripedFile;

			rparameters["processing"] = "striped";
			//rparameters["size"].clear();
			//rparameters["size"] << TODO;
			rparameters.erase("size");
		}
		catch(...)
		{
			delete file;
			delete stripedFile;
			throw;
		}
	}

	Assert(content);
	Response *response = new Response(Response::Success, rparameters, content, true);	// content is read only
	return response;

}

Identifier Request::receiver(void) const
{
	Synchronize(this);
	return mReceiver;
}

bool Request::isPending() const
{
	Synchronize(this);
	return !mPending.empty();
}

void Request::addPending(const Identifier &peering)
{
	Synchronize(this);
	mPending.insert(peering);
}

void Request::removePending(const Identifier &peering)
{
	Synchronize(this);
	Set<Identifier>::iterator it = mPending.find(peering);
	if(it != mPending.end())
	{
		mPending.erase(it);
		if(mPending.empty()) notifyAll();
	}
}

int Request::responsesCount(void) const
{
	Synchronize(this);
	return mResponses.size();
}

int Request::addResponse(Response *response)
{
	Synchronize(this);
	Assert(response != NULL);
	//Assert(!mResponses.contains(response));

	mResponses.push_back(response);
	if(mResponseSender) mResponseSender->notify();
	return mResponses.size()-1;
}

Request::Response *Request::response(int num)
{
	Synchronize(this);
	return mResponses.at(num);
}

const Request::Response *Request::response(int num) const
{
	Synchronize(this);
	return mResponses.at(num);
}

bool Request::isSuccessful(void) const
{
	Synchronize(this);

	for(int i=0; i<responsesCount(); ++i)
		if(!response(i)->error())
			return true;

	return false;
}

bool Request::hasContent(void) const
{
	Synchronize(this);

	for(int i=0; i<responsesCount(); ++i)
		if(!response(i)->content())
			return true;

	return false;
}

Request::Response::Response(int status) :
	mStatus(status),
	mContent(NULL),
	mChannel(0),
	mTransfertStarted(false),
	mTransfertFinished(false)
{
	Assert(status >= 0);
}

Request::Response::Response(int status, const StringMap &parameters, ByteStream *content, bool readOnly) :
	mStatus(status),
	mParameters(parameters),
	mContent(NULL),
	mChannel(0),
	mTransfertStarted(false),
	mTransfertFinished(false)
{
	Assert(status >= 0);
	if(content) mContent = new Pipe(content, readOnly);
	else mContent = NULL;
}

Request::Response::~Response(void)
{
	delete mContent;
}

const Identifier &Request::Response::peering(void) const
{
	return mPeering;
}

String Request::Response::instance(void) const
{
	if(isLocal()) return Core::Instance->getName();
	else return mPeering.getName();
}

const StringMap &Request::Response::parameters(void) const
{
	return mParameters;
}

String Request::Response::parameter(const String &name) const
{
	String value;
	if(mParameters.contains(name)) return value;
	else return "";
}

bool Request::Response::parameter(const String &name, String &value) const
{
	return mParameters.get(name,value);
}

Pipe *Request::Response::content(void) const
{
	return mContent;
}

bool Request::Response::isLocal(void) const
{
	return mPeering == Identifier::Null;
}

int Request::Response::status(void) const
{
 	return mStatus;
}

bool Request::Response::error(void) const
{
	return (status() > 0
		&& status() != Pending
		&& status() != Empty);
}

bool Request::Response::finished(void) const
{
	return status() < 0;
}

}
<commit_msg>Fixed broken createResponse for file access<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/request.h"
#include "tpn/core.h"
#include "tpn/user.h"
#include "tpn/addressbook.h"
#include "tpn/store.h"
#include "tpn/stripedfile.h"
#include "tpn/yamlserializer.h"

namespace tpn
{

Request::Request(const String &target, bool data) :
		mId(0),				// 0 = invalid id
		mResponseSender(NULL),
		mContentSink(NULL)
{
	setTarget(target, data);
}

Request::~Request(void)
{
	cancel();

	for(int i=0; i<mResponses.size(); ++i)
		delete mResponses[i];
}

unsigned Request::id(void) const
{
	Synchronize(this);
	return mId;
}

String Request::target(void) const
{
	Synchronize(this);
	return mTarget;
}

void Request::setContentSink(ByteStream *bs)
{
	Synchronize(this);
	mContentSink = bs;
}

void Request::setTarget(const String &target, bool data)
{
	Synchronize(this);
	mTarget = target;
	mIsData = data;
}

void Request::setParameters(StringMap &params)
{
	Synchronize(this);
	mParameters = params;
}

void Request::setParameter(const String &name, const String &value)
{
	Synchronize(this);
	mParameters.insert(name, value);
}

void Request::submit(void)
{
	if(!mId)
	{
		Synchronize(this);
		mId = Core::Instance->addRequest(this);
	}
}

void Request::submit(const Identifier &receiver)
{
	if(!mId)
	{
		Synchronize(this);
		mReceiver = receiver;
		mId = Core::Instance->addRequest(this);
	}
}

void Request::cancel(void)
{
	if(mId)
	{
		Synchronize(this);
		Core::Instance->removeRequest(mId);
	}
}

bool Request::execute(User *user)
{
	Assert(user);
	Synchronize(this);

	AddressBook *addressBook = user->addressBook();
	Store *store = user->store();

	StringMap parameters = mParameters;
	String command;
	String argument;
	int pos = mTarget.find(':');
	if(pos != String::NotFound)
	{
		command  = mTarget.substr(0,pos);
		argument = mTarget.substr(pos+1);
	}
	else {
		if(mTarget.contains('/')) command  = "file";
		else command == "digest";
		argument = mTarget;
	}

	if(mParameters.contains("instance") && mParameters["instance"] != Core::Instance->getName())
	{
		addResponse(new Response(Response::NotFound));
		return false;
	}

	if(command.empty() || command == "digest" || command == "peer")
	{
		Identifier identifier;
		try { argument >> identifier; }
		catch(const Exception &e) { identifier.clear(); }

		if(!identifier.empty())
		{
			if(command == "peer")
			{
				LogDebug("Request::execute", "Got request for peer (" + identifier.getName()+")");

				if(!mParameters.contains("adresses"))
					throw Exception("Missing addresses in peer request");

				AddressBook::Contact *contact = addressBook->getContact(identifier);
				const String &instance = identifier.getName();
				if(contact && !contact->isConnected(instance))
				{
					List<String> list;
					mParameters.get("adresses").explode(list, ',');

					if(mParameters.contains("port") && !mRemoteAddr.isNull())
						list.push_back(mRemoteAddr.host() + ':' + mParameters.get("port"));

					for(	List<String>::iterator it = list.begin();
						it != list.end();
						++it)
					try {
						Address addr(*it);
						Socket *sock = new Socket(addr, 1000);	// TODO: timeout
						Core::Instance->addPeer(sock, identifier, true);	// async
						LogDebug("Request::execute", "Socket connected to peer");
						StringMap parameters;
						parameters["remote"] = contact->remotePeering().toString();
						Response *response = new Response(Response::Success, parameters);
						addResponse(response);
						return true;
					}
					catch(...)
					{

					}

					addResponse(new Response(Response::Failed));
					return false;
				}
			}
			else {
				Resource::Query query;
				query.setDigest(identifier);

				Resource resource;
				if(query.submitLocal(resource, store))
				{
					addResponse(createResponse(resource, parameters, store));
					return true;
				}
			}
		}
	}
	else if(command == "file")
	{
		Resource::Query query(argument);
		Set<Resource> resources;
		if(query.submitLocal(resources, store))
		{
			if(resources.empty())
			{
				addResponse(new Response(Response::Empty));
			}
			else for(Set<Resource>::iterator it = resources.begin();
				it != resources.end();
				++it)
			{
				addResponse(createResponse(*it, parameters, store));
			}

			return true;
		}
	}
	else if(command == "search")
	{
		Resource::Query query;
		query.deserialize(parameters);
		if(!argument.empty() || argument != "*") query.setMatch(argument);

		Set<Resource> resources;
		if(query.submitLocal(resources, store) && !resources.empty())
		{
			for(Set<Resource>::iterator it = resources.begin();
				it != resources.end();
				++it)
			{
				addResponse(createResponse(*it, parameters, store));
			}

			return true;
		}
	}

	addResponse(new Response(Response::NotFound));
	LogDebug("Request", "Target not Found");
	return false;
}

bool Request::executeDummy(void)
{
	Synchronize(this);
	addResponse(new Response(Response::NotFound));
	return false;
}

Request::Response *Request::createResponse(const Resource &resource, const StringMap &parameters, Store *store)
{
	StringMap rparameters;
	resource.serialize(rparameters);

	// TODO: Backward compatibility, should be removed (08/2013)
	if(rparameters.contains("digest"))
	{
		rparameters["hash"] = rparameters["digest"];	// hash in parameters is digest in store
		rparameters.erase("digest");
	}
	//

	if(resource.isDirectory())
	{
		rparameters["processing"] = "none";
		rparameters["formatting"] = "YAML";

		Response *response = new Response(Response::Success, rparameters, new ByteString);

		Assert(store);
		Assert(response->content());

		// The trailing '/' means it's a directory listing
		String url = resource.url();
		if(url.empty() || url[url.size()-1] != '/') url+= '/';

		Resource::Query query(url);
		SerializableSet<Resource> resources;
		if(query.submitLocal(resources, store))
		{
			YamlSerializer serializer(response->content());
			serializer.output(resources);
		}

		response->content()->close();	// no more content
		return response;
	}

	ByteStream *content = NULL;
	if(!parameters.contains("stripe"))
	{
		content = resource.accessor();
		resource.dissociateAccessor();

		if(parameters.contains("position"))
		{
			int64_t position = 0;
			parameters.get("position").extract(position);
			content->seekRead(position);
			rparameters["position"] << position;
		}

		rparameters["processing"] = "none";
	}
	else {
		size_t blockSize;
		int stripesCount, stripe;
		parameters.get("block-size").extract(blockSize);
		parameters.get("stripes-count").extract(stripesCount);
		parameters.get("stripe").extract(stripe);

		Assert(blockSize > 0);
		Assert(stripesCount > 0);
		Assert(stripe >= 0);

		File *file = NULL;
		StripedFile *stripedFile = NULL;

		try {
			// TODO: Request should not be Resource's friend
			file = new File(resource.mPath, File::Read);
			stripedFile = new StripedFile(file, blockSize, stripesCount, stripe);

			size_t block = 0;
			size_t offset = 0;
			if(parameters.contains("block")) parameters.get("block").extract(block);
			if(parameters.contains("offset")) parameters.get("offset").extract(offset);
			stripedFile->seekRead(block, offset);

			content = stripedFile;

			rparameters["processing"] = "striped";
			//rparameters["size"].clear();
			//rparameters["size"] << TODO;
			rparameters.erase("size");
		}
		catch(...)
		{
			delete file;
			delete stripedFile;
			throw;
		}
	}

	Assert(content);
	Response *response = new Response(Response::Success, rparameters, content, true);	// content is read only
	return response;

}

Identifier Request::receiver(void) const
{
	Synchronize(this);
	return mReceiver;
}

bool Request::isPending() const
{
	Synchronize(this);
	return !mPending.empty();
}

void Request::addPending(const Identifier &peering)
{
	Synchronize(this);
	mPending.insert(peering);
}

void Request::removePending(const Identifier &peering)
{
	Synchronize(this);
	Set<Identifier>::iterator it = mPending.find(peering);
	if(it != mPending.end())
	{
		mPending.erase(it);
		if(mPending.empty()) notifyAll();
	}
}

int Request::responsesCount(void) const
{
	Synchronize(this);
	return mResponses.size();
}

int Request::addResponse(Response *response)
{
	Synchronize(this);
	Assert(response != NULL);
	//Assert(!mResponses.contains(response));

	mResponses.push_back(response);
	if(mResponseSender) mResponseSender->notify();
	return mResponses.size()-1;
}

Request::Response *Request::response(int num)
{
	Synchronize(this);
	return mResponses.at(num);
}

const Request::Response *Request::response(int num) const
{
	Synchronize(this);
	return mResponses.at(num);
}

bool Request::isSuccessful(void) const
{
	Synchronize(this);

	for(int i=0; i<responsesCount(); ++i)
		if(!response(i)->error())
			return true;

	return false;
}

bool Request::hasContent(void) const
{
	Synchronize(this);

	for(int i=0; i<responsesCount(); ++i)
		if(!response(i)->content())
			return true;

	return false;
}

Request::Response::Response(int status) :
	mStatus(status),
	mContent(NULL),
	mChannel(0),
	mTransfertStarted(false),
	mTransfertFinished(false)
{
	Assert(status >= 0);
}

Request::Response::Response(int status, const StringMap &parameters, ByteStream *content, bool readOnly) :
	mStatus(status),
	mParameters(parameters),
	mContent(NULL),
	mChannel(0),
	mTransfertStarted(false),
	mTransfertFinished(false)
{
	Assert(status >= 0);
	if(content) mContent = new Pipe(content, readOnly);
	else mContent = NULL;
}

Request::Response::~Response(void)
{
	delete mContent;
}

const Identifier &Request::Response::peering(void) const
{
	return mPeering;
}

String Request::Response::instance(void) const
{
	if(isLocal()) return Core::Instance->getName();
	else return mPeering.getName();
}

const StringMap &Request::Response::parameters(void) const
{
	return mParameters;
}

String Request::Response::parameter(const String &name) const
{
	String value;
	if(mParameters.contains(name)) return value;
	else return "";
}

bool Request::Response::parameter(const String &name, String &value) const
{
	return mParameters.get(name,value);
}

Pipe *Request::Response::content(void) const
{
	return mContent;
}

bool Request::Response::isLocal(void) const
{
	return mPeering == Identifier::Null;
}

int Request::Response::status(void) const
{
 	return mStatus;
}

bool Request::Response::error(void) const
{
	return (status() > 0
		&& status() != Pending
		&& status() != Empty);
}

bool Request::Response::finished(void) const
{
	return status() < 0;
}

}
<|endoftext|>


<commit_before>#ifndef DUNE_MS_PROBLEMS_SELECTOR_HH
#define DUNE_MS_PROBLEMS_SELECTOR_HH

#include "one.hh"
#include "two.hh"

namespace Problem {
//this pulls everything from the subnamespace into Problem and should only be done for the "active" problem
using namespace Problem::One;
} // namespace Problem

#endif // DUNE_MS_PROBLEMS_SELECTOR_HH
<commit_msg>always include all problem headers in selector<commit_after>#ifndef DUNE_MS_PROBLEMS_SELECTOR_HH
#define DUNE_MS_PROBLEMS_SELECTOR_HH

//for i in $(ls *hh) ; do echo \#include \"${i}\" ; done
#include "easy.hh"
#include "eight.hh"
#include "five.hh"
#include "four.hh"
#include "nine.hh"
#include "one.hh"
#include "seven.hh"
#include "six.hh"
#include "ten.hh"
#include "three.hh"
#include "toy.hh"
#include "two.hh"

namespace Problem {
//this pulls everything from the subnamespace into Problem and should only be done for the "active" problem
using namespace Problem::One;
} // namespace Problem

#endif // DUNE_MS_PROBLEMS_SELECTOR_HH
<|endoftext|>


<commit_before>#pragma once

#include <iostream>
#include <string>

using std::cout;
using std::endl;

struct Test final {
  template <typename... A>
  explicit Test(A...) = delete;

  template <typename A>
  void operator=(A) = delete;

  template <typename A>
  operator A(void) = delete;

  template <typename A>
  static void assertEquals(const char* const msg, A expected, A actual) {
    if (expected == actual) {
      cout << "SUCCESS:" << endl
           << "\texpected(" << expected << ") ";
    } else {
      cout << "FAILURE:" << endl
           << "\t" << msg << ": " << "expected(" << expected << ") !";
    }

    cout << "= actual(" << actual << ")." << endl;
  }

  template <typename A>
  static void assertEquals(std::string msg, A expected, A actual) {
    Test::assertEquals<A>(msg.c_str(), expected, actual);
  }

  template <typename A>
  static void assertEquals(A expected, A actual) {
    Test::assertEquals<A>("Actual does not match expected", expected, actual);
  }
};
<commit_msg>give user greater formatting of assertEquals output<commit_after>#pragma once

#include <iostream>
#include <string>

using std::cout;
using std::endl;

struct Test final {
  template <typename... A>
  explicit Test(A...) = delete;

  template <typename A>
  void operator=(A) = delete;

  template <typename A>
  operator A(void) = delete;

  template <typename A>
  static void assertEquals(const char* const msg, A expected, A actual) {
    if (expected == actual) {
      cout << "SUCCESS:" << endl
           << "\texpected(" << expected << ") ";
    } else {
      cout << "FAILURE:" << endl
           << "\t" << msg << "expected(" << expected << ") !";
    }

    cout << "= actual(" << actual << ")." << endl;
  }

  template <typename A>
  static void assertEquals(std::string msg, A expected, A actual) {
    Test::assertEquals<A>(msg.c_str(), expected, actual);
  }

  template <typename A>
  static void assertEquals(A expected, A actual) {
    Test::assertEquals<A>("", expected, actual);
  }
};
<|endoftext|>


<commit_before>#include "interactive_mode.hpp"

#include <unistd.h>
#include <cmath>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>

#include "comp.hpp"
#include "computer.hpp"
#include "cursor.hpp"
#include "drawing3D.hpp"
#include "drawing3Db.hpp"
#include "drawing2D.hpp"
#include "load.hpp"
#include "output.hpp"
#include "printer.hpp"
#include "ram.hpp"
#include "random_input.hpp"
#include "renderer.hpp"
#include "view.hpp"

// remove
#include <numeric>
#include <iostream>

using namespace std;

extern "C" {
  extern volatile sig_atomic_t pleaseExit;
  void setEnvironment();
}

// MAIN
void startInteractiveMode(string filename);
void selectView();
void prepareOutput();
void updateBuffer();
// EXECUTION MODE
void run();
void exec();
void sleepAndCheckForKey();
// EDIT MODE
void userInput();
bool switchKey(char c);
void isertCharIntoRam(char c);
bool processInputWithShift(char c);
bool insertNumberIntoRam(char c);
void engageInsertCharMode();
void engageInsertNumberMode();
void switchDrawing(bool direction);
// SAVE
void save();
void saveAs();
string getFreeFileName();
string getGenericFileName(int index);
void saveRamToFile(string filename);
// KEY READER
char readStdin();

//////////////////////
//////// VARS ////////
//////////////////////

View view3d = View(drawing3D, LIGHTBULB_ON_3D, LIGHTBULB_OFF_3D);
View view3db = View(drawing3Db, LIGHTBULB_ON_3D_B, LIGHTBULB_OFF_3D_B);
View view2d = View(drawing2D, LIGHTBULB_ON_2D, LIGHTBULB_OFF_2D);
View *selectedView = &view3d;
RandomInput input;
Computer computer = Computer(redrawScreen, sleepAndCheckForKey);
Printer printer = Printer(computer, redrawScreen, sleepAndCheckForKey);
Cursor cursor = Cursor(computer.ram);

// Whether esc was pressed during execution.
bool executionCanceled = false;
// Filename.
string loadedFilename;
bool fileSaved = true;
// Number of executions.
int executionCounter = 0;
// Saved state of a ram. Loaded after execution ends.
map<AddrSpace, vector<vector<bool>>> savedRamState;
// Whether next key should be read as a char whose value shall thence be
// inserted into ram.
bool insertChar = false;
bool insertNumber = false;
vector<int> digits;
bool shiftPressed = false;
// volatile sig_atomic_t shiftPressed = 0;
// Copy/paste.
vector<bool> clipboard = EMPTY_WORD;

//////////////////////
//////// MAIN ////////
//////////////////////

void InteractiveMode::startInteractiveMode(string filename) {
  computer.ram.input = &input;
  executionCanceled = false;
  if (filename != "") {
    Load::fillRamWithFile(filename.c_str(), computer.ram);
    loadedFilename = filename;
  }
  selectView();
  setEnvironment();
  prepareOutput();
  clearScreen();
  redrawScreen();
  userInput();
}

void selectView() {
  const char* term = std::getenv("TERM");
  if (strcmp(term, "linux") == 0) {
    selectedView = &view3db;
  } else if (strcmp(term, "rxvt") == 0) {
    selectedView = &view2d;
  }
}

/*
 * Initializes 'output.cpp' by sending dimensions of a 'drawing' and
 * a 'drawScreen' callback function, that output.c will use on every
 * screen redraw.
 */
void prepareOutput() {
  initOutput(&updateBuffer, selectedView->width, selectedView->height);
}

void updateBuffer() {
  vector<vector<string>> tmp = Renderer::renderState(printer, computer.ram,
                                                        computer.cpu, cursor,
                                                        *selectedView);
  int i = 0;
  for (vector<string> line : tmp) {
    replaceBufferLine(line, i++);
  }
}

//////////////////////
/// EXECUTION MODE ///
//////////////////////

/*
 * Saves the state of the ram and starts the execution of a program.
 * When execution stops, due to it reaching last address or user pressing
 * 'esc', it loads back the saved state of the ram, and resets the cpu.
 */
void run() {
  savedRamState = computer.ram.state;
  computer.cpu.switchOn();
  redrawScreen();
  sleepAndCheckForKey();
  printer.run();
  // If 'esc' was pressed then it doesn't wait for keypress at the end.
  if (executionCanceled) {
    executionCanceled = false;
  } else {
    readStdin();
  }
  computer.ram.state = savedRamState;
  computer.cpu.reset();
  redrawScreen();
  executionCounter++;
}

/*
 * Runs every cycle.
 */
void sleepAndCheckForKey() {
  usleep(FQ*1000);
  // Exits if ctrl-c was pressed.
  if (pleaseExit) {
    exit(0);
  }
  if (screenResized) {
    redrawScreen();
  }
  // Pauses execution if a key was hit, and waits for another key hit.
  if (int keyCode = Util::getKey()) {
    // Cancels execution if escape was pressed.
    if (keyCode == 27) {
      executionCanceled = true;
      return;
    }
    // "Press key to continue."
    keyCode = readStdin();
    // Cancels execution if escape or tab was pressed.
    if (keyCode == 27 || keyCode == 9) {
      executionCanceled = true;
    }
    // If s was pressed - save.
    if (keyCode == 115) {
      save();
      executionCanceled = true;
    }
    // If S was pressed - save as.
    if (keyCode == 83) {
      saveAs();
      executionCanceled = true;
    }
  }
}

////////////////////
/// EDITING MODE ///
////////////////////

void userInput() {
  while(1) {
    char c = readStdin();
    if (insertChar) {
      isertCharIntoRam(c);
      fileSaved = false;
    } else if (shiftPressed) {
      bool shouldContinue = !processInputWithShift(c);
      if (shouldContinue) {
        continue;
      }
    } else {
      if (insertNumber) {
        if (insertNumberIntoRam(c)) {
          fileSaved = false;
          continue;
        }
      }
      bool shouldContinue = switchKey(c);
      if (shouldContinue) {
        continue;
      }
    }
    redrawScreen();
  }
}

bool switchKey(char c) {
  switch (c) {
    // BEGIN EXECUTION
    case 10:   // enter
      run();
      break;

    // MODES
    case 50:   // 2
      shiftPressed = 1;
      return true;
    case 105:  // i
      engageInsertCharMode();
      break;
    case 73:   // I
      engageInsertNumberMode();
      break;

    // VIEWS
    case 44:   // ,
      switchDrawing(false);
      break;
    case 46:   // .
      switchDrawing(true);
      break;

    // SAVE
    case 115:  // s
      save();
      break;
    case 83:   // S
      saveAs();
      break;

    // QUIT
    case 81:   // Q
      exit(0);
      break;
    case 113: { // q
      if (!fileSaved) {
        saveAs();
      }
      exit(0);
      break;
    }

    // BASIC MOVEMENT
    case 107:  // k
    case 65:   // A, part of escape seqence of up arrow
      cursor.decreaseY();
      break;
    case 106:  // j
    case 66:   // B, part of escape seqence of down arrow
      cursor.increaseY();
      break;
    case 108:  // l
    case 67:   // C, part of escape seqence of rigth arrow
      cursor.increaseX();
      break;
    case 104:  // h
    case 68:   // D, part of escape seqence of left arrow
      cursor.decreaseX();
      break;
    case 116:  // t
    case 9:    // tab
      cursor.switchAddressSpace();
      break;
    case 72:   // H (home)
    case 94:   // ^
      cursor.setBitIndex(0);
      break;
    case 70:   // F (end)
    case 36:   // $
      cursor.setBitIndex(WORD_SIZE-1);
      break;

    // VIM MOVEMENT
    case 103:  // g
      cursor.setByteIndex(0);
      break;
    case 71:   // G
      cursor.setByteIndex(RAM_SIZE-1);
      break;
    case 101:  // e
      cursor.goToEndOfWord();
      break;
    case 98:   // b
      cursor.goToBeginningOfWord();
      break;
    case 119:  // w
      cursor.goToBeginningOfNextWord();
      break;
    case 97:   // a
      cursor.setBitIndex(4);
      break;
    case 122:  // z
    case 90:   // shift + tab
    case 84:   // T
      cursor.goToInstructionsAddress();
      break;

    // BASIC MANIPULATION
    case 32:   // space
      cursor.switchBit();
      fileSaved = false;
      break;
    case 51:   // 3, part of escape seqence of delete key
    case 88: { // X
      vector<bool> temp = cursor.getWord();
      bool success = cursor.deleteByteAndMoveRestUp();
      if (success) {
        clipboard = temp;
        fileSaved = false;
      }
      break;
    }
    case 75:   // K
    case 53:   // 5, part of escape seqence of page up
      cursor.moveByteUp();
      fileSaved = false;
      break;
    case 74:   // J
    case 54:   // 6, part of escape seqence of page down
      cursor.moveByteDown();
      fileSaved = false;
      break;

    // VIM MANIPULATION
    case 102:  // f
      cursor.setBit(true);
      cursor.increaseX();
      fileSaved = false;
      break;
    case 100:  // d
      cursor.setBit(false);
      cursor.increaseX();
      fileSaved = false;
      break;
    case 120: { // x
      clipboard = cursor.getWord();
      cursor.eraseByte();
      fileSaved = false;
      // cursor.setBitIndex(0);
      break;
    }
    case 111: { // o
      cursor.increaseY();
      bool success = cursor.insertByteAndMoveRestDown();
      if (success) {
        cursor.setBitIndex(0);
        fileSaved = false;
      } else {
        cursor.decreaseY();
      }
      break;
    }
    case 99:   // c
    case 121:  // y
      clipboard = cursor.getWord();
      break;
    case 118:  // v
    case 112:  // p
      cursor.setWord(clipboard);
      fileSaved = false;
      break;
    case 80: { // P
      bool success = cursor.insertByteAndMoveRestDown();
      if (success) {
        cursor.setWord(clipboard);
      fileSaved = false;
      }
      break;
    }
    default:
      return true;
  }
  return false;
}

void isertCharIntoRam(char c) {
  insertChar = false;
  if (c == 27) {  // Esc
    return;
  }
  cursor.setWord(Util::getBoolByte(c));
  cursor.increaseY();
}

/*
 * Returns whether it found a key.
 */
bool processInputWithShift(char c) {
  shiftPressed = 0;
  if (c == 65) {           // A, part of up arrow
    cursor.moveByteUp();
    fileSaved = false;
    return true;
  } else if (c == 66) {    // B, part of down arrow
    cursor.moveByteDown();
    fileSaved = false;
    return true;
  } else if (c == 126) {   // ~, part of insert key (also is 2)
    cursor.insertByteAndMoveRestDown();
    fileSaved = false;
    return true;
  } else {
    return false;
  }
}

// Returns whether the loop should continue.
bool insertNumberIntoRam(char c) {
  if (c < 48 || c > 57) {
    digits = vector<int>();
    insertNumber = false;
    return false;
  }
  digits.insert(digits.begin(), c - 48);
  int numbersValue = 0;
  int i = 0;
  for (int digit : digits) {
    numbersValue += digit * pow(10, i++);
  }
  cursor.setWord(Util::getBoolByte(numbersValue));
  redrawScreen();
  return true;
}

void engageInsertCharMode() {
  if (cursor.getAddressSpace() == DATA) {
    insertChar = true;
  }
}

void engageInsertNumberMode() {
  if (cursor.getAddressSpace() == DATA) {
    insertNumber = true;
  }
}

void switchDrawing(bool direction) {
  if (direction) {
    if (*selectedView == view3d) {
      selectedView = &view3db;
    } else if (*selectedView == view3db) {
      selectedView = &view2d;
    } else {
      selectedView = &view3d;
    }
  } else {
    if (*selectedView == view3d) {
      selectedView = &view2d;
    } else if (*selectedView == view3db) {
      selectedView = &view3d;
    } else {
      selectedView = &view3db;
    }
  }
  prepareOutput();
  clearScreen();
  redrawScreen();
}

////////////
/// SAVE ///
////////////

void save() {
  if (fileSaved) {
    printer.printString("Saved "+loadedFilename);
    return;
  }
  if (loadedFilename.empty()) {
    saveAs();
  } else {
    saveRamToFile(loadedFilename);
    fileSaved = true;
    printer.printString("Saved "+loadedFilename);
    redrawScreen();
  }
}

void saveAs() {
  string fileName = getFreeFileName();
  saveRamToFile(fileName);
  loadedFilename = fileName;
  fileSaved = true;
  printer.printString("Saved as "+fileName);
  redrawScreen();
}

string getFreeFileName() {
  int i = 0;
  while (Util::fileExists(getGenericFileName(++i)));
  return getGenericFileName(i);
}

string getGenericFileName(int index) {
  string fileNumber;
  if (index > 9) {
    fileNumber = to_string(index);
  } else {
    fileNumber = "0" + to_string(index);
  }
  return SAVE_FILE_NAME + fileNumber + '.' + FILE_EXTENSION;
}

void saveRamToFile(string fileName) {
  ofstream fileStream(fileName);
  bool computerRunning = computer.cpu.getCycle() != 0;
  if (computerRunning) {
    fileStream << Ram::stateToString(savedRamState);
  } else {
    fileStream << computer.ram.getString();
  }
  fileStream.close();
}

//////////////////
/// KEY READER ///
//////////////////

char readStdin() {
  char c = 0;
  errno = 0;
  ssize_t num = read(0, &c, 1);
  if (num == -1 && errno == EINTR) {
    // Exits if ctrl-c was pressed.
    if (pleaseExit) {
      exit(0);
    }
    if (screenResized) {
      redrawScreen();
    }
    return readStdin();
  }
  return c;
}
<commit_msg>Reasigned few keyx (x)<commit_after>#include "interactive_mode.hpp"

#include <unistd.h>
#include <cmath>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>

#include "comp.hpp"
#include "computer.hpp"
#include "cursor.hpp"
#include "drawing3D.hpp"
#include "drawing3Db.hpp"
#include "drawing2D.hpp"
#include "load.hpp"
#include "output.hpp"
#include "printer.hpp"
#include "ram.hpp"
#include "random_input.hpp"
#include "renderer.hpp"
#include "view.hpp"

// remove
#include <numeric>
#include <iostream>

using namespace std;

extern "C" {
  extern volatile sig_atomic_t pleaseExit;
  void setEnvironment();
}

// MAIN
void startInteractiveMode(string filename);
void selectView();
void prepareOutput();
void updateBuffer();
// EXECUTION MODE
void run();
void exec();
void sleepAndCheckForKey();
// EDIT MODE
void userInput();
bool switchKey(char c);
void isertCharIntoRam(char c);
bool processInputWithShift(char c);
bool insertNumberIntoRam(char c);
void engageInsertCharMode();
void engageInsertNumberMode();
void switchDrawing(bool direction);
// SAVE
void save();
void saveAs();
string getFreeFileName();
string getGenericFileName(int index);
void saveRamToFile(string filename);
// KEY READER
char readStdin();

//////////////////////
//////// VARS ////////
//////////////////////

View view3d = View(drawing3D, LIGHTBULB_ON_3D, LIGHTBULB_OFF_3D);
View view3db = View(drawing3Db, LIGHTBULB_ON_3D_B, LIGHTBULB_OFF_3D_B);
View view2d = View(drawing2D, LIGHTBULB_ON_2D, LIGHTBULB_OFF_2D);
View *selectedView = &view3d;
RandomInput input;
Computer computer = Computer(redrawScreen, sleepAndCheckForKey);
Printer printer = Printer(computer, redrawScreen, sleepAndCheckForKey);
Cursor cursor = Cursor(computer.ram);

// Whether esc was pressed during execution.
bool executionCanceled = false;
// Filename.
string loadedFilename;
bool fileSaved = true;
// Number of executions.
int executionCounter = 0;
// Saved state of a ram. Loaded after execution ends.
map<AddrSpace, vector<vector<bool>>> savedRamState;
// Whether next key should be read as a char whose value shall thence be
// inserted into ram.
bool insertChar = false;
bool insertNumber = false;
vector<int> digits;
bool shiftPressed = false;
// volatile sig_atomic_t shiftPressed = 0;
// Copy/paste.
vector<bool> clipboard = EMPTY_WORD;

//////////////////////
//////// MAIN ////////
//////////////////////

void InteractiveMode::startInteractiveMode(string filename) {
  computer.ram.input = &input;
  executionCanceled = false;
  if (filename != "") {
    Load::fillRamWithFile(filename.c_str(), computer.ram);
    loadedFilename = filename;
  }
  selectView();
  setEnvironment();
  prepareOutput();
  clearScreen();
  redrawScreen();
  userInput();
}

void selectView() {
  const char* term = std::getenv("TERM");
  if (strcmp(term, "linux") == 0) {
    selectedView = &view3db;
  } else if (strcmp(term, "rxvt") == 0) {
    selectedView = &view2d;
  }
}

/*
 * Initializes 'output.cpp' by sending dimensions of a 'drawing' and
 * a 'drawScreen' callback function, that output.c will use on every
 * screen redraw.
 */
void prepareOutput() {
  initOutput(&updateBuffer, selectedView->width, selectedView->height);
}

void updateBuffer() {
  vector<vector<string>> tmp = Renderer::renderState(printer, computer.ram,
                                                        computer.cpu, cursor,
                                                        *selectedView);
  int i = 0;
  for (vector<string> line : tmp) {
    replaceBufferLine(line, i++);
  }
}

//////////////////////
/// EXECUTION MODE ///
//////////////////////

/*
 * Saves the state of the ram and starts the execution of a program.
 * When execution stops, due to it reaching last address or user pressing
 * 'esc', it loads back the saved state of the ram, and resets the cpu.
 */
void run() {
  savedRamState = computer.ram.state;
  computer.cpu.switchOn();
  redrawScreen();
  sleepAndCheckForKey();
  printer.run();
  // If 'esc' was pressed then it doesn't wait for keypress at the end.
  if (executionCanceled) {
    executionCanceled = false;
  } else {
    readStdin();
  }
  computer.ram.state = savedRamState;
  computer.cpu.reset();
  redrawScreen();
  executionCounter++;
}

/*
 * Runs every cycle.
 */
void sleepAndCheckForKey() {
  usleep(FQ*1000);
  // Exits if ctrl-c was pressed.
  if (pleaseExit) {
    exit(0);
  }
  if (screenResized) {
    redrawScreen();
  }
  // Pauses execution if a key was hit, and waits for another key hit.
  if (int keyCode = Util::getKey()) {
    // Cancels execution if escape was pressed.
    if (keyCode == 27) {
      executionCanceled = true;
      return;
    }
    // "Press key to continue."
    keyCode = readStdin();
    // Cancels execution if escape or tab was pressed.
    if (keyCode == 27 || keyCode == 9) {
      executionCanceled = true;
    }
    // If s was pressed - save.
    if (keyCode == 115) {
      save();
      executionCanceled = true;
    }
    // If S was pressed - save as.
    if (keyCode == 83) {
      saveAs();
      executionCanceled = true;
    }
  }
}

////////////////////
/// EDITING MODE ///
////////////////////

void userInput() {
  while(1) {
    char c = readStdin();
    if (insertChar) {
      isertCharIntoRam(c);
      fileSaved = false;
    } else if (shiftPressed) {
      bool shouldContinue = !processInputWithShift(c);
      if (shouldContinue) {
        continue;
      }
    } else {
      if (insertNumber) {
        if (insertNumberIntoRam(c)) {
          fileSaved = false;
          continue;
        }
      }
      bool shouldContinue = switchKey(c);
      if (shouldContinue) {
        continue;
      }
    }
    redrawScreen();
  }
}

bool switchKey(char c) {
  switch (c) {
    // BEGIN EXECUTION
    case 10:   // enter
      run();
      break;

    // MODES
    case 50:   // 2
      shiftPressed = 1;
      return true;
    case 105:  // i
      engageInsertCharMode();
      break;
    case 73:   // I
      engageInsertNumberMode();
      break;

    // VIEWS
    case 44:   // ,
      switchDrawing(false);
      break;
    case 46:   // .
      switchDrawing(true);
      break;

    // SAVE
    case 115:  // s
      save();
      break;
    case 83:   // S
      saveAs();
      break;

    // QUIT
    case 81:   // Q
      exit(0);
      break;
    case 113: { // q
      if (!fileSaved) {
        saveAs();
      }
      exit(0);
      break;
    }

    // BASIC MOVEMENT
    case 107:  // k
    case 65:   // A, part of escape seqence of up arrow
      cursor.decreaseY();
      break;
    case 106:  // j
    case 66:   // B, part of escape seqence of down arrow
      cursor.increaseY();
      break;
    case 108:  // l
    case 67:   // C, part of escape seqence of rigth arrow
      cursor.increaseX();
      break;
    case 104:  // h
    case 68:   // D, part of escape seqence of left arrow
      cursor.decreaseX();
      break;
    case 116:  // t
    case 9:    // tab
      cursor.switchAddressSpace();
      break;
    case 72:   // H (home)
    case 94:   // ^
      cursor.setBitIndex(0);
      break;
    case 70:   // F (end)
    case 36:   // $
      cursor.setBitIndex(WORD_SIZE-1);
      break;

    // VIM MOVEMENT
    case 103:  // g
      cursor.setByteIndex(0);
      break;
    case 71:   // G
      cursor.setByteIndex(RAM_SIZE-1);
      break;
    case 101:  // e
      cursor.goToEndOfWord();
      break;
    case 98:   // b
      cursor.goToBeginningOfWord();
      break;
    case 119:  // w
      cursor.goToBeginningOfNextWord();
      break;
    case 97:   // a
      cursor.setBitIndex(4);
      break;
    case 122:  // z
    case 90:   // shift + tab
    case 84:   // T
      cursor.goToInstructionsAddress();
      break;

    // BASIC MANIPULATION
    case 32:   // space
      cursor.switchBit();
      fileSaved = false;
      break;
    case 51:   // 3, part of escape seqence of delete key
    // case 88: { // X
    // case 22: // find backspace
    case 120: { // x
      vector<bool> temp = cursor.getWord();
      bool success = cursor.deleteByteAndMoveRestUp();
      if (success) {
        clipboard = temp;
        fileSaved = false;
      }
      break;
    }
    case 75:   // K
    case 53:   // 5, part of escape seqence of page up
      cursor.moveByteUp();
      fileSaved = false;
      break;
    case 74:   // J
    case 54:   // 6, part of escape seqence of page down
      cursor.moveByteDown();
      fileSaved = false;
      break;

    // VIM MANIPULATION
    case 102:  // f
      cursor.setBit(true);
      cursor.increaseX();
      fileSaved = false;
      break;
    case 100:  // d
      cursor.setBit(false);
      cursor.increaseX();
      fileSaved = false;
      break;
    // case 120: { // x
      // clipboard = cursor.getWord();
      // cursor.eraseByte();
      // fileSaved = false;
      // break;
    // }
    case 111: { // o
      // cursor.increaseY();
      // bool success = cursor.insertByteAndMoveRestDown();
      // if (success) {
      //   cursor.setBitIndex(0);
      //   fileSaved = false;
      // } else {
      //   cursor.decreaseY();
      // }
      // break;
      // Temporary solution until I find a better key for keyboards without
      // insert key:
      cursor.insertByteAndMoveRestDown();
      fileSaved = false;
      break;
    }
    case 99:   // c
    case 121:  // y
      clipboard = cursor.getWord();
      break;
    case 118:  // v
    case 112:  // p
      cursor.setWord(clipboard);
      fileSaved = false;
      break;
    case 80: { // P
      bool success = cursor.insertByteAndMoveRestDown();
      if (success) {
        cursor.setWord(clipboard);
      fileSaved = false;
      }
      break;
    }
    default:
      return true;
  }
  return false;
}

void isertCharIntoRam(char c) {
  insertChar = false;
  if (c == 27) {  // Esc
    return;
  }
  cursor.setWord(Util::getBoolByte(c));
  cursor.increaseY();
}

/*
 * Returns whether it found a key.
 */
bool processInputWithShift(char c) {
  shiftPressed = 0;
  if (c == 65) {           // A, part of up arrow
    cursor.moveByteUp();
    fileSaved = false;
    return true;
  } else if (c == 66) {    // B, part of down arrow
    cursor.moveByteDown();
    fileSaved = false;
    return true;
  } else if (c == 126) {   // ~, part of insert key (also is 2)
    cursor.insertByteAndMoveRestDown();
    fileSaved = false;
    return true;
  } else {
    return false;
  }
}

// Returns whether the loop should continue.
bool insertNumberIntoRam(char c) {
  if (c < 48 || c > 57) {
    digits = vector<int>();
    insertNumber = false;
    return false;
  }
  digits.insert(digits.begin(), c - 48);
  int numbersValue = 0;
  int i = 0;
  for (int digit : digits) {
    numbersValue += digit * pow(10, i++);
  }
  cursor.setWord(Util::getBoolByte(numbersValue));
  redrawScreen();
  return true;
}

void engageInsertCharMode() {
  if (cursor.getAddressSpace() == DATA) {
    insertChar = true;
  }
}

void engageInsertNumberMode() {
  if (cursor.getAddressSpace() == DATA) {
    insertNumber = true;
  }
}

void switchDrawing(bool direction) {
  if (direction) {
    if (*selectedView == view3d) {
      selectedView = &view3db;
    } else if (*selectedView == view3db) {
      selectedView = &view2d;
    } else {
      selectedView = &view3d;
    }
  } else {
    if (*selectedView == view3d) {
      selectedView = &view2d;
    } else if (*selectedView == view3db) {
      selectedView = &view3d;
    } else {
      selectedView = &view3db;
    }
  }
  prepareOutput();
  clearScreen();
  redrawScreen();
}

////////////
/// SAVE ///
////////////

void save() {
  if (fileSaved) {
    printer.printString("Saved "+loadedFilename);
    return;
  }
  if (loadedFilename.empty()) {
    saveAs();
  } else {
    saveRamToFile(loadedFilename);
    fileSaved = true;
    printer.printString("Saved "+loadedFilename);
    redrawScreen();
  }
}

void saveAs() {
  string fileName = getFreeFileName();
  saveRamToFile(fileName);
  loadedFilename = fileName;
  fileSaved = true;
  printer.printString("Saved as "+fileName);
  redrawScreen();
}

string getFreeFileName() {
  int i = 0;
  while (Util::fileExists(getGenericFileName(++i)));
  return getGenericFileName(i);
}

string getGenericFileName(int index) {
  string fileNumber;
  if (index > 9) {
    fileNumber = to_string(index);
  } else {
    fileNumber = "0" + to_string(index);
  }
  return SAVE_FILE_NAME + fileNumber + '.' + FILE_EXTENSION;
}

void saveRamToFile(string fileName) {
  ofstream fileStream(fileName);
  bool computerRunning = computer.cpu.getCycle() != 0;
  if (computerRunning) {
    fileStream << Ram::stateToString(savedRamState);
  } else {
    fileStream << computer.ram.getString();
  }
  fileStream.close();
}

//////////////////
/// KEY READER ///
//////////////////

char readStdin() {
  char c = 0;
  errno = 0;
  ssize_t num = read(0, &c, 1);
  if (num == -1 && errno == EINTR) {
    // Exits if ctrl-c was pressed.
    if (pleaseExit) {
      exit(0);
    }
    if (screenResized) {
      redrawScreen();
    }
    return readStdin();
  }
  return c;
}
<|endoftext|>


<commit_before>#include "projectile.h"
#include "../packets.h"
#include "../mineserver.h"
#include "../map.h"
#include <cmath>

uint32_t generateEID();

bool ItemProjectile::affectedItem(int item)
{
  switch(item){
  case ITEM_SNOWBALL:
  case ITEM_EGG:
  case ITEM_BOW:
    return true;
  }
  return false;
}

void ItemProjectile::onRightClick(User* user, Item* item)
{
  int8_t projID = 0;
  switch(item->getType())
  {
    case ITEM_SNOWBALL:
      projID = 61;
    break;
    case ITEM_EGG:
      projID = 62;
    break;
    case ITEM_BOW:
      projID = 60;
    break;
  }

  // TODO check arrows and remove one!
  // instead of BOW itself here
  item->decCount();

  Mineserver::get()->map(0)->sendProjectileSpawn(user, projID);
}
<commit_msg>use user->pos.map instead of 0<commit_after>#include "projectile.h"
#include "../packets.h"
#include "../mineserver.h"
#include "../map.h"
#include <cmath>

uint32_t generateEID();

bool ItemProjectile::affectedItem(int item)
{
  switch(item){
  case ITEM_SNOWBALL:
  case ITEM_EGG:
  case ITEM_BOW:
    return true;
  }
  return false;
}

void ItemProjectile::onRightClick(User* user, Item* item)
{
  int8_t projID = 0;
  switch(item->getType())
  {
    case ITEM_SNOWBALL:
      projID = 61;
    break;
    case ITEM_EGG:
      projID = 62;
    break;
    case ITEM_BOW:
      projID = 60;
    break;
  }

  // TODO check arrows and remove one!
  // instead of BOW itself here
  item->decCount();

  Mineserver::get()->map(user->pos.map)->sendProjectileSpawn(user, projID);
}
<|endoftext|>


<commit_before>//===- llvm-db.cpp - LLVM Debugger ----------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This utility implements a simple text-mode front-end to the LLVM debugger
// library.
//
//===----------------------------------------------------------------------===//

#include "CLIDebugger.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/System/Signals.h"
#include <iostream>

using namespace llvm;

namespace {
  // Command line options for specifying the program to debug and options to use
  cl::opt<std::string>
  InputFile(cl::desc("<program>"), cl::Positional, cl::init(""));

  cl::list<std::string>
  InputArgs("args", cl::Positional, cl::desc("<program and arguments>"),
            cl::ZeroOrMore);

  // Command line options to control various directory related stuff
  cl::list<std::string>
  SourceDirectories("directory", cl::value_desc("directory"),
                    cl::desc("Add directory to the search for source files"));
  cl::alias SDA("d", cl::desc("Alias for --directory"),
                cl::aliasopt(SourceDirectories));

  cl::opt<std::string>
  WorkingDirectory("cd", cl::desc("Use directory as current working directory"),
                   cl::value_desc("directory"));

  // Command line options specific to the llvm-db debugger driver
  cl::opt<bool> Quiet("quiet", cl::desc("Do not print introductory messages"));
  cl::alias QA1("silent", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet));
  cl::alias QA2("q", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet));
}

//===----------------------------------------------------------------------===//
// main Driver function
//
int main(int argc, char **argv, char * const *envp) {
  try {
    cl::ParseCommandLineOptions(argc, argv,
                                " llvm source-level debugger\n");
    sys::PrintStackTraceOnErrorSignal();

    if (!Quiet)
      std::cout << "llvm-db: The LLVM source-level debugger\n";

    // Merge Inputfile and InputArgs into the InputArgs list...
    if (!InputFile.empty() && InputArgs.empty())
      InputArgs.push_back(InputFile);

    // Create the CLI debugger...
    CLIDebugger D;

    // Initialize the debugger with the command line options we read...
    Debugger &Dbg = D.getDebugger();

    // Initialize the debugger environment.
    Dbg.initializeEnvironment(envp);
    Dbg.setWorkingDirectory(WorkingDirectory);
    for (unsigned i = 0, e = SourceDirectories.size(); i != e; ++i)
      D.addSourceDirectory(SourceDirectories[i]);

    if (!InputArgs.empty()) {
      try {
        D.fileCommand(InputArgs[0]);
      } catch (const std::string &Error) {
        std::cout << "Error: " << Error << "\n";
      }

      Dbg.setProgramArguments(InputArgs.begin()+1, InputArgs.end());
    }

    // Now that we have initialized the debugger, run it.
    return D.run();
  } catch (const std::string& msg) {
    std::cerr << argv[0] << ": " << msg << "\n";
  } catch (...) {
    std::cerr << argv[0] << ": Unexpected unknown exception occurred.\n";
  }
  return 1;
}
<commit_msg>Add a warning<commit_after>//===- llvm-db.cpp - LLVM Debugger ----------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This utility implements a simple text-mode front-end to the LLVM debugger
// library.
//
//===----------------------------------------------------------------------===//

#include "CLIDebugger.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/System/Signals.h"
#include <iostream>

using namespace llvm;

namespace {
  // Command line options for specifying the program to debug and options to use
  cl::opt<std::string>
  InputFile(cl::desc("<program>"), cl::Positional, cl::init(""));

  cl::list<std::string>
  InputArgs("args", cl::Positional, cl::desc("<program and arguments>"),
            cl::ZeroOrMore);

  // Command line options to control various directory related stuff
  cl::list<std::string>
  SourceDirectories("directory", cl::value_desc("directory"),
                    cl::desc("Add directory to the search for source files"));
  cl::alias SDA("d", cl::desc("Alias for --directory"),
                cl::aliasopt(SourceDirectories));

  cl::opt<std::string>
  WorkingDirectory("cd", cl::desc("Use directory as current working directory"),
                   cl::value_desc("directory"));

  // Command line options specific to the llvm-db debugger driver
  cl::opt<bool> Quiet("quiet", cl::desc("Do not print introductory messages"));
  cl::alias QA1("silent", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet));
  cl::alias QA2("q", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet));
}

//===----------------------------------------------------------------------===//
// main Driver function
//
int main(int argc, char **argv, char * const *envp) {
  std::cout << "NOTE: llvm-db is known useless right now.\n";
  try {
    cl::ParseCommandLineOptions(argc, argv,
                                " llvm source-level debugger\n");
    sys::PrintStackTraceOnErrorSignal();

    if (!Quiet)
      std::cout << "llvm-db: The LLVM source-level debugger\n";

    // Merge Inputfile and InputArgs into the InputArgs list...
    if (!InputFile.empty() && InputArgs.empty())
      InputArgs.push_back(InputFile);

    // Create the CLI debugger...
    CLIDebugger D;

    // Initialize the debugger with the command line options we read...
    Debugger &Dbg = D.getDebugger();

    // Initialize the debugger environment.
    Dbg.initializeEnvironment(envp);
    Dbg.setWorkingDirectory(WorkingDirectory);
    for (unsigned i = 0, e = SourceDirectories.size(); i != e; ++i)
      D.addSourceDirectory(SourceDirectories[i]);

    if (!InputArgs.empty()) {
      try {
        D.fileCommand(InputArgs[0]);
      } catch (const std::string &Error) {
        std::cout << "Error: " << Error << "\n";
      }

      Dbg.setProgramArguments(InputArgs.begin()+1, InputArgs.end());
    }

    // Now that we have initialized the debugger, run it.
    return D.run();
  } catch (const std::string& msg) {
    std::cerr << argv[0] << ": " << msg << "\n";
  } catch (...) {
    std::cerr << argv[0] << ": Unexpected unknown exception occurred.\n";
  }
  return 1;
}
<|endoftext|>


<commit_before>/*
 * Copyright Copyright 2012, System Insights, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "connector.hpp"
#include "dlib/logger.h"

using namespace std;

static dlib::logger sLogger("input.connector");

/* Connector public methods */
Connector::Connector(const string& server, unsigned int port, int aLegacyTimeout)
: mConnected(false), mRealTime(false), mHeartbeats(false),
  mLegacyTimeout(aLegacyTimeout * 1000)
{
  mServer = server;
  mPort = port;
  mCommandLock = new dlib::mutex;
}

Connector::~Connector()
{
  delete mCommandLock;
}

void Connector::connect()
{
  mConnected = false;
  const char *ping = "* PING\n";

  try
  {
    // Connect to server:port, failure will throw dlib::socket_error exception
    // Using a smart pointer to ensure connection is deleted if exception thrown
    sLogger << LDEBUG << "Connecting to data source: " << mServer << " on port: " << mPort;
    mConnection.reset(dlib::connect(mServer, mPort));

    // Check to see if this connection supports heartbeats.
    sLogger << LDEBUG << "Sending initial PING";
    int status = mConnection->write(ping, strlen(ping));
    if (status < 0)
    {
      sLogger << LWARN << "connect: Could not write initial heartbeat: " << intToString(status);
      close();
      return;
    }

    connected();

    // If we have heartbeats, make sure we receive something every
    // freq milliseconds.
    dlib::timestamper stamper;
    mLastSent = mLastHeartbeat = stamper.get_timestamp();

    // Make sure connection buffer is clear
    mBuffer.clear();

    // Socket buffer to put the extracted data into
    char sockBuf[SOCKET_BUFFER_SIZE + 1];

    // Keep track of the status return, else status = character bytes read
    // Assuming it always enters the while loop, it should never be 1
    mConnected = true;

    // Boost priority if this is a real-time adapter
    if (mRealTime)
    {
#ifdef WIN32
      HANDLE hand = OpenThread(THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId());
      SetThreadPriority(hand, THREAD_PRIORITY_TIME_CRITICAL);
      CloseHandle(hand);
#else
      struct sched_param param;
      param.sched_priority = 30;
      if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0)
        sLogger << LDEBUG << "Cannot set high thread priority";
#endif
    }

    // Read from the socket, read is a blocking call
    while (mConnected)
    {
      uint64 now;
      now = stamper.get_timestamp();
      int timeout;
      if (mHeartbeats)
        timeout = (int) mHeartbeatFrequency - ((int) (now - mLastSent) / 1000);
      else
        timeout = mLegacyTimeout;

      if (timeout < 0)
        timeout = 1;
      sockBuf[0] = 0;

      status = mConnection->read(sockBuf, SOCKET_BUFFER_SIZE, timeout);
      if (status > 0)
      {
        // Give a null terminator for the end of buffer
        sockBuf[status] = '\0';
        parseBuffer(sockBuf);
      }
      else if (status == TIMEOUT && !mHeartbeats && ((int) (stamper.get_timestamp() - now) / 1000) >= timeout)
      {
        // We don't stop on heartbeats, but if we have a legacy timeout, then we stop.
        sLogger << LERROR << "connect: Did not receive data for over: " << (timeout / 1000) << " seconds";
        break;
      }
      else if (status != TIMEOUT) // Something other than timeout occurred
      {
        sLogger << LERROR << "connect: Socket error, disconnecting";
        break;
      }

      if (mHeartbeats)
      {
        now = stamper.get_timestamp();
        if ((now - mLastHeartbeat) > (uint64) (mHeartbeatFrequency * 2000))
        {
          sLogger << LERROR << "connect: Did not receive heartbeat for over: " << (mHeartbeatFrequency * 2);
          break;
        }
        else if ((now - mLastSent) >= (uint64) (mHeartbeatFrequency * 1000))
        {
          dlib::auto_mutex lock(*mCommandLock);
          sLogger << LDEBUG << "Sending a PING for " << mServer << " on port " << mPort;
          status = mConnection->write(ping, strlen(ping));
          if (status <= 0)
          {
            sLogger << LERROR << "connect: Could not write heartbeat: " << status;
            break;
          }
          mLastSent = now;
        }
      }
    }

    sLogger << LERROR << "connect: Connection exited with status: " << status;
    close();
  }
  catch (dlib::socket_error &e)
  {
    sLogger << LWARN << "connect: Socket exception: " << e.what();
  }
  catch (exception & e)
  {
    sLogger << LERROR << "connect: Exception in connect: " << e.what();
  }
}

void Connector::parseBuffer(const char *aBuffer)
{
  // Append the temporary buffer to the socket buffer
  mBuffer.append(aBuffer);

  size_t newLine = mBuffer.find_last_of('\n');

  // Check to see if there is even a '\n' in buffer
  if (newLine != string::npos)
  {
    // If the '\n' is not at the end of the buffer, then save the overflow
    string overflow = "";

    if (newLine != mBuffer.length() - 1)
    {
      overflow = mBuffer.substr(newLine + 1);
      mBuffer = mBuffer.substr(0, newLine);
    }

    // Search the buffer for new complete lines
    string line;
    stringstream stream(mBuffer, stringstream::in);

    while (!stream.eof())
    {
      getline(stream, line);
      if (line.empty()) continue;

      // Check for heartbeats
      if (line[0] == '*')
      {
        if (line.compare(0, 6, "* PONG") == 0)
        {
          sLogger << LDEBUG << "Received a PONG for " << mServer << " on port " << mPort;
          if (!mHeartbeats)
            startHeartbeats(line);
          else
          {
            dlib::timestamper stamper;
            mLastHeartbeat = stamper.get_timestamp();
          }
        }
        else
        {
          protocolCommand(line);
        }
      }
      else
      {
        processData(line);
      }
    }

    // Clear buffer/insert overflow data
    mBuffer = overflow;
  }
}

void Connector::sendCommand(const string &aCommand)
{
  if (mConnected) {
    dlib::auto_mutex lock(*mCommandLock);
    string command = "* " + aCommand + "\n";
    int status = mConnection->write(command.c_str(), command.length());
    if (status <= 0)
    {
      sLogger << LWARN << "sendCommand: Could not write command: '" << aCommand << "' - "
              << intToString(status);
    }
  }
}

void Connector::startHeartbeats(const string &aArg)
{
  size_t pos = aArg.find_last_of(' ');
  if (pos != string::npos)
  {
    int freq = atoi(aArg.substr(pos + 1).c_str());
    if (freq > 0 && freq < 120000)
    {
      sLogger << LDEBUG << "Received PONG, starting heartbeats every " << freq << "ms";
      mHeartbeats = true;
      mHeartbeatFrequency = freq;
    }
    else
    {
      sLogger << LERROR << "startHeartbeats: Bad heartbeat command " << aArg;
      close();
    }
  }
}

void Connector::close()
{
  dlib::auto_mutex lock(*mCommandLock);

  if (mConnected && mConnection.get() != NULL)
  {
    // Close the connection gracefully
    mConnection.reset();

    // Call the disconnected callback.
    mConnected = false;
    disconnected();
  }
}

<commit_msg>Made PONG logging and heartbeat delta only get computed if the current log level is debug.<commit_after>/*
 * Copyright Copyright 2012, System Insights, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "connector.hpp"
#include "dlib/logger.h"

using namespace std;

static dlib::logger sLogger("input.connector");

/* Connector public methods */
Connector::Connector(const string& server, unsigned int port, int aLegacyTimeout)
: mConnected(false), mRealTime(false), mHeartbeats(false),
  mLegacyTimeout(aLegacyTimeout * 1000)
{
  mServer = server;
  mPort = port;
  mCommandLock = new dlib::mutex;
}

Connector::~Connector()
{
  delete mCommandLock;
}

void Connector::connect()
{
  mConnected = false;
  const char *ping = "* PING\n";

  try
  {
    // Connect to server:port, failure will throw dlib::socket_error exception
    // Using a smart pointer to ensure connection is deleted if exception thrown
    sLogger << LDEBUG << "Connecting to data source: " << mServer << " on port: " << mPort;
    mConnection.reset(dlib::connect(mServer, mPort));

    // Check to see if this connection supports heartbeats.
    sLogger << LDEBUG << "Sending initial PING";
    int status = mConnection->write(ping, strlen(ping));
    if (status < 0)
    {
      sLogger << LWARN << "connect: Could not write initial heartbeat: " << intToString(status);
      close();
      return;
    }

    connected();

    // If we have heartbeats, make sure we receive something every
    // freq milliseconds.
    dlib::timestamper stamper;
    mLastSent = mLastHeartbeat = stamper.get_timestamp();

    // Make sure connection buffer is clear
    mBuffer.clear();

    // Socket buffer to put the extracted data into
    char sockBuf[SOCKET_BUFFER_SIZE + 1];

    // Keep track of the status return, else status = character bytes read
    // Assuming it always enters the while loop, it should never be 1
    mConnected = true;

    // Boost priority if this is a real-time adapter
    if (mRealTime)
    {
#ifdef WIN32
      HANDLE hand = OpenThread(THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId());
      SetThreadPriority(hand, THREAD_PRIORITY_TIME_CRITICAL);
      CloseHandle(hand);
#else
      struct sched_param param;
      param.sched_priority = 30;
      if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0)
        sLogger << LDEBUG << "Cannot set high thread priority";
#endif
    }

    // Read from the socket, read is a blocking call
    while (mConnected)
    {
      uint64 now;
      now = stamper.get_timestamp();
      int timeout;
      if (mHeartbeats)
        timeout = (int) mHeartbeatFrequency - ((int) (now - mLastSent) / 1000);
      else
        timeout = mLegacyTimeout;

      if (timeout < 0)
        timeout = 1;
      sockBuf[0] = 0;

      status = mConnection->read(sockBuf, SOCKET_BUFFER_SIZE, timeout);
      if (status > 0)
      {
        // Give a null terminator for the end of buffer
        sockBuf[status] = '\0';
        parseBuffer(sockBuf);
      }
      else if (status == TIMEOUT && !mHeartbeats && ((int) (stamper.get_timestamp() - now) / 1000) >= timeout)
      {
        // We don't stop on heartbeats, but if we have a legacy timeout, then we stop.
        sLogger << LERROR << "connect: Did not receive data for over: " << (timeout / 1000) << " seconds";
        break;
      }
      else if (status != TIMEOUT) // Something other than timeout occurred
      {
        sLogger << LERROR << "connect: Socket error, disconnecting";
        break;
      }

      if (mHeartbeats)
      {
        now = stamper.get_timestamp();
        if ((now - mLastHeartbeat) > (uint64) (mHeartbeatFrequency * 2000))
        {
          sLogger << LERROR << "connect: Did not receive heartbeat for over: " << (mHeartbeatFrequency * 2);
          break;
        }
        else if ((now - mLastSent) >= (uint64) (mHeartbeatFrequency * 1000))
        {
          dlib::auto_mutex lock(*mCommandLock);
          sLogger << LDEBUG << "Sending a PING for " << mServer << " on port " << mPort;
          status = mConnection->write(ping, strlen(ping));
          if (status <= 0)
          {
            sLogger << LERROR << "connect: Could not write heartbeat: " << status;
            break;
          }
          mLastSent = now;
        }
      }
    }

    sLogger << LERROR << "connect: Connection exited with status: " << status;
    close();
  }
  catch (dlib::socket_error &e)
  {
    sLogger << LWARN << "connect: Socket exception: " << e.what();
  }
  catch (exception & e)
  {
    sLogger << LERROR << "connect: Exception in connect: " << e.what();
  }
}

void Connector::parseBuffer(const char *aBuffer)
{
  // Append the temporary buffer to the socket buffer
  mBuffer.append(aBuffer);

  size_t newLine = mBuffer.find_last_of('\n');

  // Check to see if there is even a '\n' in buffer
  if (newLine != string::npos)
  {
    // If the '\n' is not at the end of the buffer, then save the overflow
    string overflow = "";

    if (newLine != mBuffer.length() - 1)
    {
      overflow = mBuffer.substr(newLine + 1);
      mBuffer = mBuffer.substr(0, newLine);
    }

    // Search the buffer for new complete lines
    string line;
    stringstream stream(mBuffer, stringstream::in);

    while (!stream.eof())
    {
      getline(stream, line);
      if (line.empty()) continue;

      // Check for heartbeats
      if (line[0] == '*')
      {
        if (line.compare(0, 6, "* PONG") == 0)
        {
          if (sLogger.level().priority <= LDEBUG.priority) {
            sLogger << LDEBUG << "Received a PONG for " << mServer << " on port " << mPort;
            dlib::timestamper stamper;
            int delta = (int) ((stamper.get_timestamp() - mLastHeartbeat) / 1000ull);
            sLogger << LDEBUG << "    Time since last heartbeat: " << delta << "ms";
          }
          if (!mHeartbeats)
            startHeartbeats(line);
          else
          {
            dlib::timestamper stamper;
            mLastHeartbeat = stamper.get_timestamp();
          }
        }
        else
        {
          protocolCommand(line);
        }
      }
      else
      {
        processData(line);
      }
    }

    // Clear buffer/insert overflow data
    mBuffer = overflow;
  }
}

void Connector::sendCommand(const string &aCommand)
{
  if (mConnected) {
    dlib::auto_mutex lock(*mCommandLock);
    string command = "* " + aCommand + "\n";
    int status = mConnection->write(command.c_str(), command.length());
    if (status <= 0)
    {
      sLogger << LWARN << "sendCommand: Could not write command: '" << aCommand << "' - "
              << intToString(status);
    }
  }
}

void Connector::startHeartbeats(const string &aArg)
{
  size_t pos = aArg.find_last_of(' ');
  if (pos != string::npos)
  {
    int freq = atoi(aArg.substr(pos + 1).c_str());
    if (freq > 0 && freq < 120000)
    {
      sLogger << LDEBUG << "Received PONG, starting heartbeats every " << freq << "ms";
      mHeartbeats = true;
      mHeartbeatFrequency = freq;
    }
    else
    {
      sLogger << LERROR << "startHeartbeats: Bad heartbeat command " << aArg;
      close();
    }
  }
}

void Connector::close()
{
  dlib::auto_mutex lock(*mCommandLock);

  if (mConnected && mConnection.get() != NULL)
  {
    // Close the connection gracefully
    mConnection.reset();

    // Call the disconnected callback.
    mConnected = false;
    disconnected();
  }
}

<|endoftext|>


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

#include "CoordinatorClient.h"
#include "ProtoBuf.h"

namespace RAMCloud {

/**
 * Create a new table.
 *
 * \param name
 *      Name for the new table (NULL-terminated string).
 *
 * \exception InternalError
 */
void
CoordinatorClient::createTable(const char* name)
{
    Buffer req;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    CreateTableRpc::Request& reqHdr(allocHeader<CreateTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    while (true) {
        Buffer resp;
        sendRecv<CreateTableRpc>(session, req, resp);
        try {
            checkStatus(HERE);
            return;
        } catch (const RetryException& e) {
            LOG(DEBUG, "RETRY trying to create table");
            yield();
        }
    }
}

/**
 * Delete a table.
 *
 * All objects in the table are implicitly deleted, along with any
 * other information associated with the table (such as, someday,
 * indexes).  If the table does not currently exist than the operation
 * returns successfully without actually doing anything.
 *
 * \param name
 *      Name of the table to delete (NULL-terminated string).
 *
 * \exception InternalError
 */
void
CoordinatorClient::dropTable(const char* name)
{
    Buffer req, resp;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    DropTableRpc::Request& reqHdr(allocHeader<DropTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    sendRecv<DropTableRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Look up a table by name and return a small integer handle that
 * can be used to access the table.
 *
 * \param name
 *      Name of the desired table (NULL-terminated string).
 *
 * \return
 *      The return value is an identifier for the table; this is used
 *      instead of the table's name for most RAMCloud operations
 *      involving the table.
 *
 * \exception TableDoesntExistException
 * \exception InternalError
 */
uint32_t
CoordinatorClient::openTable(const char* name)
{
    Buffer req, resp;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    OpenTableRpc::Request& reqHdr(allocHeader<OpenTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    const OpenTableRpc::Response& respHdr(
        sendRecv<OpenTableRpc>(session, req, resp));
    checkStatus(HERE);
    return respHdr.tableId;
}

/**
 * Servers call this when they come online to beg for work.
 * \param serverType
 *      Master, etc.
 * \param localServiceLocator
 *      The service locator describing how other hosts can contact the server.
 * \param readSpeed
 *      Read speed of the backup in MB/s if serverType is BACKUP, ignored otherwise.
 * \param writeSpeed
 *      Write speed of the backup in MB/s if serverType is BACKUP, ignored otherwise.
 * \return
 *      A server ID guaranteed never to have been used before.
 */
uint64_t
CoordinatorClient::enlistServer(ServerType serverType,
                                string localServiceLocator,
                                uint32_t readSpeed,
                                uint32_t writeSpeed)
{
    while (true) {
        try {
            Buffer req;
            Buffer resp;
            EnlistServerRpc::Request& reqHdr(
                allocHeader<EnlistServerRpc>(req));
            reqHdr.serverType = serverType;
            reqHdr.readSpeed = readSpeed;
            reqHdr.writeSpeed = writeSpeed;
            reqHdr.serviceLocatorLength =
                downCast<uint32_t>(localServiceLocator.length() + 1);
            strncpy(new(&req, APPEND) char[reqHdr.serviceLocatorLength],
                    localServiceLocator.c_str(),
                    reqHdr.serviceLocatorLength);
            const EnlistServerRpc::Response& respHdr(
                sendRecv<EnlistServerRpc>(session, req, resp));
            checkStatus(HERE);
            return respHdr.serverId;
        } catch (TransportException& e) {
            LOG(NOTICE,
                "TransportException trying to talk to coordinator: %s",
                e.str().c_str());
            LOG(NOTICE, "retrying");
            yield();
        }
    }
}

/**
 * List all live servers of the given type.
 * \param[in] type
 *      The type of server to get a list of. Presently either MASTER or BACKUP.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current master servers.
 */
void
CoordinatorClient::getServerList(ServerType type,
                                 ProtoBuf::ServerList& serverList)
{
    Buffer req;
    Buffer resp;
    GetServerListRpc::Request& reqHdr(
        allocHeader<GetServerListRpc>(req));
    reqHdr.serverType = type;
    const GetServerListRpc::Response& respHdr(
        sendRecv<GetServerListRpc>(session, req, resp));
    checkStatus(HERE);
    ProtoBuf::parseFromResponse(resp, sizeof(respHdr),
                                respHdr.serverListLength, serverList);
}

/**
 * List all live servers.
 * Used in ensureHosts.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current servers.
 */
void
CoordinatorClient::getServerList(ProtoBuf::ServerList& serverList)
{
    getServerList(MASTER, serverList);
    ProtoBuf::ServerList mergeList;
    getServerList(BACKUP, mergeList);
    serverList.MergeFrom(mergeList);
}

/**
 * List all live master servers.
 * The failure detector uses this to periodically probe for failed masters.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current master servers.
 */
void
CoordinatorClient::getMasterList(ProtoBuf::ServerList& serverList)
{
    getServerList(MASTER, serverList);
}

/**
 * List all live backup servers.
 * Masters call and cache this periodically to find backups. The failure
 * detector also uses this to periodically probe for failed backups.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current backup servers.
 */
void
CoordinatorClient::getBackupList(ProtoBuf::ServerList& serverList)
{
    getServerList(BACKUP, serverList);
}

/**
 * Return the entire tablet map.
 * Clients use this to find objects.
 * If the returned data becomes too big, we should add parameters to
 * specify a subrange.
 * \param[out] tabletMap
 *      An empty Tablets that will be filled with current tablets.
 *      Each tablet has a service locator string describing where to find
 *      its master.
 */
void
CoordinatorClient::getTabletMap(ProtoBuf::Tablets& tabletMap)
{
    Buffer req;
    Buffer resp;
    allocHeader<GetTabletMapRpc>(req);
    const GetTabletMapRpc::Response& respHdr(
        sendRecv<GetTabletMapRpc>(session, req, resp));
    checkStatus(HERE);
    ProtoBuf::parseFromResponse(resp, sizeof(respHdr),
                                respHdr.tabletMapLength, tabletMap);
}

/**
 * Report a slow or dead server.
 */
void
CoordinatorClient::hintServerDown(string serviceLocator)
{
    Buffer req;
    Buffer resp;
    HintServerDownRpc::Request& reqHdr(
        allocHeader<HintServerDownRpc>(req));
    reqHdr.serviceLocatorLength =
        downCast<uint32_t>(serviceLocator.length() + 1);
    strncpy(new(&req, APPEND) char[reqHdr.serviceLocatorLength],
            serviceLocator.c_str(),
            reqHdr.serviceLocatorLength);
    sendRecv<HintServerDownRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Have all backups flush their dirty segments to storage.
 * This is useful for measuring recovery performance accurately.
 */
void
CoordinatorClient::quiesce()
{
    Buffer req;
    Buffer resp;
    allocHeader<BackupQuiesceRpc>(req);
    sendRecv<BackupQuiesceRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Tell the coordinator that recovery of a particular tablets have
 * been recovered on the master who is calling.
 *
 * \param[in] masterId
 *      The masterId of the server invoking this method.
 * \param[in] tablets
 *      The tablets which form a partition of a will which are
 *      now done recovering.
 * \param[in] will
 *      The serialized ProtoBuf representation of the post-recovery
 *      Will to send to the Coordinator.
 */
void
CoordinatorClient::tabletsRecovered(uint64_t masterId,
    const ProtoBuf::Tablets& tablets, const ProtoBuf::Tablets& will)
{
    Buffer req, resp;
    TabletsRecoveredRpc::Request& reqHdr(allocHeader<TabletsRecoveredRpc>(req));
    reqHdr.masterId = masterId;
    reqHdr.tabletsLength = serializeToRequest(req, tablets);
    reqHdr.willLength = serializeToRequest(req, will);
    sendRecv<TabletsRecoveredRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Update a masterId's Will with the Coordinator.
 *
 * \param[in] masterId
 *      The masterId whose Will we're updating.
 * \param[in] will
 *      The ProtoBuf serialised representation of the Will.
 */
void
CoordinatorClient::setWill(uint64_t masterId, const ProtoBuf::Tablets& will)
{
    Buffer req, resp;
    SetWillRpc::Request& reqHdr(allocHeader<SetWillRpc>(req));
    reqHdr.masterId = masterId;
    reqHdr.willLength = serializeToRequest(req, will);
    sendRecv<SetWillRpc>(session, req, resp);
    checkStatus(HERE);
}

} // namespace RAMCloud
<commit_msg>Fix bug introduced by separation of services ("quiesce" RPC is supported by 2 different services).<commit_after>/* Copyright (c) 2010 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "CoordinatorClient.h"
#include "ProtoBuf.h"

namespace RAMCloud {

/**
 * Create a new table.
 *
 * \param name
 *      Name for the new table (NULL-terminated string).
 *
 * \exception InternalError
 */
void
CoordinatorClient::createTable(const char* name)
{
    Buffer req;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    CreateTableRpc::Request& reqHdr(allocHeader<CreateTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    while (true) {
        Buffer resp;
        sendRecv<CreateTableRpc>(session, req, resp);
        try {
            checkStatus(HERE);
            return;
        } catch (const RetryException& e) {
            LOG(DEBUG, "RETRY trying to create table");
            yield();
        }
    }
}

/**
 * Delete a table.
 *
 * All objects in the table are implicitly deleted, along with any
 * other information associated with the table (such as, someday,
 * indexes).  If the table does not currently exist than the operation
 * returns successfully without actually doing anything.
 *
 * \param name
 *      Name of the table to delete (NULL-terminated string).
 *
 * \exception InternalError
 */
void
CoordinatorClient::dropTable(const char* name)
{
    Buffer req, resp;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    DropTableRpc::Request& reqHdr(allocHeader<DropTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    sendRecv<DropTableRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Look up a table by name and return a small integer handle that
 * can be used to access the table.
 *
 * \param name
 *      Name of the desired table (NULL-terminated string).
 *
 * \return
 *      The return value is an identifier for the table; this is used
 *      instead of the table's name for most RAMCloud operations
 *      involving the table.
 *
 * \exception TableDoesntExistException
 * \exception InternalError
 */
uint32_t
CoordinatorClient::openTable(const char* name)
{
    Buffer req, resp;
    uint32_t length = downCast<uint32_t>(strlen(name) + 1);
    OpenTableRpc::Request& reqHdr(allocHeader<OpenTableRpc>(req));
    reqHdr.nameLength = length;
    memcpy(new(&req, APPEND) char[length], name, length);
    const OpenTableRpc::Response& respHdr(
        sendRecv<OpenTableRpc>(session, req, resp));
    checkStatus(HERE);
    return respHdr.tableId;
}

/**
 * Servers call this when they come online to beg for work.
 * \param serverType
 *      Master, etc.
 * \param localServiceLocator
 *      The service locator describing how other hosts can contact the server.
 * \param readSpeed
 *      Read speed of the backup in MB/s if serverType is BACKUP, ignored otherwise.
 * \param writeSpeed
 *      Write speed of the backup in MB/s if serverType is BACKUP, ignored otherwise.
 * \return
 *      A server ID guaranteed never to have been used before.
 */
uint64_t
CoordinatorClient::enlistServer(ServerType serverType,
                                string localServiceLocator,
                                uint32_t readSpeed,
                                uint32_t writeSpeed)
{
    while (true) {
        try {
            Buffer req;
            Buffer resp;
            EnlistServerRpc::Request& reqHdr(
                allocHeader<EnlistServerRpc>(req));
            reqHdr.serverType = serverType;
            reqHdr.readSpeed = readSpeed;
            reqHdr.writeSpeed = writeSpeed;
            reqHdr.serviceLocatorLength =
                downCast<uint32_t>(localServiceLocator.length() + 1);
            strncpy(new(&req, APPEND) char[reqHdr.serviceLocatorLength],
                    localServiceLocator.c_str(),
                    reqHdr.serviceLocatorLength);
            const EnlistServerRpc::Response& respHdr(
                sendRecv<EnlistServerRpc>(session, req, resp));
            checkStatus(HERE);
            return respHdr.serverId;
        } catch (TransportException& e) {
            LOG(NOTICE,
                "TransportException trying to talk to coordinator: %s",
                e.str().c_str());
            LOG(NOTICE, "retrying");
            yield();
        }
    }
}

/**
 * List all live servers of the given type.
 * \param[in] type
 *      The type of server to get a list of. Presently either MASTER or BACKUP.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current master servers.
 */
void
CoordinatorClient::getServerList(ServerType type,
                                 ProtoBuf::ServerList& serverList)
{
    Buffer req;
    Buffer resp;
    GetServerListRpc::Request& reqHdr(
        allocHeader<GetServerListRpc>(req));
    reqHdr.serverType = type;
    const GetServerListRpc::Response& respHdr(
        sendRecv<GetServerListRpc>(session, req, resp));
    checkStatus(HERE);
    ProtoBuf::parseFromResponse(resp, sizeof(respHdr),
                                respHdr.serverListLength, serverList);
}

/**
 * List all live servers.
 * Used in ensureHosts.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current servers.
 */
void
CoordinatorClient::getServerList(ProtoBuf::ServerList& serverList)
{
    getServerList(MASTER, serverList);
    ProtoBuf::ServerList mergeList;
    getServerList(BACKUP, mergeList);
    serverList.MergeFrom(mergeList);
}

/**
 * List all live master servers.
 * The failure detector uses this to periodically probe for failed masters.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current master servers.
 */
void
CoordinatorClient::getMasterList(ProtoBuf::ServerList& serverList)
{
    getServerList(MASTER, serverList);
}

/**
 * List all live backup servers.
 * Masters call and cache this periodically to find backups. The failure
 * detector also uses this to periodically probe for failed backups.
 * \param[out] serverList
 *      An empty ServerList that will be filled with current backup servers.
 */
void
CoordinatorClient::getBackupList(ProtoBuf::ServerList& serverList)
{
    getServerList(BACKUP, serverList);
}

/**
 * Return the entire tablet map.
 * Clients use this to find objects.
 * If the returned data becomes too big, we should add parameters to
 * specify a subrange.
 * \param[out] tabletMap
 *      An empty Tablets that will be filled with current tablets.
 *      Each tablet has a service locator string describing where to find
 *      its master.
 */
void
CoordinatorClient::getTabletMap(ProtoBuf::Tablets& tabletMap)
{
    Buffer req;
    Buffer resp;
    allocHeader<GetTabletMapRpc>(req);
    const GetTabletMapRpc::Response& respHdr(
        sendRecv<GetTabletMapRpc>(session, req, resp));
    checkStatus(HERE);
    ProtoBuf::parseFromResponse(resp, sizeof(respHdr),
                                respHdr.tabletMapLength, tabletMap);
}

/**
 * Report a slow or dead server.
 */
void
CoordinatorClient::hintServerDown(string serviceLocator)
{
    Buffer req;
    Buffer resp;
    HintServerDownRpc::Request& reqHdr(
        allocHeader<HintServerDownRpc>(req));
    reqHdr.serviceLocatorLength =
        downCast<uint32_t>(serviceLocator.length() + 1);
    strncpy(new(&req, APPEND) char[reqHdr.serviceLocatorLength],
            serviceLocator.c_str(),
            reqHdr.serviceLocatorLength);
    sendRecv<HintServerDownRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Have all backups flush their dirty segments to storage.
 * This is useful for measuring recovery performance accurately.
 */
void
CoordinatorClient::quiesce()
{
    Buffer req;
    Buffer resp;
    BackupQuiesceRpc::Request& reqHdr(
        allocHeader<BackupQuiesceRpc>(req));
    // By default this RPC is since the backup service; retarget it
    // for the coordinator service (which will forward it on to all
    // backups).
    reqHdr.common.service = COORDINATOR_SERVICE;
    sendRecv<BackupQuiesceRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Tell the coordinator that recovery of a particular tablets have
 * been recovered on the master who is calling.
 *
 * \param[in] masterId
 *      The masterId of the server invoking this method.
 * \param[in] tablets
 *      The tablets which form a partition of a will which are
 *      now done recovering.
 * \param[in] will
 *      The serialized ProtoBuf representation of the post-recovery
 *      Will to send to the Coordinator.
 */
void
CoordinatorClient::tabletsRecovered(uint64_t masterId,
    const ProtoBuf::Tablets& tablets, const ProtoBuf::Tablets& will)
{
    Buffer req, resp;
    TabletsRecoveredRpc::Request& reqHdr(allocHeader<TabletsRecoveredRpc>(req));
    reqHdr.masterId = masterId;
    reqHdr.tabletsLength = serializeToRequest(req, tablets);
    reqHdr.willLength = serializeToRequest(req, will);
    sendRecv<TabletsRecoveredRpc>(session, req, resp);
    checkStatus(HERE);
}

/**
 * Update a masterId's Will with the Coordinator.
 *
 * \param[in] masterId
 *      The masterId whose Will we're updating.
 * \param[in] will
 *      The ProtoBuf serialised representation of the Will.
 */
void
CoordinatorClient::setWill(uint64_t masterId, const ProtoBuf::Tablets& will)
{
    Buffer req, resp;
    SetWillRpc::Request& reqHdr(allocHeader<SetWillRpc>(req));
    reqHdr.masterId = masterId;
    reqHdr.willLength = serializeToRequest(req, will);
    sendRecv<SetWillRpc>(session, req, resp);
    checkStatus(HERE);
}

} // namespace RAMCloud
<|endoftext|>