<commit_before>#include <fcntl.h>

#include <netinet/in.h>
#include <sys/socket.h>

#include "server.h"

#include <xapian.h>
#include "net/length.h"

const int WRITE_QUEUE_SIZE = 30;

const int MSECS_IDLE_TIMEOUT_DEFAULT = 60000;
const int MSECS_ACTIVE_TIMEOUT_DEFAULT = 15000;

void print_string(const std::string &string) {
	const char *p = string.c_str();
	const char *p_end = p + string.size();
	printf("'");
	while (p != p_end) {
		if (*p >= ' ' && *p <= '~') {
			printf("%c", *p++);
		} else {
			printf("\\x%02x", *p++ & 0xff);
		}
	}
	printf("'\n");
}

class XapianWorker : public Task {
private:
	XapiandClient *client;

public:
	XapianWorker(XapiandClient *client_) : Task(), client(client_) {}

	~XapianWorker() {}

	virtual void run() {
		client->run();
	}
};

//
//   Buffer class - allow for output buffering such that it can be written out
//                                 into async pieces
//
struct Buffer {
	char type;
	char *data;
	ssize_t len;
	ssize_t pos;

	Buffer(char type_, const char *bytes, ssize_t nbytes) {
		pos = 0;
		len = nbytes;
		type = type_;
		data = new char[nbytes];
		memcpy(data, bytes, nbytes);
	}

	virtual ~Buffer() {
		delete [] data;
	}

	char *dpos() {
		return data + pos;
	}

	ssize_t nbytes() {
		return len - pos;
	}
};

void XapiandClient::callback(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	if (revents & EV_READ)
		read_cb(watcher);

	if (revents & EV_WRITE)
		write_cb(watcher);

	pthread_mutex_lock(&qmtx);
	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::async_cb(ev::async &watcher, int revents)
{
	pthread_mutex_lock(&qmtx);
	if (!write_queue.empty()) {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::write_cb(ev::io &watcher)
{
	pthread_mutex_lock(&qmtx);

	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		Buffer* buffer = write_queue.front();

		// printf(">>> ");
		// print_string(std::string(buffer->dpos(), buffer->nbytes()));

		ssize_t written = write(watcher.fd, buffer->dpos(), buffer->nbytes());
		if (written < 0) {
			perror("read error");
		} else {
			buffer->pos += written;
			if (buffer->nbytes() == 0) {
				write_queue.pop(buffer);
				delete buffer;
			}
		}
	}

	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::read_cb(ev::io &watcher)
{
	char buf[1024];

	ssize_t received = recv(watcher.fd, buf, sizeof(buf), 0);

	if (received < 0) {
		perror("read error");
		return;
	}

	if (received == 0) {
		// Gack - we're deleting ourself inside of ourself!
		delete this;
	} else {
		buffer.append(buf, received);
		if (buffer.length() >= 2) {
			const char *o = buffer.data();
			const char *p = o;
			const char *p_end = p + buffer.size();

			message_type type = static_cast<message_type>(*p++);
			size_t len;
			try {
				len = decode_length(&p, p_end, true);
			} catch (const Xapian::NetworkError & e) {
				return;
			}
			std::string data = std::string(p, len);
			buffer.erase(0, p - o + len);

			// printf("<<< ");
			// print_string(data);

			Buffer *msg = new Buffer(type, data.c_str(), data.size());

			messages_queue.push(msg);

			if (type != MSG_GETMSET && type != MSG_SHUTDOWN) {
				thread_pool->addTask(new XapianWorker(this));
			}
		}
	}
}

void XapiandClient::signal_cb(ev::sig &signal, int revents)
{
	delete this;
}

message_type XapiandClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	std::string buf(&msg->type, 1);
	buf += encode_length(msg->nbytes());
	buf += std::string(msg->dpos(), msg->nbytes());
	printf("get_message:");
	print_string(buf);

	message_type type = static_cast<message_type>(msg->type);
	result.assign(msg->dpos(), msg->nbytes());

	delete msg;
	return type;
}

void XapiandClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	printf("send_message:");
	print_string(buf);

	pthread_mutex_lock(&qmtx);
	Buffer *buffer = new Buffer(type, buf.c_str(), buf.size());
	write_queue.push(buffer);
	pthread_mutex_unlock(&qmtx);

	async.send();
}

void XapiandClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

Xapian::Database * XapiandClient::get_db(bool writable_)
{
	if (endpoints.empty()) {
		return NULL;
	}
	if (!database_pool->checkout(&database, endpoints, writable_)) {
		return NULL;
	}
	return database->db;
}

void XapiandClient::release_db(Xapian::Database *db_)
{
	if (database) {
		database_pool->checkin(&database);
	}
}

void XapiandClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_)
{
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), 8890);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
}

void XapiandClient::run()
{
	try {
		run_one();
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::XapiandClient(int sock_, ThreadPool *thread_pool_, DatabasePool *database_pool_, double active_timeout_, double idle_timeout_)
	: RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  sock(sock_),
	  thread_pool(thread_pool_),
	  database_pool(database_pool_),
	  write_queue(WRITE_QUEUE_SIZE),
	  database(NULL)
{
	pthread_mutex_init(&qmtx, 0);

	fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

	printf("Got connection, %d client(s) connected.\n", ++total_clients);

	io.set<XapiandClient, &XapiandClient::callback>(this);
	io.start(sock, ev::READ);

	sig.set<XapiandClient, &XapiandClient::signal_cb>(this);
	sig.start(SIGINT);

	async.set<XapiandClient, &XapiandClient::async_cb>(this);
	async.start();

	try {
		msg_update(std::string());
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::~XapiandClient()
{
	shutdown(sock, SHUT_RDWR);

	// Stop and free watcher if client socket is closing
	io.stop();
	sig.stop();
	async.stop();

	close(sock);

	printf("Lost connection, %d client(s) connected.\n", --total_clients);

	pthread_mutex_destroy(&qmtx);
}

void XapiandServer::io_accept(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	int client_sock = accept(watcher.fd, (struct sockaddr *)&client_addr, &client_len);

	if (client_sock < 0) {
		perror("accept error");
		return;
	}

	double active_timeout = MSECS_ACTIVE_TIMEOUT_DEFAULT * 1e-3;
	double idle_timeout = MSECS_IDLE_TIMEOUT_DEFAULT * 1e-3;
	new XapiandClient(client_sock, &thread_pool, &database_pool, active_timeout, idle_timeout);
}

void XapiandServer::signal_cb(ev::sig &signal, int revents)
{
	signal.loop.break_loop();
}

XapiandServer::XapiandServer(int port, int thread_pool_size)
	: thread_pool(thread_pool_size)
{
	int optval = 1;
	struct sockaddr_in addr;

	sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		perror("bind");
		close(sock);
		sock = 0;
	} else {
		printf("Listening on port %d\n", port);
		fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

		listen(sock, 5);

		io.set<XapiandServer, &XapiandServer::io_accept>(this);
		io.start(sock, ev::READ);

		sig.set<&XapiandServer::signal_cb>();
		sig.start(SIGINT);
	}
}

XapiandServer::~XapiandServer()
{
	shutdown(sock, SHUT_RDWR);

	io.stop();
	sig.stop();

	close(sock);

	printf("Done with all work!\n");
}

int XapiandClient::total_clients = 0;
<commit_msg>Moved code around<commit_after>#include <fcntl.h>

#include <netinet/in.h>
#include <sys/socket.h>

#include "server.h"

#include <xapian.h>
#include "net/length.h"

const int WRITE_QUEUE_SIZE = 30;

const int MSECS_IDLE_TIMEOUT_DEFAULT = 60000;
const int MSECS_ACTIVE_TIMEOUT_DEFAULT = 15000;

void print_string(const std::string &string) {
	const char *p = string.c_str();
	const char *p_end = p + string.size();
	printf("'");
	while (p != p_end) {
		if (*p >= ' ' && *p <= '~') {
			printf("%c", *p++);
		} else {
			printf("\\x%02x", *p++ & 0xff);
		}
	}
	printf("'\n");
}

class XapianWorker : public Task {
private:
	XapiandClient *client;

public:
	XapianWorker(XapiandClient *client_) : Task(), client(client_) {}

	~XapianWorker() {}

	virtual void run() {
		client->run();
	}
};

//
//   Buffer class - allow for output buffering such that it can be written out
//                                 into async pieces
//

struct Buffer {
	char type;
	char *data;
	ssize_t len;
	ssize_t pos;

	Buffer(char type_, const char *bytes, ssize_t nbytes) {
		pos = 0;
		len = nbytes;
		type = type_;
		data = new char[nbytes];
		memcpy(data, bytes, nbytes);
	}

	virtual ~Buffer() {
		delete [] data;
	}

	char *dpos() {
		return data + pos;
	}

	ssize_t nbytes() {
		return len - pos;
	}
};

//
// Xapian Server
//

XapiandServer::XapiandServer(int port, int thread_pool_size)
	: thread_pool(thread_pool_size)
{
	int optval = 1;
	struct sockaddr_in addr;

	sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		perror("bind");
		close(sock);
		sock = 0;
	} else {
		printf("Listening on port %d\n", port);
		fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

		listen(sock, 5);

		io.set<XapiandServer, &XapiandServer::io_accept>(this);
		io.start(sock, ev::READ);

		sig.set<&XapiandServer::signal_cb>();
		sig.start(SIGINT);
	}
}

XapiandServer::~XapiandServer()
{
	shutdown(sock, SHUT_RDWR);

	io.stop();
	sig.stop();

	close(sock);

	printf("Done with all work!\n");
}

void XapiandServer::io_accept(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	int client_sock = accept(watcher.fd, (struct sockaddr *)&client_addr, &client_len);

	if (client_sock < 0) {
		perror("accept error");
		return;
	}

	double active_timeout = MSECS_ACTIVE_TIMEOUT_DEFAULT * 1e-3;
	double idle_timeout = MSECS_IDLE_TIMEOUT_DEFAULT * 1e-3;
	new XapiandClient(client_sock, &thread_pool, &database_pool, active_timeout, idle_timeout);
}

void XapiandServer::signal_cb(ev::sig &signal, int revents)
{
	signal.loop.break_loop();
}

int XapiandClient::total_clients = 0;

//
// Xapian binary client
//

XapiandClient::XapiandClient(int sock_, ThreadPool *thread_pool_, DatabasePool *database_pool_, double active_timeout_, double idle_timeout_)
: RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
sock(sock_),
thread_pool(thread_pool_),
database_pool(database_pool_),
write_queue(WRITE_QUEUE_SIZE),
database(NULL)
{
	pthread_mutex_init(&qmtx, 0);

	fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

	printf("Got connection, %d client(s) connected.\n", ++total_clients);

	io.set<XapiandClient, &XapiandClient::callback>(this);
	io.start(sock, ev::READ);

	sig.set<XapiandClient, &XapiandClient::signal_cb>(this);
	sig.start(SIGINT);

	async.set<XapiandClient, &XapiandClient::async_cb>(this);
	async.start();

	try {
		msg_update(std::string());
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::~XapiandClient()
{
	shutdown(sock, SHUT_RDWR);

	// Stop and free watcher if client socket is closing
	io.stop();
	sig.stop();
	async.stop();

	close(sock);

	printf("Lost connection, %d client(s) connected.\n", --total_clients);

	pthread_mutex_destroy(&qmtx);
}

void XapiandClient::callback(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	if (revents & EV_READ)
		read_cb(watcher);

	if (revents & EV_WRITE)
		write_cb(watcher);

	pthread_mutex_lock(&qmtx);
	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::async_cb(ev::async &watcher, int revents)
{
	pthread_mutex_lock(&qmtx);
	if (!write_queue.empty()) {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::write_cb(ev::io &watcher)
{
	pthread_mutex_lock(&qmtx);

	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		Buffer* buffer = write_queue.front();

		// printf(">>> ");
		// print_string(std::string(buffer->dpos(), buffer->nbytes()));

		ssize_t written = write(watcher.fd, buffer->dpos(), buffer->nbytes());
		if (written < 0) {
			perror("read error");
		} else {
			buffer->pos += written;
			if (buffer->nbytes() == 0) {
				write_queue.pop(buffer);
				delete buffer;
			}
		}
	}

	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::read_cb(ev::io &watcher)
{
	char buf[1024];

	ssize_t received = recv(watcher.fd, buf, sizeof(buf), 0);

	if (received < 0) {
		perror("read error");
		return;
	}

	if (received == 0) {
		// Gack - we're deleting ourself inside of ourself!
		delete this;
	} else {
		buffer.append(buf, received);
		if (buffer.length() >= 2) {
			const char *o = buffer.data();
			const char *p = o;
			const char *p_end = p + buffer.size();

			message_type type = static_cast<message_type>(*p++);
			size_t len;
			try {
				len = decode_length(&p, p_end, true);
			} catch (const Xapian::NetworkError & e) {
				return;
			}
			std::string data = std::string(p, len);
			buffer.erase(0, p - o + len);

			// printf("<<< ");
			// print_string(data);

			Buffer *msg = new Buffer(type, data.c_str(), data.size());

			messages_queue.push(msg);

			if (type != MSG_GETMSET && type != MSG_SHUTDOWN) {
				thread_pool->addTask(new XapianWorker(this));
			}
		}
	}
}

void XapiandClient::signal_cb(ev::sig &signal, int revents)
{
	delete this;
}

message_type XapiandClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	std::string buf(&msg->type, 1);
	buf += encode_length(msg->nbytes());
	buf += std::string(msg->dpos(), msg->nbytes());
	printf("get_message:");
	print_string(buf);

	message_type type = static_cast<message_type>(msg->type);
	result.assign(msg->dpos(), msg->nbytes());

	delete msg;
	return type;
}

void XapiandClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	printf("send_message:");
	print_string(buf);

	pthread_mutex_lock(&qmtx);
	Buffer *buffer = new Buffer(type, buf.c_str(), buf.size());
	write_queue.push(buffer);
	pthread_mutex_unlock(&qmtx);

	async.send();
}

void XapiandClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

Xapian::Database * XapiandClient::get_db(bool writable_)
{
	if (endpoints.empty()) {
		return NULL;
	}
	if (!database_pool->checkout(&database, endpoints, writable_)) {
		return NULL;
	}
	return database->db;
}

void XapiandClient::release_db(Xapian::Database *db_)
{
	if (database) {
		database_pool->checkin(&database);
	}
}

void XapiandClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_)
{
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), 8890);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
}

void XapiandClient::run()
{
	try {
		run_one();
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}
<|endoftext|>


<commit_before>#include "slice.h"

#include <cstdio>
#include <iostream>

#define OUT_OF_PLANE (0)
#define POINT_IN_PLANE (1)
#define FACE_IN_PLANE (2)

int inplane_status(float z, face* f) {
    edge *e0 = f->e;
    // check for in-plane conditions
    int verts_inplane = 0;
    int verts_above = 0;
    int verts_below = 0;

    do {
        vertex *v = e0->vert;
        if (v->loc[2] == z) {
            verts_inplane++;
        } else if (v->loc[2] > z) {
            verts_above++;
        } else {
            verts_below++;
        }
        e0 = e0->next;
    } while (e0 != f->e);

    // check for whole-tri-in-plane
    if (verts_above == 0 && verts_below == 0) {
        return FACE_IN_PLANE;

    }
    // check for single-point-in-plane
    if (verts_inplane == 1 && (verts_above == 0 || verts_below == 0)) {
        return POINT_IN_PLANE;
    }
    return OUT_OF_PLANE;
}

// returns a zero-length line segment if face does not intersect plane
lineseg isect_tri_xy_plane(float z, face* f) {
    lineseg l;

    if (f->sides() != 3) {
        // TODO: triangularize!
        cout << "got a nontriangular mesh and I'm a stupid slicer." << endl;
        return l;
    }

    int points_found = 0;
    edge *e0 = f->e;
    do {
        // do this at the start so we can use continue within the loop
        e0 = e0->next;

        vertex *v = e0->next->vert,
               *v0 = e0->vert;
        float v_z = v->loc[2],
              v0_z = v0->loc[2];

        if (v_z == v0_z) {
            // check for edge-in-plane condition. since we know the whole
            // tri /isn't/ in-plane from the check above, we know that this
            // edge must be our levelset poly line segment
            if (v_z == z) {
                l.p1 = v0->loc;
                l.p2 = v->loc;
                points_found = 2;
                break;
            }
            continue;
        }

        float n = (v_z - z) / (v_z - v0_z);
        if (0 <= n && n <= 1) {
            Vector3f p = v0->loc + n * (v->loc - v0->loc);
            if (++points_found == 1) {
                l.p1 = p;
            } else {
                l.p2 = p;
            }
        }
    } while (e0 != f->e);

    return l;
}

// assign faces to layer buckets. this modifies the levelset vector in-place.
void bucket_faces(mesh &m, bounds &b, float layer_height, vector<levelset> &layers) {
    for (auto iter = m.faces.begin(); iter != m.faces.end(); iter++) {
        face* f = *iter;

        // find face bounds
        float z_min = INFINITY, z_max = -INFINITY;
        edge *e = f->e;
        vertex *v;
        do {
            v = e->vert;
            if (v->loc[2] > z_max) {
                z_max = v->loc[2];
            }
            if (v->loc[2] < z_min) {
                z_min = v->loc[2];
            }
            e = e->next;
        } while (e != f->e);

        // add face to appropriate buckets
        for (auto ls = layers.begin(); ls != layers.end(); ls++) {
            if (z_min <= ls->z && ls->z <= z_max) {
                ls->faces.push_back(f);
            } else if (ls->z > z_max) {
                // we're out of our good range, break
                break;
            }
        }
    }
}

void find_line_segments(levelset &ls) {
    for (auto iter = ls.faces.begin(); iter != ls.faces.end(); iter++) {
        face* f = *iter;
        int inplane = inplane_status(ls.z, f);

        if (inplane == FACE_IN_PLANE) {
            ls.inplane.push_back(f);
            continue;
        } else if (inplane == POINT_IN_PLANE) {
            continue;
        }

        lineseg line = isect_tri_xy_plane(ls.z, f);
        if ((line.p1 - line.p2).norm() == 0) {
            cout << "warning: face doesn't intersect z = " << ls.z << endl;
            edge *e = f->e;
            do {
                vertex *v = e->vert;
                printf("  v[%lx]\t%f\t%f\t%f\n", (unsigned long) v,
                        v->loc[0], v->loc[1], v->loc[2]);
                e = e->next;
            } while (e != f->e);
        } else {
            ls.lines.push_back(line);
        }
    }
}

void slice(tooldef td, mesh &m, vector<levelset> &levelsets) {
    levelsets.clear();

    bounds b = m.get_bounds();
    int level_count = ceil((b.max_z - b.min_z) / td.z_accuracy);
    for (int i = 0; i < level_count; i++) {
        levelset l;
        l.z = b.min_z + i * td.z_accuracy;
        levelsets.push_back(l);
    }
    levelset l;
    l.z = b.max_z;
    levelsets.push_back(l);

    bucket_faces(m, b, td.z_accuracy, levelsets);

    for (auto iter = levelsets.begin(); iter != levelsets.end(); iter++) {
        find_line_segments(*iter);
    }
}

lineseg::lineseg() {}

lineseg::lineseg(const lineseg &other) : p1(other.p1), p2(other.p2) {}

levelset::levelset() {}

levelset::levelset(const levelset &other) :
        perimeter(other.perimeter), edges(other.edges), inplane(other.inplane),
        z(other.z), faces(other.faces), lines(other.lines) {
}

ostream& operator<< (ostream &out, const lineseg &l) {
    out << "(" << l.p1 << ", " << l.p2 << ")";
    return out;
}

ostream& operator<< (ostream &out, const levelset &ls) {
    out << "[perimeter size = " << ls.perimeter.size()
        << " lines size " << ls.lines.size()
        << "]";
    return out;
}
<commit_msg>Functional tri-plane isection.<commit_after>#include "slice.h"

#include <cstdio>
#include <iostream>

#define OUT_OF_PLANE (0)
#define POINT_IN_PLANE (1)
#define FACE_IN_PLANE (2)

int inplane_status(float z, face* f) {
    edge *e0 = f->e;
    // check for in-plane conditions
    int verts_inplane = 0;
    int verts_above = 0;
    int verts_below = 0;

    do {
        vertex *v = e0->vert;
        if (v->loc[2] == z) {
            verts_inplane++;
        } else if (v->loc[2] > z) {
            verts_above++;
        } else {
            verts_below++;
        }
        e0 = e0->next;
    } while (e0 != f->e);

    // check for whole-tri-in-plane
    if (verts_above == 0 && verts_below == 0) {
        return FACE_IN_PLANE;

    }
    // check for single-point-in-plane
    if (verts_inplane == 1 && (verts_above == 0 || verts_below == 0)) {
        return POINT_IN_PLANE;
    }
    return OUT_OF_PLANE;
}

// returns a zero-length line segment if face does not intersect plane
lineseg isect_tri_xy_plane(float z, face* f) {
    lineseg l;

    if (f->sides() != 3) {
        // TODO: triangularize!
        cout << "got a nontriangular mesh and I'm a stupid slicer." << endl;
        return l;
    }

    int points_found = 0;
    edge *e0 = f->e;
    do {
        // do this at the start so we can use continue within the loop
        e0 = e0->next;

        vertex *v = e0->next->vert,
               *v0 = e0->vert;
        float v_z = v->loc[2],
              v0_z = v0->loc[2];

        if (v_z == v0_z) {
            // check for edge-in-plane condition. since we know the whole
            // tri /isn't/ in-plane from the check above, we know that this
            // edge must be our levelset poly line segment
            if (v_z == z) {
                l.p1 = v0->loc;
                l.p2 = v->loc;
                points_found = 2;
                break;
            }
            continue;
        }

        float n = (v_z - z) / (v_z - v0_z);
        if (0 <= n && n <= 1) {
            Vector3f p = v->loc - n * (v->loc - v0->loc);
            if (++points_found == 1) {
                l.p1 = p;
            } else {
                l.p2 = p;
            }
        }
    } while (e0 != f->e);

    return l;
}

// assign faces to layer buckets. this modifies the levelset vector in-place.
void bucket_faces(mesh &m, bounds &b, float layer_height, vector<levelset> &layers) {
    for (auto iter = m.faces.begin(); iter != m.faces.end(); iter++) {
        face* f = *iter;

        // find face bounds
        float z_min = INFINITY, z_max = -INFINITY;
        edge *e = f->e;
        vertex *v;
        do {
            v = e->vert;
            if (v->loc[2] > z_max) {
                z_max = v->loc[2];
            }
            if (v->loc[2] < z_min) {
                z_min = v->loc[2];
            }
            e = e->next;
        } while (e != f->e);

        // add face to appropriate buckets
        for (auto ls = layers.begin(); ls != layers.end(); ls++) {
            if (z_min <= ls->z && ls->z <= z_max) {
                ls->faces.push_back(f);
            } else if (ls->z > z_max) {
                // we're out of our good range, break
                break;
            }
        }
    }
}

void find_line_segments(levelset &ls) {
    for (auto iter = ls.faces.begin(); iter != ls.faces.end(); iter++) {
        face* f = *iter;
        int inplane = inplane_status(ls.z, f);

        if (inplane == FACE_IN_PLANE) {
            ls.inplane.push_back(f);
            continue;
        } else if (inplane == POINT_IN_PLANE) {
            continue;
        }

        lineseg line = isect_tri_xy_plane(ls.z, f);
        if ((line.p1 - line.p2).norm() == 0) {
            cout << "warning: face doesn't intersect z = " << ls.z << endl;
            edge *e = f->e;
            do {
                vertex *v = e->vert;
                printf("  v[%lx]\t%f\t%f\t%f\n", (unsigned long) v,
                        v->loc[0], v->loc[1], v->loc[2]);
                e = e->next;
            } while (e != f->e);
        } else {
            ls.lines.push_back(line);
        }
    }
}

void slice(tooldef td, mesh &m, vector<levelset> &levelsets) {
    levelsets.clear();

    bounds b = m.get_bounds();
    int level_count = ceil((b.max_z - b.min_z) / td.z_accuracy);
    for (int i = 0; i < level_count; i++) {
        levelset l;
        l.z = b.min_z + i * td.z_accuracy;
        levelsets.push_back(l);
    }
    levelset l;
    l.z = b.max_z;
    levelsets.push_back(l);

    bucket_faces(m, b, td.z_accuracy, levelsets);

    for (auto iter = levelsets.begin(); iter != levelsets.end(); iter++) {
        find_line_segments(*iter);
    }
}

lineseg::lineseg() {}

lineseg::lineseg(const lineseg &other) : p1(other.p1), p2(other.p2) {}

levelset::levelset() {}

levelset::levelset(const levelset &other) :
        perimeter(other.perimeter), edges(other.edges), inplane(other.inplane),
        z(other.z), faces(other.faces), lines(other.lines) {
}

ostream& operator<< (ostream &out, const lineseg &l) {
    out << "(" << l.p1 << ", " << l.p2 << ")";
    return out;
}

ostream& operator<< (ostream &out, const levelset &ls) {
    out << "[perimeter size = " << ls.perimeter.size()
        << " lines size " << ls.lines.size()
        << "]";
    return out;
}
<|endoftext|>


<commit_before>/*
 * Copyright (©) 2015 Nate Rosenblum
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "wte/stream.h"

#include <errno.h>
#if !defined(_WIN32)
#include <unistd.h>
#else
#include <io.h>
#endif

#include <cassert>
#include <limits>

#include "wte/buffer.h"
#include "wte/event_base.h"
#include "wte/event_handler.h"
#include "wte/porting.h"

namespace wte {

class StreamImpl final : public Stream {
public:
    // TODO: temporary fd-based constructor for testing
    StreamImpl(EventBase *base, int fd) : handler_(this, fd),
        base_(base), requests_{nullptr, nullptr}, readCallback_(nullptr) { }
    ~StreamImpl();

    void write(const char *buf, size_t size, WriteCallback *cb) override;
    void write(Buffer *buf, WriteCallback *cb) override;
    void startRead(ReadCallback *cb) override;
    void stopRead() override;
    void close() override;
private:
    void writeHelper();
    void readHelper();

    class SockHandler final : public EventHandler {
    public:
        SockHandler(StreamImpl *stream, int fd)
            : EventHandler(fd), stream_(stream) { }
        void ready(What what) NOEXCEPT override;
    private:
        StreamImpl *stream_;
    };

    // State about a write request (buffer, callback)
    class WriteRequest {
    public:
        WriteRequest(const char *buffer, size_t size, WriteCallback *cb);
        WriteRequest(Buffer *buf, WriteCallback *cb);
        ~WriteRequest();
        Buffer buffer_;
        WriteCallback *callback_;
        WriteRequest *next_;
    };

    SockHandler handler_;
    EventBase *base_;
    struct Requests {
        WriteRequest *head;
        WriteRequest *tail;

        void append(WriteRequest *req) {
            if (!head) {
                assert(!tail);
                head = req;
                tail = req;
            } else {
                tail->next_ = req;
            }
        }

        WriteRequest* consumeFront() {
            if (!head) {
                return nullptr;
            }
            WriteRequest *tmp = head;
            head = head->next_;
            if (!head) {
                tail = nullptr;
            }
            delete tmp;
            return head;
        }
    } requests_;
    ReadCallback *readCallback_;
    Buffer readBuffer_;
};

void StreamImpl::SockHandler::ready(What event) NOEXCEPT {
    if (isWrite(event)) {
        stream_->writeHelper();
    }

   if (isRead(event)) {
       stream_->readHelper();
   }
}

StreamImpl::WriteRequest::WriteRequest(const char *buffer, size_t size,
        WriteCallback *cb) : callback_(cb), next_(nullptr) {
    buffer_.append(buffer, size);
}

StreamImpl::WriteRequest::WriteRequest(Buffer *buffer, WriteCallback *cb)
        : callback_(cb), next_(nullptr) {
    buffer_.append(buffer);
}

StreamImpl::WriteRequest::~WriteRequest() { }

void StreamImpl::startRead(Stream::ReadCallback *cb) {
    if (readCallback_ == cb) {
        return;
    }
    readCallback_ = cb;
    base_->registerHandler(&handler_, ensureRead(handler_.watched()));
}

void StreamImpl::stopRead() {
    if (!readCallback_) {
        return;
    }

    readCallback_ = nullptr;

    What events = handler_.watched();
    if (!isRead(handler_.watched())) {
        return;
    } else if (isWrite(handler_.watched())) {
        assert(events == What::READ_WRITE);
        base_->registerHandler(&handler_, What::WRITE);
    }
}

void StreamImpl::write(const char *buf, size_t size, WriteCallback *cb) {
    // TODO: try writing immediately, saving a loop iteration if the socket
    // can handle the entire write w/o blocking
    WriteRequest *req = new WriteRequest(buf, size, cb);
    requests_.append(req);
    base_->registerHandler(&handler_, ensureWrite(handler_.watched()));
}

void StreamImpl::write(Buffer *buf, WriteCallback *cb) {
    WriteRequest *req = new WriteRequest(buf, cb);
    requests_.append(req);
    base_->registerHandler(&handler_, ensureWrite(handler_.watched()));
}

void StreamImpl::close() {
    if (readCallback_) {
        readCallback_->eof();
    }
    if (handler_.registered()) {
        handler_.unregister();
    }
    ::close(handler_.fd());
}

StreamImpl::~StreamImpl() {
    handler_.unregister();
    WriteRequest *r;
    // Delete all outstanding write requests
    while ((r = requests_.consumeFront()) != nullptr) { }
}

void StreamImpl::readHelper() {
    char buf[4096];

    // TODO: consider not reading indefinitely
    for (;;) {
        int nread = ::read(handler_.fd(), buf, sizeof(buf));
        if (nread < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                break;
            }
            // TODO: better errors
            if (readCallback_) {
                readCallback_->error(std::runtime_error("Read failed"));
            }
            break;
        } else if (nread == 0) {
            if (readCallback_) {
                readCallback_->eof();
            }
            break;
        }

        // TODO: a reserve + get readable iovec + readv will be more efficient
        // than a read + append.
        readBuffer_.append(buf, nread);

        if (readCallback_) {
            readCallback_->available(&readBuffer_);
        }
        if (nread < sizeof(buf)) {
            break;
        }
    }
}

void StreamImpl::writeHelper() {
    WriteRequest *req = requests_.head;
    std::vector<Extent> extents;
    bool blocked = false;
    bool failed = false;
    while (req) {
        extents.clear();
        // TODO: better limit
        req->buffer_.peek(std::numeric_limits<size_t>::max(), &extents);
        assert(!extents.empty());

        // TODO: writev
        size_t total_written = 0;
        for (auto& extent : extents) {
            int written = ::write(handler_.fd(), extent.data, extent.size);
            if (written >= 0) {
                total_written += written;
                if (written < extent.size) {
                    blocked = true;
                }
            } else {
                failed = true;
                break;
            }
        }

        req->buffer_.drain(total_written);

        if (blocked || failed) {
            break;
        }

        WriteCallback *cb = req->callback_;
        // Must not touch req after this call
        WriteRequest *next = requests_.consumeFront();
        if (!next) {
            // Uninstall the write handler before invoking the callback
            // for this final request; callbacks that know that they are
            // last invocation may legitimately do destructive things like
            // freeing this stream.
            base_->registerHandler(&handler_, removeWrite(handler_.watched()));
        }

        if (cb) {
            cb->complete(this);
        }

        req = next;
    }

    if (failed && req->callback_) {
        // TODO: better errors
        req->callback_->error(std::runtime_error("Write failed"));
    }
}

Stream* wrapFd(EventBase *base, int fd) {
    return new StreamImpl(base, fd);
}

} // wte namespace
<commit_msg>List initialization hacks<commit_after>/*
 * Copyright (©) 2015 Nate Rosenblum
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "wte/stream.h"

#include <errno.h>
#if !defined(_WIN32)
#include <unistd.h>
#else
#include <io.h>
#endif

#include <cassert>
#include <limits>

#include "wte/buffer.h"
#include "wte/event_base.h"
#include "wte/event_handler.h"
#include "wte/porting.h"

namespace wte {

class StreamImpl final : public Stream {
public:
    // TODO: temporary fd-based constructor for testing
    StreamImpl(EventBase *base, int fd) : handler_(this, fd),
        base_(base), requests_({nullptr, nullptr}), readCallback_(nullptr) { }
    ~StreamImpl();

    void write(const char *buf, size_t size, WriteCallback *cb) override;
    void write(Buffer *buf, WriteCallback *cb) override;
    void startRead(ReadCallback *cb) override;
    void stopRead() override;
    void close() override;
private:
    void writeHelper();
    void readHelper();

    class SockHandler final : public EventHandler {
    public:
        SockHandler(StreamImpl *stream, int fd)
            : EventHandler(fd), stream_(stream) { }
        void ready(What what) NOEXCEPT override;
    private:
        StreamImpl *stream_;
    };

    // State about a write request (buffer, callback)
    class WriteRequest {
    public:
        WriteRequest(const char *buffer, size_t size, WriteCallback *cb);
        WriteRequest(Buffer *buf, WriteCallback *cb);
        ~WriteRequest();
        Buffer buffer_;
        WriteCallback *callback_;
        WriteRequest *next_;
    };

    SockHandler handler_;
    EventBase *base_;
    struct Requests {
        WriteRequest *head;
        WriteRequest *tail;

        void append(WriteRequest *req) {
            if (!head) {
                assert(!tail);
                head = req;
                tail = req;
            } else {
                tail->next_ = req;
            }
        }

        WriteRequest* consumeFront() {
            if (!head) {
                return nullptr;
            }
            WriteRequest *tmp = head;
            head = head->next_;
            if (!head) {
                tail = nullptr;
            }
            delete tmp;
            return head;
        }
    } requests_;
    ReadCallback *readCallback_;
    Buffer readBuffer_;
};

void StreamImpl::SockHandler::ready(What event) NOEXCEPT {
    if (isWrite(event)) {
        stream_->writeHelper();
    }

   if (isRead(event)) {
       stream_->readHelper();
   }
}

StreamImpl::WriteRequest::WriteRequest(const char *buffer, size_t size,
        WriteCallback *cb) : callback_(cb), next_(nullptr) {
    buffer_.append(buffer, size);
}

StreamImpl::WriteRequest::WriteRequest(Buffer *buffer, WriteCallback *cb)
        : callback_(cb), next_(nullptr) {
    buffer_.append(buffer);
}

StreamImpl::WriteRequest::~WriteRequest() { }

void StreamImpl::startRead(Stream::ReadCallback *cb) {
    if (readCallback_ == cb) {
        return;
    }
    readCallback_ = cb;
    base_->registerHandler(&handler_, ensureRead(handler_.watched()));
}

void StreamImpl::stopRead() {
    if (!readCallback_) {
        return;
    }

    readCallback_ = nullptr;

    What events = handler_.watched();
    if (!isRead(handler_.watched())) {
        return;
    } else if (isWrite(handler_.watched())) {
        assert(events == What::READ_WRITE);
        base_->registerHandler(&handler_, What::WRITE);
    }
}

void StreamImpl::write(const char *buf, size_t size, WriteCallback *cb) {
    // TODO: try writing immediately, saving a loop iteration if the socket
    // can handle the entire write w/o blocking
    WriteRequest *req = new WriteRequest(buf, size, cb);
    requests_.append(req);
    base_->registerHandler(&handler_, ensureWrite(handler_.watched()));
}

void StreamImpl::write(Buffer *buf, WriteCallback *cb) {
    WriteRequest *req = new WriteRequest(buf, cb);
    requests_.append(req);
    base_->registerHandler(&handler_, ensureWrite(handler_.watched()));
}

void StreamImpl::close() {
    if (readCallback_) {
        readCallback_->eof();
    }
    if (handler_.registered()) {
        handler_.unregister();
    }
    ::close(handler_.fd());
}

StreamImpl::~StreamImpl() {
    handler_.unregister();
    WriteRequest *r;
    // Delete all outstanding write requests
    while ((r = requests_.consumeFront()) != nullptr) { }
}

void StreamImpl::readHelper() {
    char buf[4096];

    // TODO: consider not reading indefinitely
    for (;;) {
        int nread = ::read(handler_.fd(), buf, sizeof(buf));
        if (nread < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                break;
            }
            // TODO: better errors
            if (readCallback_) {
                readCallback_->error(std::runtime_error("Read failed"));
            }
            break;
        } else if (nread == 0) {
            if (readCallback_) {
                readCallback_->eof();
            }
            break;
        }

        // TODO: a reserve + get readable iovec + readv will be more efficient
        // than a read + append.
        readBuffer_.append(buf, nread);

        if (readCallback_) {
            readCallback_->available(&readBuffer_);
        }
        if (nread < sizeof(buf)) {
            break;
        }
    }
}

void StreamImpl::writeHelper() {
    WriteRequest *req = requests_.head;
    std::vector<Extent> extents;
    bool blocked = false;
    bool failed = false;
    while (req) {
        extents.clear();
        // TODO: better limit
        req->buffer_.peek(std::numeric_limits<size_t>::max(), &extents);
        assert(!extents.empty());

        // TODO: writev
        size_t total_written = 0;
        for (auto& extent : extents) {
            int written = ::write(handler_.fd(), extent.data, extent.size);
            if (written >= 0) {
                total_written += written;
                if (written < extent.size) {
                    blocked = true;
                }
            } else {
                failed = true;
                break;
            }
        }

        req->buffer_.drain(total_written);

        if (blocked || failed) {
            break;
        }

        WriteCallback *cb = req->callback_;
        // Must not touch req after this call
        WriteRequest *next = requests_.consumeFront();
        if (!next) {
            // Uninstall the write handler before invoking the callback
            // for this final request; callbacks that know that they are
            // last invocation may legitimately do destructive things like
            // freeing this stream.
            base_->registerHandler(&handler_, removeWrite(handler_.watched()));
        }

        if (cb) {
            cb->complete(this);
        }

        req = next;
    }

    if (failed && req->callback_) {
        // TODO: better errors
        req->callback_->error(std::runtime_error("Write failed"));
    }
}

Stream* wrapFd(EventBase *base, int fd) {
    return new StreamImpl(base, fd);
}

} // wte namespace
<|endoftext|>


<commit_before>// Copyright (c) 2016 Kasper Kronborg Isager and Radoslaw Niemczyk.
#include "table.hpp"

namespace lsh {
  /**
   * Construct a new lookup table.
   *
   * @param dimensions The number of dimensions of vectors in the table.
   * @param width The width of each vector hash.
   * @param partitions The number of paritions to use.
   */
  table::table(unsigned int dimensions, unsigned int width, unsigned int partitions) {
    this->dimensions_ = dimensions;

    for (unsigned int i = 0; i < partitions; i++) {
      this->masks_.push_back(mask(dimensions, width));
      this->partitions_.push_back(partition());
    }
  }

  /**
   * Get the number of vectors in this lookup table.
   *
   * @return The number of vectors in this lookup table.
   */
  int table::size() const {
    return this->size_;
  }

  /**
   * Add a vector to this lookup table.
   *
   * @param vector The vector to add to this lookup table.
   */
  void table::add(vector v) {
    unsigned int n = this->partitions_.size();

    for (unsigned int i = 0; i < n; i++) {
      vector k = this->masks_[i].project(v);
      bucket* b = &this->partitions_[i][k];

      b->push_back(v);
    }

    this->size_++;
  }

  /**
   * Query this lookup table for the nearest neighbour of a query vector.
   *
   * @param vector The query vector to look up the nearest neighbour of.
   * @return The nearest neighbouring vector if found, otherwise a vector of size 0.
   */
  vector table::query(const vector& v) {
    unsigned int n = this->partitions_.size();

    // Keep track of the best candidate we've encountered.
    vector* best_c = NULL;

    // Keep track of the distance to the best candidate.
    unsigned int best_d;

    for (unsigned int i = 0; i < n; i++) {
      vector k = this->masks_[i].project(v);
      bucket* b = &this->partitions_[i][k];

      for (vector& c: *b) {
        unsigned int d = vector::distance(v, c);

        if (!best_c || d < best_d) {
          best_c = &c;
          best_d = d;
        }
      }
    }

    // In case we didn't find a vector then return the null vector.
    if (!best_c) {
      return vector(std::vector<bool> {});
    }

    return *best_c;
  }
}
<commit_msg>Initialise best distance to 0<commit_after>// Copyright (c) 2016 Kasper Kronborg Isager and Radoslaw Niemczyk.
#include "table.hpp"

namespace lsh {
  /**
   * Construct a new lookup table.
   *
   * @param dimensions The number of dimensions of vectors in the table.
   * @param width The width of each vector hash.
   * @param partitions The number of paritions to use.
   */
  table::table(unsigned int dimensions, unsigned int width, unsigned int partitions) {
    this->dimensions_ = dimensions;

    for (unsigned int i = 0; i < partitions; i++) {
      this->masks_.push_back(mask(dimensions, width));
      this->partitions_.push_back(partition());
    }
  }

  /**
   * Get the number of vectors in this lookup table.
   *
   * @return The number of vectors in this lookup table.
   */
  int table::size() const {
    return this->size_;
  }

  /**
   * Add a vector to this lookup table.
   *
   * @param vector The vector to add to this lookup table.
   */
  void table::add(vector v) {
    unsigned int n = this->partitions_.size();

    for (unsigned int i = 0; i < n; i++) {
      vector k = this->masks_[i].project(v);
      bucket* b = &this->partitions_[i][k];

      b->push_back(v);
    }

    this->size_++;
  }

  /**
   * Query this lookup table for the nearest neighbour of a query vector.
   *
   * @param vector The query vector to look up the nearest neighbour of.
   * @return The nearest neighbouring vector if found, otherwise a vector of size 0.
   */
  vector table::query(const vector& v) {
    unsigned int n = this->partitions_.size();

    // Keep track of the best candidate we've encountered.
    vector* best_c = NULL;

    // Keep track of the distance to the best candidate.
    unsigned int best_d = 0;

    for (unsigned int i = 0; i < n; i++) {
      vector k = this->masks_[i].project(v);
      bucket* b = &this->partitions_[i][k];

      for (vector& c: *b) {
        unsigned int d = vector::distance(v, c);

        if (!best_c || d < best_d) {
          best_c = &c;
          best_d = d;
        }
      }
    }

    // In case we didn't find a vector then return the null vector.
    if (!best_c) {
      return vector(std::vector<bool> {});
    }

    return *best_c;
  }
}
<|endoftext|>


<commit_before>#include "FPSRole.h"
#include "Engine.h"
#include "Creature.h"
#include "AnimationBlend.h"

CFPSRole::CFPSRole(void)
{
	m_nFire = 0;
	m_fAniCoolingTime = 0;
	m_fAniNowCoolingTime = 0;
	m_fSudCoolingTime = 0;
	m_fEmitCoolingTime = 0;
}
CFPSRole::~CFPSRole(void)
{
	g_Engine.pGame->RemoveNode(m_pMuzzleEffect);
}

int CFPSRole::Init( int nRoleID,const char* strCharFile )
{
	if(CRoleBase::Init(nRoleID,strCharFile))
	{
		m_pStand[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("stand_h");
		m_pFire[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("fire_h");
		m_pStand[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("stand_v");
		m_pFire[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("fire_v");
		m_pRun[0] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend("run_h");
		m_pRun[1] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend("run_v");
	}
	return 1;//always return 1;
}

void CFPSRole::OnKeyFrame( _ActionCallback_KeyFrame* pKeyInfo )
{
	//I will finish this function later.
}<commit_msg>Signed-off-by: mrlitong <litongtongxue@gmail.com><commit_after>#include "FPSRole.h"
#include "Engine.h"
#include "Creature.h"
#include "AnimationBlend.h"

CFPSRole::CFPSRole(void)
{
	m_nFire = 0;
	m_fAniCoolingTime = 0;
	m_fAniNowCoolingTime = 0;
	m_fSudCoolingTime = 0;
	m_fEmitCoolingTime = 0;
}
CFPSRole::~CFPSRole(void)
{
	g_Engine.pGame->RemoveNode(m_pMuzzleEffect);
}

int CFPSRole::Init( int nRoleID,const char* strCharFile )
{
	if(CRoleBase::Init(nRoleID,strCharFile))
	{
		m_pStand[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("stand_h");
		m_pFire[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("fire_h");
		m_pStand[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("stand_v");
		m_pFire[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend("fire_v");
		m_pRun[0] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend("run_h");
		m_pRun[1] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend("run_v");
	}
	return 1;//always return 1;
}

void CFPSRole::OnKeyFrame( _ActionCallback_KeyFrame* pKeyInfo )
{
	//I will finish this function later.
}

void CFPSRole::OnActionComplete( _ActionCallback_Complete* pActInfo )
{
	return CRoleBase::OnActionComplete(pActInfo);
}<|endoftext|>


<commit_before>#include "utils.h"

#include <string.h>

#include <fstream>

namespace {

std::vector<int> read_cpu_range(std::string path)
{
  std::ifstream cpus_range_stream { path };
  std::vector<int> cpus;
  std::string cpu_range;

  while (std::getline(cpus_range_stream, cpu_range, ',')) {
    std::size_t rangeop = cpu_range.find('-');
    if (rangeop == std::string::npos) {
      cpus.push_back(std::stoi(cpu_range));
    }
    else {
      int start = std::stoi(cpu_range.substr(0, rangeop));
      int end = std::stoi(cpu_range.substr(rangeop + 1));
      for (int i = start; i <= end; i++)
        cpus.push_back(i);
    }
  }
  return cpus;
}

} // namespace

namespace bpftrace {

bool has_wildcard(const std::string &str)
{
  if (str.find("*") != std::string::npos ||
     (str.find("[") != std::string::npos &&
      str.find("]") != std::string::npos))
     return true;
  else
     return false;
}

std::vector<int> get_online_cpus()
{
  return read_cpu_range("/sys/devices/system/cpu/online");
}

std::vector<int> get_possible_cpus()
{
  return read_cpu_range("/sys/devices/system/cpu/possible");
}

std::vector<std::string> get_kernel_cflags(
    const char* uname_machine,
    const std::string& ksrc,
    const std::string& kobj)
{
  std::vector<std::string> cflags;
  std::string arch = uname_machine;
  const char *archenv;

  if (!strncmp(uname_machine, "x86_64", 6)) {
    arch = "x86";
  } else if (uname_machine[0] == 'i' && !strncmp(&uname_machine[2], "86", 2)) {
    arch = "x86";
  } else if (!strncmp(uname_machine, "arm", 3)) {
    arch = "arm";
  } else if (!strncmp(uname_machine, "sa110", 5)) {
    arch = "arm";
  } else if (!strncmp(uname_machine, "s390x", 5)) {
    arch = "s390";
  } else if (!strncmp(uname_machine, "parisc64", 8)) {
    arch = "parisc";
  } else if (!strncmp(uname_machine, "ppc", 3)) {
    arch = "powerpc";
  } else if (!strncmp(uname_machine, "mips", 4)) {
    arch = "mips";
  } else if (!strncmp(uname_machine, "sh", 2)) {
    arch = "sh";
  } else if (!strncmp(uname_machine, "aarch64", 7)) {
    arch = "arm64";
  }

  // If ARCH env is defined, use it over uname
  archenv = getenv("ARCH");
  if (archenv)
    arch = std::string(archenv);

  cflags.push_back("-nostdinc");
  cflags.push_back("-isystem");
  cflags.push_back("/virtual/lib/clang/include");

  // see linux/Makefile for $(LINUXINCLUDE) + $(USERINCLUDE)
  cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include");
  cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated");
  cflags.push_back("-I" + ksrc + "/include");
  cflags.push_back("-I" + kobj + "/include");
  cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include/uapi");
  cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated/uapi");
  cflags.push_back("-I" + ksrc + "/include/uapi");
  cflags.push_back("-I" + kobj + "/include/generated/uapi");

  cflags.push_back("-include");
  cflags.push_back(ksrc + "/include/linux/kconfig.h");
  cflags.push_back("-D__KERNEL__");
  cflags.push_back("-D__BPF_TRACING__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP16__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP32__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP64__");

  // If ARCH env variable is set, pass this along.
  if (archenv)
	cflags.push_back("-D__TARGET_ARCH_" + arch);

  return cflags;
}

std::string is_deprecated(std::string &str)
{

  std::vector<DeprecatedName>::iterator item;

  for (item = DEPRECATED_LIST.begin(); item != DEPRECATED_LIST.end(); item++)
  {
    if (str.compare(item->old_name) == 0)
    {
      if (item->show_warning)
      {
        std::cerr << "warning: " << item->old_name << " is deprecated and will be removed in the future. ";
        std::cerr << "Use " << item->new_name << "instead." << std::endl;
        item->show_warning = false;
      }

      return item->new_name;
    }
  }

  return str;
}

} // namespace bpftrace
<commit_msg>add missing space<commit_after>#include "utils.h"

#include <string.h>

#include <fstream>

namespace {

std::vector<int> read_cpu_range(std::string path)
{
  std::ifstream cpus_range_stream { path };
  std::vector<int> cpus;
  std::string cpu_range;

  while (std::getline(cpus_range_stream, cpu_range, ',')) {
    std::size_t rangeop = cpu_range.find('-');
    if (rangeop == std::string::npos) {
      cpus.push_back(std::stoi(cpu_range));
    }
    else {
      int start = std::stoi(cpu_range.substr(0, rangeop));
      int end = std::stoi(cpu_range.substr(rangeop + 1));
      for (int i = start; i <= end; i++)
        cpus.push_back(i);
    }
  }
  return cpus;
}

} // namespace

namespace bpftrace {

bool has_wildcard(const std::string &str)
{
  if (str.find("*") != std::string::npos ||
     (str.find("[") != std::string::npos &&
      str.find("]") != std::string::npos))
     return true;
  else
     return false;
}

std::vector<int> get_online_cpus()
{
  return read_cpu_range("/sys/devices/system/cpu/online");
}

std::vector<int> get_possible_cpus()
{
  return read_cpu_range("/sys/devices/system/cpu/possible");
}

std::vector<std::string> get_kernel_cflags(
    const char* uname_machine,
    const std::string& ksrc,
    const std::string& kobj)
{
  std::vector<std::string> cflags;
  std::string arch = uname_machine;
  const char *archenv;

  if (!strncmp(uname_machine, "x86_64", 6)) {
    arch = "x86";
  } else if (uname_machine[0] == 'i' && !strncmp(&uname_machine[2], "86", 2)) {
    arch = "x86";
  } else if (!strncmp(uname_machine, "arm", 3)) {
    arch = "arm";
  } else if (!strncmp(uname_machine, "sa110", 5)) {
    arch = "arm";
  } else if (!strncmp(uname_machine, "s390x", 5)) {
    arch = "s390";
  } else if (!strncmp(uname_machine, "parisc64", 8)) {
    arch = "parisc";
  } else if (!strncmp(uname_machine, "ppc", 3)) {
    arch = "powerpc";
  } else if (!strncmp(uname_machine, "mips", 4)) {
    arch = "mips";
  } else if (!strncmp(uname_machine, "sh", 2)) {
    arch = "sh";
  } else if (!strncmp(uname_machine, "aarch64", 7)) {
    arch = "arm64";
  }

  // If ARCH env is defined, use it over uname
  archenv = getenv("ARCH");
  if (archenv)
    arch = std::string(archenv);

  cflags.push_back("-nostdinc");
  cflags.push_back("-isystem");
  cflags.push_back("/virtual/lib/clang/include");

  // see linux/Makefile for $(LINUXINCLUDE) + $(USERINCLUDE)
  cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include");
  cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated");
  cflags.push_back("-I" + ksrc + "/include");
  cflags.push_back("-I" + kobj + "/include");
  cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include/uapi");
  cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated/uapi");
  cflags.push_back("-I" + ksrc + "/include/uapi");
  cflags.push_back("-I" + kobj + "/include/generated/uapi");

  cflags.push_back("-include");
  cflags.push_back(ksrc + "/include/linux/kconfig.h");
  cflags.push_back("-D__KERNEL__");
  cflags.push_back("-D__BPF_TRACING__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP16__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP32__");
  cflags.push_back("-D__HAVE_BUILTIN_BSWAP64__");

  // If ARCH env variable is set, pass this along.
  if (archenv)
	cflags.push_back("-D__TARGET_ARCH_" + arch);

  return cflags;
}

std::string is_deprecated(std::string &str)
{

  std::vector<DeprecatedName>::iterator item;

  for (item = DEPRECATED_LIST.begin(); item != DEPRECATED_LIST.end(); item++)
  {
    if (str.compare(item->old_name) == 0)
    {
      if (item->show_warning)
      {
        std::cerr << "warning: " << item->old_name << " is deprecated and will be removed in the future. ";
        std::cerr << "Use " << item->new_name << " instead." << std::endl;
        item->show_warning = false;
      }

      return item->new_name;
    }
  }

  return str;
}

} // namespace bpftrace
<|endoftext|>


<commit_before>#include "window.hh"

#include "assert.hh"
#include "context.hh"
#include "highlighter.hh"
#include "hook_manager.hh"
#include "input_handler.hh"
#include "user_interface.hh"

#include <algorithm>
#include <sstream>

namespace Kakoune
{

// Implementation in highlighters.cc
void highlight_selections(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);
void expand_tabulations(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);
void expand_unprintable(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);

Window::Window(Buffer& buffer)
    : Scope(buffer),
      m_buffer(&buffer)
{
    run_hook_in_own_context("WinCreate", buffer.name());

    options().register_watcher(*this);

    m_builtin_highlighters.add_child({"tabulations"_str, make_simple_highlighter(expand_tabulations)});
    m_builtin_highlighters.add_child({"unprintable"_str, make_simple_highlighter(expand_unprintable)});
    m_builtin_highlighters.add_child({"selections"_str,  make_simple_highlighter(highlight_selections)});

    for (auto& option : options().flatten_options())
        on_option_changed(*option);
}

Window::~Window()
{
    run_hook_in_own_context("WinClose", buffer().name());
    options().unregister_watcher(*this);
}

void Window::display_line_at(LineCount buffer_line, LineCount display_line)
{
    if (display_line >= 0 or display_line < m_dimensions.line)
        m_position.line = std::max(0_line, buffer_line - display_line);
}

void Window::center_line(LineCount buffer_line)
{
    display_line_at(buffer_line, m_dimensions.line/2_line);
}

void Window::scroll(LineCount offset)
{
    m_position.line = std::max(0_line, m_position.line + offset);
}

void Window::scroll(CharCount offset)
{
    m_position.column = std::max(0_char, m_position.column + offset);
}

size_t Window::compute_hash(const Context& context) const
{
    size_t res = hash_values(m_position, context.ui().dimensions(), context.buffer().timestamp());

    auto& selections = context.selections();
    res = combine_hash(res, hash_value(selections.main_index()));
    for (auto& sel : selections)
        res = combine_hash(res, hash_values((const ByteCoord&)sel.cursor(), sel.anchor()));

    return res;
}

bool Window::needs_redraw(const Context& context) const
{
    size_t hash = compute_hash(context);
    return hash != m_hash;
}

const DisplayBuffer& Window::update_display_buffer(const Context& context)
{
    DisplayBuffer::LineList& lines = m_display_buffer.lines();
    lines.clear();

    m_dimensions = context.ui().dimensions();
    if (m_dimensions == CharCoord{0,0})
        return m_display_buffer;

    kak_assert(&buffer() == &context.buffer());
    scroll_to_keep_selection_visible_ifn(context);

    for (LineCount line = 0; line < m_dimensions.line; ++line)
    {
        LineCount buffer_line = m_position.line + line;
        if (buffer_line >= buffer().line_count())
            break;
        lines.emplace_back(AtomList{ {buffer(), buffer_line, buffer_line+1} });
    }

    m_display_buffer.compute_range();
    BufferRange range{{0,0}, buffer().end_coord()};
    m_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer, range);
    m_builtin_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer, range);

    // cut the start of the line before m_position.column
    for (auto& line : lines)
        line.trim(m_position.column, m_dimensions.column, true);
    m_display_buffer.optimize();

    m_hash = compute_hash(context);

    return m_display_buffer;
}

void Window::set_position(CharCoord position)
{
    m_position.line = std::max(0_line, position.line);
    m_position.column = std::max(0_char, position.column);
}

void Window::set_dimensions(CharCoord dimensions)
{
    m_dimensions = dimensions;
}

static LineCount adapt_view_pos(LineCount line, LineCount offset,
                                LineCount view_pos, LineCount view_size,
                                LineCount buffer_size)
{
    offset = std::min(offset, (view_size + 1) / 2);
    if (line - offset < view_pos)
        return std::max(0_line, line - offset);
    else if (line + offset >= view_pos + view_size)
        return std::max(0_line, line + offset - view_size);
    return view_pos;
}

static CharCount adapt_view_pos(const DisplayBuffer& display_buffer, CharCount offset,
                                ByteCoord pos, CharCount view_pos, CharCount view_size)
{
    offset = std::min(offset, (view_size + 1) / 2);
    CharCount buffer_column = 0;
    CharCount non_buffer_column = 0;
    for (auto& line : display_buffer.lines())
    {
        for (auto& atom : line)
        {
            if (atom.has_buffer_range())
            {
                if (atom.begin() <= pos and atom.end() > pos)
                {
                    CharCount pos_beg, pos_end;
                    if (atom.type() == DisplayAtom::BufferRange)
                    {
                        auto& buf = atom.buffer();
                        pos_beg = buffer_column
                                + utf8::distance(buf.iterator_at(atom.begin()),
                                                 buf.iterator_at(pos));
                        pos_end = pos_beg+1;
                    }
                    else
                    {
                        pos_beg = buffer_column;
                        pos_end = pos_beg + atom.length();
                    }

                    if (pos_beg - offset < view_pos)
                        return std::max(0_char, pos_beg - offset);

                    if (pos_end + offset >= view_pos + view_size - non_buffer_column)
                        return pos_end + offset - view_size + non_buffer_column;
                }
                buffer_column += atom.length();
            }
            else
                non_buffer_column += atom.length();
        }
    }
    return view_pos;
}

void Window::scroll_to_keep_selection_visible_ifn(const Context& context)
{
    auto& selection = context.selections().main();
    const auto& anchor = selection.anchor();
    const auto& cursor  = selection.cursor();

    const CharCoord offset = options()["scrolloff"].get<CharCoord>();

    // scroll lines if needed, try to get as much of the selection visible as possible
    m_position.line = adapt_view_pos(anchor.line, offset.line, m_position.line,
                                     m_dimensions.line, buffer().line_count());
    m_position.line = adapt_view_pos(cursor.line,  offset.line, m_position.line,
                                     m_dimensions.line, buffer().line_count());

    // highlight only the line containing the cursor
    DisplayBuffer display_buffer;
    DisplayBuffer::LineList& lines = display_buffer.lines();
    lines.emplace_back(AtomList{ {buffer(), cursor.line, cursor.line+1} });

    display_buffer.compute_range();
    BufferRange range{cursor.line, cursor.line + 1};
    m_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer, range);
    m_builtin_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer, range);

    // now we can compute where the cursor is in display columns
    // (this is only valid if highlighting one line and multiple lines put
    // the cursor in the same position, however I do not find any sane example
    // of highlighters not doing that)
    m_position.column = adapt_view_pos(display_buffer, offset.column,
                                       anchor.line == cursor.line ? anchor : cursor.line,
                                       m_position.column, m_dimensions.column);
    m_position.column = adapt_view_pos(display_buffer, offset.column, cursor,
                                       m_position.column, m_dimensions.column);
}

namespace
{
CharCount find_display_column(const DisplayLine& line, const Buffer& buffer,
                              ByteCoord coord)
{
    CharCount column = 0;
    for (auto& atom : line)
    {
        if (atom.has_buffer_range() and
            coord >= atom.begin() and coord < atom.end())
        {
            if (atom.type() == DisplayAtom::BufferRange)
                column += utf8::distance(buffer.iterator_at(atom.begin()),
                                         buffer.iterator_at(coord));
            return column;
        }
        column += atom.length();
    }
    return column;
}

ByteCoord find_buffer_coord(const DisplayLine& line, const Buffer& buffer,
                            CharCount column)
{
    auto& range = line.range();
    for (auto& atom : line)
    {
        CharCount len = atom.length();
        if (atom.has_buffer_range() and column < len)
        {
            if (atom.type() == DisplayAtom::BufferRange)
                return utf8::advance(buffer.iterator_at(atom.begin()), buffer.iterator_at(range.end),
                                     std::max(0_char, column)).coord();
             return atom.begin();
        }
        column -= len;
    }
    return buffer.clamp(buffer.prev(range.end));
}
}

CharCoord Window::display_position(ByteCoord coord) const
{
    LineCount l = 0;
    for (auto& line : m_display_buffer.lines())
    {
        auto& range = line.range();
        if (range.begin <= coord and coord < range.end)
            return {l, find_display_column(line, buffer(), coord)};
        ++l;
    }
    return { 0, 0 };
}

ByteCoord Window::buffer_coord(CharCoord coord) const
{
    if (coord <= 0_line)
        coord = {0,0};
    if ((int)coord.line >= m_display_buffer.lines().size())
        coord = CharCoord{(int)m_display_buffer.lines().size()-1, INT_MAX};

    return find_buffer_coord(m_display_buffer.lines()[(int)coord.line],
                             buffer(), coord.column);
}

ByteCoord Window::offset_coord(ByteCoord coord, CharCount offset)
{
    return buffer().offset_coord(coord, offset);
}

ByteCoordAndTarget Window::offset_coord(ByteCoordAndTarget coord, LineCount offset)
{
    auto line = clamp(coord.line + offset, 0_line, buffer().line_count()-1);
    DisplayBuffer display_buffer;
    DisplayBuffer::LineList& lines = display_buffer.lines();
    lines.emplace_back(AtomList{ {buffer(), coord.line, coord.line+1} });
    lines.emplace_back(AtomList{ {buffer(), line, line+1} });
    display_buffer.compute_range();

    BufferRange range{ std::min(line, coord.line), std::max(line,coord.line)+1};

    InputHandler input_handler{{ *m_buffer, Selection{} }, Context::Flags::Transient};
    input_handler.context().set_window(*this);
    m_highlighters.highlight(input_handler.context(), HighlightFlags::MoveOnly, display_buffer, range);
    m_builtin_highlighters.highlight(input_handler.context(), HighlightFlags::MoveOnly, display_buffer, range);

    CharCount column = coord.target == -1 ? find_display_column(lines[0], buffer(), coord) : coord.target;
    return { find_buffer_coord(lines[1], buffer(), column), column };
}

void Window::clear_display_buffer()
{
    m_display_buffer = DisplayBuffer{};
}

void Window::on_option_changed(const Option& option)
{
    run_hook_in_own_context("WinSetOption",
                            format("{}={}", option.name(), option.get_as_string()));

    // an highlighter might depend on the option, so we need to redraw
    force_redraw();
}

void Window::run_hook_in_own_context(StringView hook_name, StringView param)
{
    InputHandler hook_handler({ *m_buffer, Selection{} }, Context::Flags::Transient);
    hook_handler.context().set_window(*this);
    hooks().run_hook(hook_name, param, hook_handler.context());
}
}
<commit_msg>Fix scrolling down hiding the cursor with scrolloff == 0<commit_after>#include "window.hh"

#include "assert.hh"
#include "context.hh"
#include "highlighter.hh"
#include "hook_manager.hh"
#include "input_handler.hh"
#include "user_interface.hh"

#include <algorithm>
#include <sstream>

namespace Kakoune
{

// Implementation in highlighters.cc
void highlight_selections(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);
void expand_tabulations(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);
void expand_unprintable(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer, BufferRange range);

Window::Window(Buffer& buffer)
    : Scope(buffer),
      m_buffer(&buffer)
{
    run_hook_in_own_context("WinCreate", buffer.name());

    options().register_watcher(*this);

    m_builtin_highlighters.add_child({"tabulations"_str, make_simple_highlighter(expand_tabulations)});
    m_builtin_highlighters.add_child({"unprintable"_str, make_simple_highlighter(expand_unprintable)});
    m_builtin_highlighters.add_child({"selections"_str,  make_simple_highlighter(highlight_selections)});

    for (auto& option : options().flatten_options())
        on_option_changed(*option);
}

Window::~Window()
{
    run_hook_in_own_context("WinClose", buffer().name());
    options().unregister_watcher(*this);
}

void Window::display_line_at(LineCount buffer_line, LineCount display_line)
{
    if (display_line >= 0 or display_line < m_dimensions.line)
        m_position.line = std::max(0_line, buffer_line - display_line);
}

void Window::center_line(LineCount buffer_line)
{
    display_line_at(buffer_line, m_dimensions.line/2_line);
}

void Window::scroll(LineCount offset)
{
    m_position.line = std::max(0_line, m_position.line + offset);
}

void Window::scroll(CharCount offset)
{
    m_position.column = std::max(0_char, m_position.column + offset);
}

size_t Window::compute_hash(const Context& context) const
{
    size_t res = hash_values(m_position, context.ui().dimensions(), context.buffer().timestamp());

    auto& selections = context.selections();
    res = combine_hash(res, hash_value(selections.main_index()));
    for (auto& sel : selections)
        res = combine_hash(res, hash_values((const ByteCoord&)sel.cursor(), sel.anchor()));

    return res;
}

bool Window::needs_redraw(const Context& context) const
{
    size_t hash = compute_hash(context);
    return hash != m_hash;
}

const DisplayBuffer& Window::update_display_buffer(const Context& context)
{
    DisplayBuffer::LineList& lines = m_display_buffer.lines();
    lines.clear();

    m_dimensions = context.ui().dimensions();
    if (m_dimensions == CharCoord{0,0})
        return m_display_buffer;

    kak_assert(&buffer() == &context.buffer());
    scroll_to_keep_selection_visible_ifn(context);

    for (LineCount line = 0; line < m_dimensions.line; ++line)
    {
        LineCount buffer_line = m_position.line + line;
        if (buffer_line >= buffer().line_count())
            break;
        lines.emplace_back(AtomList{ {buffer(), buffer_line, buffer_line+1} });
    }

    m_display_buffer.compute_range();
    BufferRange range{{0,0}, buffer().end_coord()};
    m_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer, range);
    m_builtin_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer, range);

    // cut the start of the line before m_position.column
    for (auto& line : lines)
        line.trim(m_position.column, m_dimensions.column, true);
    m_display_buffer.optimize();

    m_hash = compute_hash(context);

    return m_display_buffer;
}

void Window::set_position(CharCoord position)
{
    m_position.line = std::max(0_line, position.line);
    m_position.column = std::max(0_char, position.column);
}

void Window::set_dimensions(CharCoord dimensions)
{
    m_dimensions = dimensions;
}

static LineCount adapt_view_pos(LineCount line, LineCount offset,
                                LineCount view_pos, LineCount view_size,
                                LineCount buffer_size)
{
    offset = std::min(offset, (view_size + 1) / 2);
    if (line - offset < view_pos)
        return std::max(0_line, line - offset);
    else if (line + offset >= view_pos + view_size)
        return std::max(0_line, line + offset - view_size + 1);
    return view_pos;
}

static CharCount adapt_view_pos(const DisplayBuffer& display_buffer, CharCount offset,
                                ByteCoord pos, CharCount view_pos, CharCount view_size)
{
    offset = std::min(offset, (view_size + 1) / 2);
    CharCount buffer_column = 0;
    CharCount non_buffer_column = 0;
    for (auto& line : display_buffer.lines())
    {
        for (auto& atom : line)
        {
            if (atom.has_buffer_range())
            {
                if (atom.begin() <= pos and atom.end() > pos)
                {
                    CharCount pos_beg, pos_end;
                    if (atom.type() == DisplayAtom::BufferRange)
                    {
                        auto& buf = atom.buffer();
                        pos_beg = buffer_column
                                + utf8::distance(buf.iterator_at(atom.begin()),
                                                 buf.iterator_at(pos));
                        pos_end = pos_beg+1;
                    }
                    else
                    {
                        pos_beg = buffer_column;
                        pos_end = pos_beg + atom.length();
                    }

                    if (pos_beg - offset < view_pos)
                        return std::max(0_char, pos_beg - offset);

                    if (pos_end + offset >= view_pos + view_size - non_buffer_column)
                        return pos_end + offset - view_size + non_buffer_column;
                }
                buffer_column += atom.length();
            }
            else
                non_buffer_column += atom.length();
        }
    }
    return view_pos;
}

void Window::scroll_to_keep_selection_visible_ifn(const Context& context)
{
    auto& selection = context.selections().main();
    const auto& anchor = selection.anchor();
    const auto& cursor  = selection.cursor();

    const CharCoord offset = options()["scrolloff"].get<CharCoord>();

    // scroll lines if needed, try to get as much of the selection visible as possible
    m_position.line = adapt_view_pos(anchor.line, offset.line, m_position.line,
                                     m_dimensions.line, buffer().line_count());
    m_position.line = adapt_view_pos(cursor.line,  offset.line, m_position.line,
                                     m_dimensions.line, buffer().line_count());

    // highlight only the line containing the cursor
    DisplayBuffer display_buffer;
    DisplayBuffer::LineList& lines = display_buffer.lines();
    lines.emplace_back(AtomList{ {buffer(), cursor.line, cursor.line+1} });

    display_buffer.compute_range();
    BufferRange range{cursor.line, cursor.line + 1};
    m_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer, range);
    m_builtin_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer, range);

    // now we can compute where the cursor is in display columns
    // (this is only valid if highlighting one line and multiple lines put
    // the cursor in the same position, however I do not find any sane example
    // of highlighters not doing that)
    m_position.column = adapt_view_pos(display_buffer, offset.column,
                                       anchor.line == cursor.line ? anchor : cursor.line,
                                       m_position.column, m_dimensions.column);
    m_position.column = adapt_view_pos(display_buffer, offset.column, cursor,
                                       m_position.column, m_dimensions.column);
}

namespace
{
CharCount find_display_column(const DisplayLine& line, const Buffer& buffer,
                              ByteCoord coord)
{
    CharCount column = 0;
    for (auto& atom : line)
    {
        if (atom.has_buffer_range() and
            coord >= atom.begin() and coord < atom.end())
        {
            if (atom.type() == DisplayAtom::BufferRange)
                column += utf8::distance(buffer.iterator_at(atom.begin()),
                                         buffer.iterator_at(coord));
            return column;
        }
        column += atom.length();
    }
    return column;
}

ByteCoord find_buffer_coord(const DisplayLine& line, const Buffer& buffer,
                            CharCount column)
{
    auto& range = line.range();
    for (auto& atom : line)
    {
        CharCount len = atom.length();
        if (atom.has_buffer_range() and column < len)
        {
            if (atom.type() == DisplayAtom::BufferRange)
                return utf8::advance(buffer.iterator_at(atom.begin()), buffer.iterator_at(range.end),
                                     std::max(0_char, column)).coord();
             return atom.begin();
        }
        column -= len;
    }
    return buffer.clamp(buffer.prev(range.end));
}
}

CharCoord Window::display_position(ByteCoord coord) const
{
    LineCount l = 0;
    for (auto& line : m_display_buffer.lines())
    {
        auto& range = line.range();
        if (range.begin <= coord and coord < range.end)
            return {l, find_display_column(line, buffer(), coord)};
        ++l;
    }
    return { 0, 0 };
}

ByteCoord Window::buffer_coord(CharCoord coord) const
{
    if (coord <= 0_line)
        coord = {0,0};
    if ((int)coord.line >= m_display_buffer.lines().size())
        coord = CharCoord{(int)m_display_buffer.lines().size()-1, INT_MAX};

    return find_buffer_coord(m_display_buffer.lines()[(int)coord.line],
                             buffer(), coord.column);
}

ByteCoord Window::offset_coord(ByteCoord coord, CharCount offset)
{
    return buffer().offset_coord(coord, offset);
}

ByteCoordAndTarget Window::offset_coord(ByteCoordAndTarget coord, LineCount offset)
{
    auto line = clamp(coord.line + offset, 0_line, buffer().line_count()-1);
    DisplayBuffer display_buffer;
    DisplayBuffer::LineList& lines = display_buffer.lines();
    lines.emplace_back(AtomList{ {buffer(), coord.line, coord.line+1} });
    lines.emplace_back(AtomList{ {buffer(), line, line+1} });
    display_buffer.compute_range();

    BufferRange range{ std::min(line, coord.line), std::max(line,coord.line)+1};

    InputHandler input_handler{{ *m_buffer, Selection{} }, Context::Flags::Transient};
    input_handler.context().set_window(*this);
    m_highlighters.highlight(input_handler.context(), HighlightFlags::MoveOnly, display_buffer, range);
    m_builtin_highlighters.highlight(input_handler.context(), HighlightFlags::MoveOnly, display_buffer, range);

    CharCount column = coord.target == -1 ? find_display_column(lines[0], buffer(), coord) : coord.target;
    return { find_buffer_coord(lines[1], buffer(), column), column };
}

void Window::clear_display_buffer()
{
    m_display_buffer = DisplayBuffer{};
}

void Window::on_option_changed(const Option& option)
{
    run_hook_in_own_context("WinSetOption",
                            format("{}={}", option.name(), option.get_as_string()));

    // an highlighter might depend on the option, so we need to redraw
    force_redraw();
}

void Window::run_hook_in_own_context(StringView hook_name, StringView param)
{
    InputHandler hook_handler({ *m_buffer, Selection{} }, Context::Flags::Transient);
    hook_handler.context().set_window(*this);
    hooks().run_hook(hook_name, param, hook_handler.context());
}
}
<|endoftext|>


<commit_before>/******************************
** Tsunagari Tile Engine     **
** world.cpp                 **
** Copyright 2011 OmegaSDG   **
******************************/

#include <Gosu/Utility.hpp>
#include <libxml/tree.h>

#include "area.h"
#include "common.h"
#include "player.h"
#include "log.h"
#include "resourcer.h"
#include "window.h"
#include "world.h"

static World* globalWorld = NULL;

World* World::getWorld()
{
	return globalWorld;
}

World::World(GameWindow* wnd, Resourcer* rc, ClientValues* conf)
	: rc(rc), wnd(wnd), conf(conf), area(NULL)
{
	globalWorld = this;
}

World::~World()
{
	delete area;
}

bool World::init()
{
	if (!processDescriptor()) // Try to load in descriptor.
		return false;

	// FIXME The player entity doesn't have a descriptor yet.
	player.reset(new Player(rc, NULL, conf));
	if (!player->init(xml.playerentity))
		return false;
	player->setPhase("down");

	wnd->setCaption(Gosu::widen(xml.name));
	return loadArea(xml.entry.area, xml.entry.coords);
}

void World::buttonDown(const Gosu::Button btn)
{
	area->buttonDown(btn);
}

void World::buttonUp(const Gosu::Button btn)
{
	area->buttonUp(btn);
}

void World::draw()
{
	area->draw();
}

bool World::needsRedraw() const
{
	return area->needsRedraw();
}

void World::update(unsigned long dt)
{
	area->update(dt);
}

bool World::processDescriptor()
{
	static const std::string descriptor = "world.conf";
	xmlChar* str;

	XMLDocRef doc = rc->getXMLDoc(descriptor, "world.dtd");
	if (!doc)
		return false;
	const xmlNode* root = xmlDocGetRootElement(doc.get()); // <world>
	if (!root)
		return false;

	xmlNode* node = root->xmlChildrenNode; // children of <world>
	for (; node != NULL; node = node->next) {
		if (!xmlStrncmp(node->name, BAD_CAST("name"), 5)) {
			str = xmlNodeGetContent(node);
			xml.name = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("author"), 7)) {
			str = xmlNodeGetContent(node);
			xml.author = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("player"), 7)) {
			str = xmlNodeGetContent(node);
			xml.playerentity = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("type"), 5)) {
			str = xmlGetProp(node, BAD_CAST("locality"));

			if (xmlStrncmp(str, BAD_CAST("local"), 6))
				xml.locality = LOCAL;

			else if (xmlStrncmp(str, BAD_CAST("network"), 8))
				xml.locality = NETWORK;

			else {
				Log::err(descriptor, "Invalid <locality> value");
				return false;
			}

			str = xmlGetProp(node, BAD_CAST("movement"));

			if (xmlStrncmp(str, BAD_CAST("turn"), 5))
				conf->movemode = TURN;

			else if (xmlStrncmp(str, BAD_CAST("tile"), 5))
				conf->movemode = TILE;

			else if (xmlStrncmp(str, BAD_CAST("notile"), 7))
				conf->movemode = NOTILE;

			else {
				Log::err(descriptor, "Invalid <movement> value");
				return false;
			}
		}
		if (!xmlStrncmp(node->name, BAD_CAST("entrypoint"), 11)) {
			str = xmlGetProp(node, BAD_CAST("area"));
			xml.entry.area = (char*)str;

			str = xmlGetProp(node, BAD_CAST("x"));
			xml.entry.coords.x = atol((char*)str);

			str = xmlGetProp(node, BAD_CAST("y"));
			xml.entry.coords.y = atol((char*)str);

			str = xmlGetProp(node, BAD_CAST("z"));
			xml.entry.coords.z = atol((char*)str);
		}
		if (!xmlStrncmp(node->name, BAD_CAST("scripts"), 13)) {
			node = node->xmlChildrenNode; // decend
		}
		if (!xmlStrncmp(node->name, BAD_CAST("script"), 7)) {
			str = xmlNodeGetContent(node);
			xml.scripts.push_back((char*)str);
		}
	}
	return true;
}

bool World::loadArea(const std::string& areaName, coord_t playerPos)
{
	Area* newArea = new Area(rc, this, player.get(), areaName);
	delete area;
	area = newArea;
	if (!area->init())
		return false;
	player->setArea(area);
	player->setCoordsByTile(playerPos);
	return true;
}

<commit_msg>Fixed string comparisons<commit_after>/******************************
** Tsunagari Tile Engine     **
** world.cpp                 **
** Copyright 2011 OmegaSDG   **
******************************/

#include <Gosu/Utility.hpp>
#include <libxml/tree.h>

#include "area.h"
#include "common.h"
#include "player.h"
#include "log.h"
#include "resourcer.h"
#include "window.h"
#include "world.h"

static World* globalWorld = NULL;

World* World::getWorld()
{
	return globalWorld;
}

World::World(GameWindow* wnd, Resourcer* rc, ClientValues* conf)
	: rc(rc), wnd(wnd), conf(conf), area(NULL)
{
	globalWorld = this;
}

World::~World()
{
	delete area;
}

bool World::init()
{
	if (!processDescriptor()) // Try to load in descriptor.
		return false;

	// FIXME The player entity doesn't have a descriptor yet.
	player.reset(new Player(rc, NULL, conf));
	if (!player->init(xml.playerentity))
		return false;
	player->setPhase("down");

	wnd->setCaption(Gosu::widen(xml.name));
	return loadArea(xml.entry.area, xml.entry.coords);
}

void World::buttonDown(const Gosu::Button btn)
{
	area->buttonDown(btn);
}

void World::buttonUp(const Gosu::Button btn)
{
	area->buttonUp(btn);
}

void World::draw()
{
	area->draw();
}

bool World::needsRedraw() const
{
	return area->needsRedraw();
}

void World::update(unsigned long dt)
{
	area->update(dt);
}

bool World::processDescriptor()
{
	static const std::string descriptor = "world.conf";
	xmlChar* str;

	XMLDocRef doc = rc->getXMLDoc(descriptor, "world.dtd");
	if (!doc)
		return false;
	const xmlNode* root = xmlDocGetRootElement(doc.get()); // <world>
	if (!root)
		return false;

	xmlNode* node = root->xmlChildrenNode; // children of <world>
	for (; node != NULL; node = node->next) {
		if (!xmlStrncmp(node->name, BAD_CAST("name"), 5)) {
			str = xmlNodeGetContent(node);
			xml.name = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("author"), 7)) {
			str = xmlNodeGetContent(node);
			xml.author = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("player"), 7)) {
			str = xmlNodeGetContent(node);
			xml.playerentity = (char*)str;
		}
		if (!xmlStrncmp(node->name, BAD_CAST("type"), 5)) {
			str = xmlGetProp(node, BAD_CAST("locality"));

			if (!xmlStrncmp(str, BAD_CAST("local"), 6))
				xml.locality = LOCAL;

			else if (!xmlStrncmp(str, BAD_CAST("network"), 8))
				xml.locality = NETWORK;

			else {
				Log::err(descriptor, "Invalid <locality> value");
				return false;
			}

			str = xmlGetProp(node, BAD_CAST("movement"));

			if (!xmlStrncmp(str, BAD_CAST("turn"), 5))
				conf->movemode = TURN;

			else if (!xmlStrncmp(str, BAD_CAST("tile"), 5))
				conf->movemode = TILE;

			else if (!xmlStrncmp(str, BAD_CAST("notile"), 7))
				conf->movemode = NOTILE;

			else {
				Log::err(descriptor, "Invalid <movement> value");
				return false;
			}
		}
		if (!xmlStrncmp(node->name, BAD_CAST("entrypoint"), 11)) {
			str = xmlGetProp(node, BAD_CAST("area"));
			xml.entry.area = (char*)str;

			str = xmlGetProp(node, BAD_CAST("x"));
			xml.entry.coords.x = atol((char*)str);

			str = xmlGetProp(node, BAD_CAST("y"));
			xml.entry.coords.y = atol((char*)str);

			str = xmlGetProp(node, BAD_CAST("z"));
			xml.entry.coords.z = atol((char*)str);
		}
		if (!xmlStrncmp(node->name, BAD_CAST("scripts"), 13)) {
			node = node->xmlChildrenNode; // decend
		}
		if (!xmlStrncmp(node->name, BAD_CAST("script"), 7)) {
			str = xmlNodeGetContent(node);
			xml.scripts.push_back((char*)str);
		}
	}
	return true;
}

bool World::loadArea(const std::string& areaName, coord_t playerPos)
{
	Area* newArea = new Area(rc, this, player.get(), areaName);
	delete area;
	area = newArea;
	if (!area->init())
		return false;
	player->setArea(area);
	player->setCoordsByTile(playerPos);
	return true;
}

<|endoftext|>


<commit_before>/*
 * Jonathan Doman
 * jonathan.doman@gmail.com
 */

#include "JsonRpc.h"

#include <curl/curl.h>

#include <stdexcept>
#include <cstring>
#include <cassert>

using namespace std;

// Adapted from bitcoin/src/util.cpp:EncodeBase64
static std::string encodeBase64( std::string str )
{
   const char s64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

   string ret;
   ret.reserve( (str.size() + 2) / 3 * 4 );

   int mode = 0;
   int left = 0;
   auto cur = str.data();
   auto end = cur + str.size();

   while( cur < end )
   {
      char enc = *(cur++);
      switch( mode )
      {
      case 0:
         ret += s64[enc >> 2];
         left = (enc & 0x3) << 4;
         mode = 1;
         break;

      case 1:
         ret += s64[left | (enc >> 4)];
         left = (enc & 0xF) << 2;
         mode = 2;
         break;

      case 2:
         ret += s64[left | (enc >> 6)];
         ret += s64[enc & 0x3F];
         mode = 0;
         break;
      }
   }

   if( mode != 0 )
   {
      ret += s64[left];
      ret += '=';
      if( mode == 1 )
         ret += '=';
   }

   return ret;
}

size_t recvPostData( char* ptr, size_t size, size_t nmemb, void* userdata )
{
   char** recvBuf = reinterpret_cast<char**>(userdata);
   size_t bytes = size * nmemb;
   *recvBuf = new char[bytes];
   memcpy( *recvBuf, ptr, bytes );
   return bytes;
}

JsonRpc::JsonRpc( const std::string& url,
                  const std::string& username,
                  const std::string& password )
 : _url(url),
   _headers(NULL)
{
   // Encode RPC username and password
   string authPair;
   authPair += username + ':' + password;
   string authHeader( "Authorization: Basic " );
   authHeader += encodeBase64( authPair );

   _headers = curl_slist_append( _headers, authHeader.c_str() );
   _headers = curl_slist_append( _headers, "Content-Type: application/json" );
}

JsonRpc::~JsonRpc()
{
   curl_slist_free_all( _headers );
}

Json::Value JsonRpc::call( const std::string& method, const Json::Value& json )
{
   auto curl = curl_easy_init();
   if( curl == NULL )
      throw std::runtime_error( "Failed to initialize cURL" );

   Json::Value copy( json );
   copy["method"] = method;
   Json::FastWriter writer;
   string data = writer.write( copy );

   char* recvData = NULL;

   curl_easy_setopt( curl, CURLOPT_URL, _url.c_str() );
   curl_easy_setopt( curl, CURLOPT_HTTPHEADER, _headers );
   curl_easy_setopt( curl, CURLOPT_POSTFIELDS, data.c_str() );
   curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, recvPostData );
   curl_easy_setopt( curl, CURLOPT_WRITEDATA, &recvData );

   curl_easy_perform( curl );

   Json::Reader reader;
   Json::Value response;
   reader.parse( recvData, recvData+strlen(recvData), response );
   delete [] recvData;

   curl_easy_cleanup( curl );

   // TODO: better error handling
   assert( response.isMember("result") );

   return response["result"];
}
<commit_msg>Add thread-id to JSON request<commit_after>/*
 * Jonathan Doman
 * jonathan.doman@gmail.com
 */

#include "JsonRpc.h"

#include <curl/curl.h>

#include <stdexcept>
#include <cstring>
#include <cassert>
#include <thread>
#include <functional>

using namespace std;

// Adapted from bitcoin/src/util.cpp:EncodeBase64
static std::string encodeBase64( std::string str )
{
   const char s64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

   string ret;
   ret.reserve( (str.size() + 2) / 3 * 4 );

   int mode = 0;
   int left = 0;
   auto cur = str.data();
   auto end = cur + str.size();

   while( cur < end )
   {
      char enc = *(cur++);
      switch( mode )
      {
      case 0:
         ret += s64[enc >> 2];
         left = (enc & 0x3) << 4;
         mode = 1;
         break;

      case 1:
         ret += s64[left | (enc >> 4)];
         left = (enc & 0xF) << 2;
         mode = 2;
         break;

      case 2:
         ret += s64[left | (enc >> 6)];
         ret += s64[enc & 0x3F];
         mode = 0;
         break;
      }
   }

   if( mode != 0 )
   {
      ret += s64[left];
      ret += '=';
      if( mode == 1 )
         ret += '=';
   }

   return ret;
}

size_t recvPostData( char* ptr, size_t size, size_t nmemb, void* userdata )
{
   char** recvBuf = reinterpret_cast<char**>(userdata);
   size_t bytes = size * nmemb;
   *recvBuf = new char[bytes];
   memcpy( *recvBuf, ptr, bytes );
   return bytes;
}

JsonRpc::JsonRpc( const std::string& url,
                  const std::string& username,
                  const std::string& password )
 : _url(url),
   _headers(NULL)
{
   // Encode RPC username and password
   string authPair;
   authPair += username + ':' + password;
   string authHeader( "Authorization: Basic " );
   authHeader += encodeBase64( authPair );

   _headers = curl_slist_append( _headers, authHeader.c_str() );
   _headers = curl_slist_append( _headers, "Content-Type: application/json" );
}

JsonRpc::~JsonRpc()
{
   curl_slist_free_all( _headers );
}

Json::Value JsonRpc::call( const std::string& method, const Json::Value& json )
{
   auto curl = curl_easy_init();
   if( curl == NULL )
      throw std::runtime_error( "Failed to initialize cURL" );

   Json::Value copy( json );
   copy["method"] = method;
   copy["id"] = static_cast<int>(hash<thread::id>()(this_thread::get_id()));
   Json::FastWriter writer;
   string data = writer.write( copy );

   char* recvData = NULL;

   curl_easy_setopt( curl, CURLOPT_URL, _url.c_str() );
   curl_easy_setopt( curl, CURLOPT_HTTPHEADER, _headers );
   curl_easy_setopt( curl, CURLOPT_POSTFIELDS, data.c_str() );
   curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, recvPostData );
   curl_easy_setopt( curl, CURLOPT_WRITEDATA, &recvData );

   curl_easy_perform( curl );

   Json::Reader reader;
   Json::Value response;
   reader.parse( recvData, recvData+strlen(recvData), response );
   delete [] recvData;

   curl_easy_cleanup( curl );

   // TODO: better error handling
   assert( response.isMember("result") );

   return response["result"];
}
<|endoftext|>