<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright 2015 Cloudius Systems
 */

#include <seastar/core/reactor.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/app-template.hh>
#include <seastar/core/circular_buffer.hh>
#include <seastar/core/distributed.hh>
#include <seastar/core/queue.hh>
#include <seastar/core/future-util.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/print.hh>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <seastar/http/httpd.hh>
#include <seastar/http/reply.hh>

using namespace std::chrono_literals;

namespace seastar {

namespace httpd {
http_stats::http_stats(http_server& server, const sstring& name)
 {
    namespace sm = seastar::metrics;
    std::vector<sm::label_instance> labels;

    labels.push_back(sm::label_instance("service", name));
    _metric_groups.add_group("httpd", {
            sm::make_derive("connections_total", [&server] { return server.total_connections(); }, sm::description("The total number of connections opened"), labels),
            sm::make_gauge("connections_current", [&server] { return server.current_connections(); }, sm::description("The current number of open  connections"), labels),
            sm::make_derive("read_errors", [&server] { return server.read_errors(); }, sm::description("The total number of errors while reading http requests"), labels),
            sm::make_derive("reply_errors", [&server] { return server.reply_errors(); }, sm::description("The total number of errors while replying to http"), labels),
            sm::make_derive("requests_served", [&server] { return server.requests_served(); }, sm::description("The total number of http requests served"), labels)
    });
}

sstring http_server_control::generate_server_name() {
    static thread_local uint16_t idgen;
    return seastar::format("http-{}", idgen++);
}

future<> connection::do_response_loop() {
    return _replies.pop_eventually().then(
        [this] (std::unique_ptr<reply> resp) {
            if (!resp) {
                // eof
                return make_ready_future<>();
            }
            _resp = std::move(resp);
            return start_response().then([this] {
                        return do_response_loop();
                    });
        });
}

future<> connection::start_response() {
    if (_resp->_body_writer) {
        return _resp->write_reply_to_connection(*this).then_wrapped([this] (auto f) {
            if (f.failed()) {
                // In case of an error during the write close the connection
                _server._respond_errors++;
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
                return make_ready_future<>();
            }
            return _write_buf.write("0\r\n\r\n", 5);
        }).then_wrapped([this ] (auto f) {
            if (f.failed()) {
                // We could not write the closing sequence
                // Something is probably wrong with the connection,
                // we should close it, so the client will disconnect
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
                return make_ready_future<>();
            } else {
                return _write_buf.flush();
            }
        }).then_wrapped([this] (auto f) {
            if (f.failed()) {
                // flush failed. just close the connection
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
            }
            _resp.reset();
            return make_ready_future<>();
        });
    }
    set_headers(*_resp);
    _resp->_headers["Content-Length"] = to_sstring(
            _resp->_content.size());
    return _write_buf.write(_resp->_response_line.begin(),
            _resp->_response_line.size()).then([this] {
        return _resp->write_reply_headers(*this);
    }).then([this] {
        return _write_buf.write("\r\n", 2);
    }).then([this] {
        return write_body();
    }).then([this] {
        return _write_buf.flush();
    }).then([this] {
        _resp.reset();
    });
}

connection::~connection() {
    --_server._current_connections;
    _server._connections.erase(_server._connections.iterator_to(*this));
    _server.maybe_idle();
}

bool connection::url_decode(const compat::string_view& in, sstring& out) {
    size_t pos = 0;
    sstring buff(in.length(), 0);
    for (size_t i = 0; i < in.length(); ++i) {
        if (in[i] == '%') {
            if (i + 3 <= in.size()) {
                buff[pos++] = hexstr_to_char(in, i + 1);
                i += 2;
            } else {
                return false;
            }
        } else if (in[i] == '+') {
            buff[pos++] = ' ';
        } else {
            buff[pos++] = in[i];
        }
    }
    buff.resize(pos);
    out = buff;
    return true;
}

void connection::on_new_connection() {
    ++_server._total_connections;
    ++_server._current_connections;
    _server._connections.push_back(*this);
}

future<> connection::read() {
    return do_until([this] {return _done;}, [this] {
        return read_one();
    }).then_wrapped([this] (future<> f) {
        // swallow error
        if (f.failed()) {
            _server._read_errors++;
        }
        f.ignore_ready_future();
        return _replies.push_eventually( {});
    }).finally([this] {
        return _read_buf.close();
    });
}

// Check if the request has a body, and if so read it. This function modifies
// the request object with the newly read body, and returns the object for
// further processing.
// FIXME: reading the entire request body into a string req->_content is a
// bad idea, because it may be very big. Instead, we should present to the
// handler a req->_content_stream, an input stream that reads from the request
// body - via a specialized input streams which reads exactly up to
// Content-Length or decodes chunked-encoding.
// FIXME: We currently support the case that there is a "Content-Length:"
// header - chunked encoding is not yet supported.
static future<std::unique_ptr<httpd::request>>
read_request_body(input_stream<char>& buf, std::unique_ptr<httpd::request> req) {
    if (!req->content_length) {
        return make_ready_future<std::unique_ptr<httpd::request>>(std::move(req));
    }
    return buf.read_exactly(req->content_length).then([req = std::move(req)] (temporary_buffer<char> body) mutable {
        req->content = seastar::to_sstring(std::move(body));
        return make_ready_future<std::unique_ptr<httpd::request>>(std::move(req));
    });
}

void connection::generate_error_reply_and_close(std::unique_ptr<httpd::request> req, reply::status_type status, const sstring& msg) {
    auto resp = std::make_unique<reply>();
    // TODO: Handle HTTP/2.0 when it releases
    resp->set_version(req->_version);
    resp->set_status(status, msg);
    resp->done();
    _done = true;
    _replies.push(std::move(resp));
}

future<> connection::read_one() {
    _parser.init();
    return _read_buf.consume(_parser).then([this] () mutable {
        if (_parser.eof()) {
            _done = true;
            return make_ready_future<>();
        }
        ++_server._requests_served;
        std::unique_ptr<httpd::request> req = _parser.get_parsed_request();
        if (_server._credentials) {
            req->protocol_name = "https";
        }

        size_t content_length_limit = _server.get_content_length_limit();
        sstring length_header = req->get_header("Content-Length");
        req->content_length = strtol(length_header.c_str(), nullptr, 10);

        if (req->content_length > content_length_limit) {
            generate_error_reply_and_close(std::move(req), reply::status_type::payload_too_large,
                    format("Content length limit ({}) exceeded: {}",
                            content_length_limit, req->content_length));
            return make_ready_future<>();
        }
        return read_request_body(_read_buf, std::move(req)).then([this] (std::unique_ptr<httpd::request> req) {
            return _replies.not_full().then([req = std::move(req), this] () mutable {
                return generate_reply(std::move(req));
            }).then([this](bool done) {
                _done = done;
            });
        });
    });
}

future<> connection::respond() {
    return do_response_loop().then_wrapped([this] (future<> f) {
        // swallow error
        if (f.failed()) {
            _server._respond_errors++;
        }
        f.ignore_ready_future();
        return _write_buf.close();
    });
}

future<> connection::write_body() {
    return _write_buf.write(_resp->_content.begin(),
            _resp->_content.size());
}

void connection::set_headers(reply& resp) {
    resp._headers["Server"] = "Seastar httpd";
    resp._headers["Date"] = _server._date;
}

future<bool> connection::generate_reply(std::unique_ptr<request> req) {
    auto resp = std::make_unique<reply>();
    bool conn_keep_alive = false;
    bool conn_close = false;
    auto it = req->_headers.find("Connection");
    if (it != req->_headers.end()) {
        if (it->second == "Keep-Alive") {
            conn_keep_alive = true;
        } else if (it->second == "Close") {
            conn_close = true;
        }
    }
    bool should_close;
    // TODO: Handle HTTP/2.0 when it releases
    resp->set_version(req->_version);

    if (req->_version == "1.0") {
        if (conn_keep_alive) {
            resp->_headers["Connection"] = "Keep-Alive";
        }
        should_close = !conn_keep_alive;
    } else if (req->_version == "1.1") {
        should_close = conn_close;
    } else {
        // HTTP/0.9 goes here
        should_close = true;
    }
    sstring url = set_query_param(*req.get());
    sstring version = req->_version;
    set_headers(*resp);
    resp->set_version(version);
    return _server._routes.handle(url, std::move(req), std::move(resp)).
    // Caller guarantees enough room
    then([this, should_close, version = std::move(version)](std::unique_ptr<reply> rep) {
        rep->set_version(version).done();
        this->_replies.push(std::move(rep));
        return make_ready_future<bool>(should_close);
    });
}
}

}
<commit_msg>httpd: remove redundant version assignment<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright 2015 Cloudius Systems
 */

#include <seastar/core/reactor.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/app-template.hh>
#include <seastar/core/circular_buffer.hh>
#include <seastar/core/distributed.hh>
#include <seastar/core/queue.hh>
#include <seastar/core/future-util.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/print.hh>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <seastar/http/httpd.hh>
#include <seastar/http/reply.hh>

using namespace std::chrono_literals;

namespace seastar {

namespace httpd {
http_stats::http_stats(http_server& server, const sstring& name)
 {
    namespace sm = seastar::metrics;
    std::vector<sm::label_instance> labels;

    labels.push_back(sm::label_instance("service", name));
    _metric_groups.add_group("httpd", {
            sm::make_derive("connections_total", [&server] { return server.total_connections(); }, sm::description("The total number of connections opened"), labels),
            sm::make_gauge("connections_current", [&server] { return server.current_connections(); }, sm::description("The current number of open  connections"), labels),
            sm::make_derive("read_errors", [&server] { return server.read_errors(); }, sm::description("The total number of errors while reading http requests"), labels),
            sm::make_derive("reply_errors", [&server] { return server.reply_errors(); }, sm::description("The total number of errors while replying to http"), labels),
            sm::make_derive("requests_served", [&server] { return server.requests_served(); }, sm::description("The total number of http requests served"), labels)
    });
}

sstring http_server_control::generate_server_name() {
    static thread_local uint16_t idgen;
    return seastar::format("http-{}", idgen++);
}

future<> connection::do_response_loop() {
    return _replies.pop_eventually().then(
        [this] (std::unique_ptr<reply> resp) {
            if (!resp) {
                // eof
                return make_ready_future<>();
            }
            _resp = std::move(resp);
            return start_response().then([this] {
                        return do_response_loop();
                    });
        });
}

future<> connection::start_response() {
    if (_resp->_body_writer) {
        return _resp->write_reply_to_connection(*this).then_wrapped([this] (auto f) {
            if (f.failed()) {
                // In case of an error during the write close the connection
                _server._respond_errors++;
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
                return make_ready_future<>();
            }
            return _write_buf.write("0\r\n\r\n", 5);
        }).then_wrapped([this ] (auto f) {
            if (f.failed()) {
                // We could not write the closing sequence
                // Something is probably wrong with the connection,
                // we should close it, so the client will disconnect
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
                return make_ready_future<>();
            } else {
                return _write_buf.flush();
            }
        }).then_wrapped([this] (auto f) {
            if (f.failed()) {
                // flush failed. just close the connection
                _done = true;
                _replies.abort(std::make_exception_ptr(std::logic_error("Unknown exception during body creation")));
                _replies.push(std::unique_ptr<reply>());
                f.ignore_ready_future();
            }
            _resp.reset();
            return make_ready_future<>();
        });
    }
    set_headers(*_resp);
    _resp->_headers["Content-Length"] = to_sstring(
            _resp->_content.size());
    return _write_buf.write(_resp->_response_line.begin(),
            _resp->_response_line.size()).then([this] {
        return _resp->write_reply_headers(*this);
    }).then([this] {
        return _write_buf.write("\r\n", 2);
    }).then([this] {
        return write_body();
    }).then([this] {
        return _write_buf.flush();
    }).then([this] {
        _resp.reset();
    });
}

connection::~connection() {
    --_server._current_connections;
    _server._connections.erase(_server._connections.iterator_to(*this));
    _server.maybe_idle();
}

bool connection::url_decode(const compat::string_view& in, sstring& out) {
    size_t pos = 0;
    sstring buff(in.length(), 0);
    for (size_t i = 0; i < in.length(); ++i) {
        if (in[i] == '%') {
            if (i + 3 <= in.size()) {
                buff[pos++] = hexstr_to_char(in, i + 1);
                i += 2;
            } else {
                return false;
            }
        } else if (in[i] == '+') {
            buff[pos++] = ' ';
        } else {
            buff[pos++] = in[i];
        }
    }
    buff.resize(pos);
    out = buff;
    return true;
}

void connection::on_new_connection() {
    ++_server._total_connections;
    ++_server._current_connections;
    _server._connections.push_back(*this);
}

future<> connection::read() {
    return do_until([this] {return _done;}, [this] {
        return read_one();
    }).then_wrapped([this] (future<> f) {
        // swallow error
        if (f.failed()) {
            _server._read_errors++;
        }
        f.ignore_ready_future();
        return _replies.push_eventually( {});
    }).finally([this] {
        return _read_buf.close();
    });
}

// Check if the request has a body, and if so read it. This function modifies
// the request object with the newly read body, and returns the object for
// further processing.
// FIXME: reading the entire request body into a string req->_content is a
// bad idea, because it may be very big. Instead, we should present to the
// handler a req->_content_stream, an input stream that reads from the request
// body - via a specialized input streams which reads exactly up to
// Content-Length or decodes chunked-encoding.
// FIXME: We currently support the case that there is a "Content-Length:"
// header - chunked encoding is not yet supported.
static future<std::unique_ptr<httpd::request>>
read_request_body(input_stream<char>& buf, std::unique_ptr<httpd::request> req) {
    if (!req->content_length) {
        return make_ready_future<std::unique_ptr<httpd::request>>(std::move(req));
    }
    return buf.read_exactly(req->content_length).then([req = std::move(req)] (temporary_buffer<char> body) mutable {
        req->content = seastar::to_sstring(std::move(body));
        return make_ready_future<std::unique_ptr<httpd::request>>(std::move(req));
    });
}

void connection::generate_error_reply_and_close(std::unique_ptr<httpd::request> req, reply::status_type status, const sstring& msg) {
    auto resp = std::make_unique<reply>();
    // TODO: Handle HTTP/2.0 when it releases
    resp->set_version(req->_version);
    resp->set_status(status, msg);
    resp->done();
    _done = true;
    _replies.push(std::move(resp));
}

future<> connection::read_one() {
    _parser.init();
    return _read_buf.consume(_parser).then([this] () mutable {
        if (_parser.eof()) {
            _done = true;
            return make_ready_future<>();
        }
        ++_server._requests_served;
        std::unique_ptr<httpd::request> req = _parser.get_parsed_request();
        if (_server._credentials) {
            req->protocol_name = "https";
        }

        size_t content_length_limit = _server.get_content_length_limit();
        sstring length_header = req->get_header("Content-Length");
        req->content_length = strtol(length_header.c_str(), nullptr, 10);

        if (req->content_length > content_length_limit) {
            generate_error_reply_and_close(std::move(req), reply::status_type::payload_too_large,
                    format("Content length limit ({}) exceeded: {}",
                            content_length_limit, req->content_length));
            return make_ready_future<>();
        }
        return read_request_body(_read_buf, std::move(req)).then([this] (std::unique_ptr<httpd::request> req) {
            return _replies.not_full().then([req = std::move(req), this] () mutable {
                return generate_reply(std::move(req));
            }).then([this](bool done) {
                _done = done;
            });
        });
    });
}

future<> connection::respond() {
    return do_response_loop().then_wrapped([this] (future<> f) {
        // swallow error
        if (f.failed()) {
            _server._respond_errors++;
        }
        f.ignore_ready_future();
        return _write_buf.close();
    });
}

future<> connection::write_body() {
    return _write_buf.write(_resp->_content.begin(),
            _resp->_content.size());
}

void connection::set_headers(reply& resp) {
    resp._headers["Server"] = "Seastar httpd";
    resp._headers["Date"] = _server._date;
}

future<bool> connection::generate_reply(std::unique_ptr<request> req) {
    auto resp = std::make_unique<reply>();
    bool conn_keep_alive = false;
    bool conn_close = false;
    auto it = req->_headers.find("Connection");
    if (it != req->_headers.end()) {
        if (it->second == "Keep-Alive") {
            conn_keep_alive = true;
        } else if (it->second == "Close") {
            conn_close = true;
        }
    }
    bool should_close;
    // TODO: Handle HTTP/2.0 when it releases
    resp->set_version(req->_version);

    if (req->_version == "1.0") {
        if (conn_keep_alive) {
            resp->_headers["Connection"] = "Keep-Alive";
        }
        should_close = !conn_keep_alive;
    } else if (req->_version == "1.1") {
        should_close = conn_close;
    } else {
        // HTTP/0.9 goes here
        should_close = true;
    }
    sstring url = set_query_param(*req.get());
    sstring version = req->_version;
    set_headers(*resp);
    return _server._routes.handle(url, std::move(req), std::move(resp)).
    // Caller guarantees enough room
    then([this, should_close, version = std::move(version)](std::unique_ptr<reply> rep) {
        rep->set_version(version).done();
        this->_replies.push(std::move(rep));
        return make_ready_future<bool>(should_close);
    });
}
}

}
<|endoftext|>


<commit_before>//
//#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <ctime>
#include <algorithm>
#include <cmath>
#include "GPIOClass.h"

using namespace std;

void Pulse(GPIOClass* pin, double cycles);
void Wait(double seconds);
clock_t timer;
double time_to_complete;
double resolution = 1000;

#define PI 4*atan(1)

int main (int argc, char *argv[]) {
	string type = argv[1];
	transform(type.begin(), type.end(), type.begin(), :: tolower);
	// lets assume that the way to run this is
	// pwm.exe [rising/falling/sine/constant]
	if (argc != 2) {
		cout << "Usage: pwm [rising/falling/sine/constant/blink]" << endl;
		return -1;
	}

	while (time_to_complete <= 0) {
		cout << "Input How Long To Run (in seconds)" << endl;
		cin >> time_to_complete;
	}

	GPIOClass* out1 = new GPIOClass("4");
	GPIOClass* in2 = new GPIOClass("17");

	out1->export_gpio();
	in2->export_gpio();

	out1->setdir_gpio("out");
	in2->setdir_gpio("in");

	cout << "Pins are setup." << endl;
	// avoiding flickering will be at 100hz
	// aka turn on and off 100 times a sec
	// a cycle of 0 is off
	// a cycle of 100 is on

	if (type == "rising") {
		clock_t finish = clock() + time_to_complete * CLOCKS_PER_SEC;
		double t = time_to_complete;
		while (clock() < finish) {
			// pulse for however long we need to to achieve brightness.
			t -= 1;
			//Pulse(out1, sin((2PI/2) * abs(1/t)));
			//Wait(sin((PI/2) * abs(1/t)));
			Pulse(out1, sin((2*PI) * abs(1/t)));
			Wait(sin((2*PI) * abs(1/t)));
		}
	}
	if (type == "falling") {

	}
	if (type == "sine") {

	}
	if (type == "constant") {
		out1->setval_gpio("1"); // turn the pin on
		Wait(time_to_complete); // sleep for number of cycles / 1/100 sec
		//cout << "Waiting during pulse" << endl;
		out1->setval_gpio("0"); // turn the pin off
	}
	if (type == "blink") { // aka. TESTR
		clock_t finish = clock() + time_to_complete * CLOCKS_PER_SEC;
		while (clock() < finish) {
			// pulse for however long we need to to achieve brightness.
			Pulse(out1, 1/resolution);
			Wait(2/resolution);
		}
	}
	cout << "Done." << endl;
}

//1 cycle is 1/100th of a second
//100 cycles is 1 sec
void Pulse(GPIOClass* pin, double cycles) {
	bool running = true;
	while (running) {
		pin->setval_gpio("1"); // turn the pin on
		Wait(cycles / resolution); // sleep for number of cycles / 1/100 sec
		//cout << "Waiting during pulse" << endl;
		pin->setval_gpio("0"); // turn the pin off
		running = false; // this is unnessesary but could be useful if modified a bit.
	}
}

void Wait ( double seconds )
{
	clock_t endwait;
	endwait = clock () + seconds * CLOCKS_PER_SEC ;
	while (clock() < endwait) {}
}
<commit_msg>Update pwm.cpp<commit_after>//
//#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <ctime>
#include <algorithm>
#include <cmath>
#include "GPIOClass.h"

using namespace std;

void Pulse(GPIOClass* pin, double cycles);
void Wait(double seconds);
clock_t timer;
double time_to_complete;
double resolution = 100;

#define PI 4*atan(1)

int main (int argc, char *argv[]) {
	string type = argv[1];
	transform(type.begin(), type.end(), type.begin(), :: tolower);
	// lets assume that the way to run this is
	// pwm.exe [rising/falling/sine/constant]
	if (argc != 2) {
		cout << "Usage: pwm [rising/falling/sine/constant/blink]" << endl;
		return -1;
	}

	while (time_to_complete <= 0) {
		cout << "Input How Long To Run (in seconds)" << endl;
		cin >> time_to_complete;
	}

	GPIOClass* out1 = new GPIOClass("4");
	GPIOClass* in2 = new GPIOClass("17");

	out1->export_gpio();
	in2->export_gpio();

	out1->setdir_gpio("out");
	in2->setdir_gpio("in");

	cout << "Pins are setup." << endl;
	// avoiding flickering will be at 100hz
	// aka turn on and off 100 times a sec
	// a cycle of 0 is off
	// a cycle of 100 is on

	if (type == "rising") {
		clock_t finish = clock() + time_to_complete * CLOCKS_PER_SEC;
		double t = time_to_complete;
		while (clock() < finish) {
			// pulse for however long we need to to achieve brightness.
			t -= 1;
			//Pulse(out1, sin((2PI/2) * abs(1/t)));
			//Wait(sin((PI/2) * abs(1/t)));
			Pulse(out1, sin((2*PI) * abs(1/t)));
			Wait(sin((2*PI) * abs(1/t)));
		}
	}
	if (type == "falling") {

	}
	if (type == "sine") {

	}
	if (type == "constant") {
		out1->setval_gpio("1"); // turn the pin on
		Wait(time_to_complete); // sleep for number of cycles / 1/100 sec
		//cout << "Waiting during pulse" << endl;
		out1->setval_gpio("0"); // turn the pin off
	}
	if (type == "blink") { // aka. TESTR
		clock_t finish = clock() + time_to_complete * CLOCKS_PER_SEC;
		while (clock() < finish) {
			// pulse for however long we need to to achieve brightness.
			Pulse(out1, 1/resolution);
			Wait(4/resolution);
		}
	}
	cout << "Done." << endl;
}

//1 cycle is 1/100th of a second
//100 cycles is 1 sec
void Pulse(GPIOClass* pin, double cycles) {
	bool running = true;
	while (running) {
		pin->setval_gpio("1"); // turn the pin on
		Wait(cycles / resolution); // sleep for number of cycles / 1/100 sec
		//cout << "Waiting during pulse" << endl;
		pin->setval_gpio("0"); // turn the pin off
		running = false; // this is unnessesary but could be useful if modified a bit.
	}
}

void Wait ( double seconds )
{
	clock_t endwait;
	endwait = clock () + seconds * CLOCKS_PER_SEC ;
	while (clock() < endwait) {}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>

#include "parsers.h"

using namespace std;

FastaReference::FastaReference(string ref_file_name){
    ifstream ref_file (ref_file_name);
    string L;
    uint64_t cummulative_len = 0;
    while( getline(ref_file, L)){
        size_t i = 0;
        string chrom;
        for(; L[i] != '\t'; i++){
            chrom.push_back(L[i]);
        }
        i += 1;
        string s_len;
        for (; L[i] !='\t'; i++){
            s_len.push_back(L[i]);
        }
        uint32_t chrom_len = stoul(s_len);
        cummulative_len += chrom_len;
        chromosomes.push_back(FastaReferenceData{ chrom,
                                                  chrom_len,
                                                  cummulative_len
                                                 });
    }
}

void FastaReference::get_ref_id(string search_name, int& chr_id){
    auto it = find_if(chromosomes.begin(),
                     chromosomes.end(),
                     [&search_name](const FastaReferenceData& chrom){
                        return chrom.name == search_name;
                      });
    int idx  = distance(chromosomes.begin(), it);
    chr_id = idx;
}

BedFile::BedFile(string bed_file_name){
    ifstream bed_file (bed_file_name);
}

int BedFile::get_interval(BedInterval& current_interval){
    string L;
    if(getline(bed_file, L)){
       stringstream Lstream(L);
        string chrom;
        string start_s;
        string end_s;
        getline(Lstream, chrom, '\t');
        getline(Lstream, start_s, '\t');
        getline(Lstream, start_s, '\t');
        current_interval = BedInterval{ chrom,
                                         stoul(start_s),
                                         stoul(end_s)};
        return 0;
    }
    else {return 1;}
}

int main() {return 0;}

<commit_msg>added parsers for FASTA reference and bed files<commit_after>#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>

#include "parsers.h"

using namespace std;

FastaReference::FastaReference(string ref_file_name){
    ifstream ref_file (ref_file_name);
    string L;
    uint64_t cummulative_len = 0;
    while( getline(ref_file, L)){
        size_t i = 0;
        string chrom;
        for(; L[i] != '\t'; i++){
            chrom.push_back(L[i]);
        }
        i += 1;
        string s_len;
        for (; L[i] !='\t'; i++){
            s_len.push_back(L[i]);
        }
        uint32_t chrom_len = stoul(s_len);
        cummulative_len += chrom_len;
        chromosomes.push_back(FastaReferenceData{ chrom,
                                                  chrom_len,
                                                  cummulative_len
                                                 });
    }
}

void FastaReference::get_ref_id(string search_name, int& chr_id){
    auto it = find_if(chromosomes.begin(),
                     chromosomes.end(),
                     [&search_name](const FastaReferenceData& chrom){
                        return chrom.name == search_name;
                      });
    int idx  = distance(chromosomes.begin(), it);
    chr_id = idx;
}

BedFile::BedFile(string bed_file_name){
    ifstream bed_file (bed_file_name);
}

int BedFile::get_interval(BedInterval& current_interval){
    string L;
    if(getline(bed_file, L)){
       stringstream Lstream(L);
        string chrom;
        string start_s;
        string end_s;
        getline(Lstream, chrom, '\t');
        getline(Lstream, start_s, '\t');
        getline(Lstream, start_s, '\t');
        current_interval = BedInterval{ chrom,
                                         stoul(start_s),
                                         stoul(end_s)};
        return 0;
    }
    else {return 1;}
}

int main() {
    FastaReference("test/test.fai");

    return 0;
}

<|endoftext|>


<commit_before>#include "file.hpp"
#include "../runtime.hpp"
#include "../platform/platform.hpp"
#include "string.hpp"
#include "fixnum.hpp"

namespace Mirb
{
	CharArray File::normalize_path(const CharArray &path)
	{
		CharArray result(path);

		if(!result.size())
			return result;

		result.localize();

#ifdef WIN32
		for(size_t i = 0; i < result.size(); ++i)
			if(result[i] == '\\')
				result[i] = '/';
#endif

		if(result[result.size()-1] == '/')
			result.shrink(result.size() - 1);

		return result;
	}

	bool File::absolute_path(const CharArray &path)
	{
#ifdef WIN32
		return path.size() > 1 && path[1] == ':';
#else
		return path.size() > 0 && path[0] == '/';
#endif
	}

	void JoinSegments::push(const CharArray &path)
	{
		File::normalize_path(path).split([&](const CharArray &part) {
			if(part.size() || (segments.size() == 0))
				segments.push_back(part);
		}, CharArray("/"));
	}

	CharArray JoinSegments::join()
	{
		int pos = 0;

		CharArray result;

		for(size_t i = segments.size(); i-- > 0;)
		{
			if(segments[i] == ".")
				continue;

			if(segments[i] == "..")
			{
				pos--;
				continue;
			}

			if(pos < 0)
				pos++;
			else
				result = segments[i] + "/" + result;
		}

		if(result.size())
			result.shrink(result.size() - 1);

		return result;
	}

	CharArray File::expand_path(CharArray relative)
	{
		if(!absolute_path(relative))
		{
			try
			{
				return join(Platform::cwd(), relative);
			}
			catch(Exception *)
			{
				return CharArray();
			}
		}
		else
			return relative;
	};

	CharArray File::expand_path(CharArray relative, CharArray from)
	{
		if(!absolute_path(relative))
		{
			if(!absolute_path(from))
				from = expand_path(from);

			return join(from, relative);
		}
		else
			return relative;
	};

	bool File::fnmatch(const CharArray &path, const CharArray &pattern)
	{
		size_t i = 0;
		size_t p = 0;
		size_t pr = pattern.size();

		for(size_t j = 0; j < pattern.size(); ++j)
			if(pattern[j] == '*')
				pr -= 1;

		if(pr > path.size())
			return false;

		while(p < pattern.size())
		{
			switch(pattern[p])
			{
				case '*':
				{
					if(++p >= pattern.size())
						return true;

					char_t terminator = pattern[p];

					if(terminator == '*')
						break;

					if(terminator == '?')
					{
						while(pr < path.size() - i)
							i++;

						break;
					}

					if(i >= path.size())
						return true;

					while(path[i] != terminator)
					{
						i++;

						if(i >= path.size())
							return false;
					}

					break;
				}

				case '?':
					if(i >= path.size())
						return false;

					p++, i++;
					break;

				default:
				{
					if(i >= path.size())
						return false;

					if(pattern[p] != path[i])
						return false;

					p++, i++;

					break;
				}
			}
		}

		return i == path.size() && p == pattern.size();
	}

	value_t File::rb_expand_path(String *relative, String *absolute)
	{
		if(absolute)
			return File::expand_path(relative->string, absolute->string).to_string();
		else
			return File::expand_path(relative->string).to_string();
	}

	CharArray File::join(const CharArray &left, const CharArray &right)
	{
		JoinSegments joiner;

		joiner.push(left);
		joiner.push(right);

		return joiner.join();
	}

	CharArray File::basename(CharArray path)
	{
		CharArray result = normalize_path(path);

		size_t i = result.size();

		while(i-- > 0)
			if(const_cast<const CharArray &>(result)[i] == '/')
				break;

		return result.copy(++i, result.size());
	}

	value_t dirname(String *path)
	{
		CharArray result = File::normalize_path(path->string);

		for(size_t i = result.size(); i-- > 0;)
			if(result[i] == '/')
			{
				if(i)
				{
					result.shrink(i);
					return result.to_string();
				}
				else
					return CharArray("/").to_string();
			}

		return CharArray(".").to_string();
	}

	value_t rb_join(size_t argc, value_t argv[])
	{
		JoinSegments joiner;

		for(size_t i = 0; i < argc; ++i)
		{
			auto string = raise_cast<String>(argv[i]);

			if(!string)
				return 0;

			joiner.push(string->string);
		}

		return joiner.join().to_string();
	}

	value_t exists(String *path)
	{
		return Value::from_bool(Platform::file_exists(path->string));
	}

	value_t file(String *path)
	{
		return Value::from_bool(Platform::is_file(path->string));
	}

	value_t directory(String *path)
	{
		return Value::from_bool(Platform::is_directory(path->string));
	}

	value_t rb_fnmatch(String *pattern, String *path)
	{
		return Value::from_bool(File::fnmatch(path->string, pattern->string));
	}

	void File::initialize()
	{
		context->file_class = define_class("File", context->io_class);

		singleton_method<Arg::Class<String>, &exists>(context->file_class, "exists?");
		singleton_method<Arg::Class<String>, &exists>(context->file_class, "exist?");
		singleton_method<Arg::Class<String>, &file>(context->file_class, "file?");
		singleton_method<Arg::Class<String>, &directory>(context->file_class, "directory?");
		singleton_method<Arg::Class<String>, Arg::Class<String>, &rb_fnmatch>(context->file_class, "fnmatch?");
		singleton_method<Arg::Class<String>, Arg::Class<String>, &rb_fnmatch>(context->file_class, "fnmatch");

		singleton_method<Arg::Class<String>, Arg::Default<Arg::Class<String>>, &rb_expand_path>(context->file_class, "expand_path");
		singleton_method<Arg::Class<String>, &dirname>(context->file_class, "dirname");
		singleton_method<Arg::Count, Arg::Values, &rb_join>(context->file_class, "join");

		set_const(context->file_class, Symbol::get("SEPARATOR"), String::get("/"));
	}
};

<commit_msg>Allow arrays in File#join.<commit_after>#include "file.hpp"
#include "../runtime.hpp"
#include "../platform/platform.hpp"
#include "string.hpp"
#include "array.hpp"
#include "fixnum.hpp"

namespace Mirb
{
	CharArray File::normalize_path(const CharArray &path)
	{
		CharArray result(path);

		if(!result.size())
			return result;

		result.localize();

#ifdef WIN32
		for(size_t i = 0; i < result.size(); ++i)
			if(result[i] == '\\')
				result[i] = '/';
#endif

		if(result[result.size()-1] == '/')
			result.shrink(result.size() - 1);

		return result;
	}

	bool File::absolute_path(const CharArray &path)
	{
#ifdef WIN32
		return path.size() > 1 && path[1] == ':';
#else
		return path.size() > 0 && path[0] == '/';
#endif
	}

	void JoinSegments::push(const CharArray &path)
	{
		File::normalize_path(path).split([&](const CharArray &part) {
			if(part.size() || (segments.size() == 0))
				segments.push_back(part);
		}, CharArray("/"));
	}

	CharArray JoinSegments::join()
	{
		int pos = 0;

		CharArray result;

		for(size_t i = segments.size(); i-- > 0;)
		{
			if(segments[i] == ".")
				continue;

			if(segments[i] == "..")
			{
				pos--;
				continue;
			}

			if(pos < 0)
				pos++;
			else
				result = segments[i] + "/" + result;
		}

		if(result.size())
			result.shrink(result.size() - 1);

		return result;
	}

	CharArray File::expand_path(CharArray relative)
	{
		if(!absolute_path(relative))
		{
			try
			{
				return join(Platform::cwd(), relative);
			}
			catch(Exception *)
			{
				return CharArray();
			}
		}
		else
			return relative;
	};

	CharArray File::expand_path(CharArray relative, CharArray from)
	{
		if(!absolute_path(relative))
		{
			if(!absolute_path(from))
				from = expand_path(from);

			return join(from, relative);
		}
		else
			return relative;
	};

	bool File::fnmatch(const CharArray &path, const CharArray &pattern)
	{
		size_t i = 0;
		size_t p = 0;
		size_t pr = pattern.size();

		for(size_t j = 0; j < pattern.size(); ++j)
			if(pattern[j] == '*')
				pr -= 1;

		if(pr > path.size())
			return false;

		while(p < pattern.size())
		{
			switch(pattern[p])
			{
				case '*':
				{
					if(++p >= pattern.size())
						return true;

					char_t terminator = pattern[p];

					if(terminator == '*')
						break;

					if(terminator == '?')
					{
						while(pr < path.size() - i)
							i++;

						break;
					}

					if(i >= path.size())
						return true;

					while(path[i] != terminator)
					{
						i++;

						if(i >= path.size())
							return false;
					}

					break;
				}

				case '?':
					if(i >= path.size())
						return false;

					p++, i++;
					break;

				default:
				{
					if(i >= path.size())
						return false;

					if(pattern[p] != path[i])
						return false;

					p++, i++;

					break;
				}
			}
		}

		return i == path.size() && p == pattern.size();
	}

	value_t File::rb_expand_path(String *relative, String *absolute)
	{
		if(absolute)
			return File::expand_path(relative->string, absolute->string).to_string();
		else
			return File::expand_path(relative->string).to_string();
	}

	CharArray File::join(const CharArray &left, const CharArray &right)
	{
		JoinSegments joiner;

		joiner.push(left);
		joiner.push(right);

		return joiner.join();
	}

	CharArray File::basename(CharArray path)
	{
		CharArray result = normalize_path(path);

		size_t i = result.size();

		while(i-- > 0)
			if(const_cast<const CharArray &>(result)[i] == '/')
				break;

		return result.copy(++i, result.size());
	}

	value_t dirname(String *path)
	{
		CharArray result = File::normalize_path(path->string);

		for(size_t i = result.size(); i-- > 0;)
			if(result[i] == '/')
			{
				if(i)
				{
					result.shrink(i);
					return result.to_string();
				}
				else
					return CharArray("/").to_string();
			}

		return CharArray(".").to_string();
	}

	value_t do_join(JoinSegments &joiner, size_t argc, value_t argv[])
	{
		for(size_t i = 0; i < argc; ++i)
		{
			auto array = try_cast<Array>(argv[i]);

			if(array)
			{
				do_join(joiner, array->size(), array->vector.raw());
				continue;
			}

			auto string = raise_cast<String>(argv[i]);

			if(!string)
				return 0;

			joiner.push(string->string);
		}
	}

	value_t rb_join(size_t argc, value_t argv[])
	{
		JoinSegments joiner;

		do_join(joiner, argc, argv);

		return joiner.join().to_string();
	}

	value_t exists(String *path)
	{
		return Value::from_bool(Platform::file_exists(path->string));
	}

	value_t file(String *path)
	{
		return Value::from_bool(Platform::is_file(path->string));
	}

	value_t directory(String *path)
	{
		return Value::from_bool(Platform::is_directory(path->string));
	}

	value_t rb_fnmatch(String *pattern, String *path)
	{
		return Value::from_bool(File::fnmatch(path->string, pattern->string));
	}

	void File::initialize()
	{
		context->file_class = define_class("File", context->io_class);

		singleton_method<Arg::Class<String>, &exists>(context->file_class, "exists?");
		singleton_method<Arg::Class<String>, &exists>(context->file_class, "exist?");
		singleton_method<Arg::Class<String>, &file>(context->file_class, "file?");
		singleton_method<Arg::Class<String>, &directory>(context->file_class, "directory?");
		singleton_method<Arg::Class<String>, Arg::Class<String>, &rb_fnmatch>(context->file_class, "fnmatch?");
		singleton_method<Arg::Class<String>, Arg::Class<String>, &rb_fnmatch>(context->file_class, "fnmatch");

		singleton_method<Arg::Class<String>, Arg::Default<Arg::Class<String>>, &rb_expand_path>(context->file_class, "expand_path");
		singleton_method<Arg::Class<String>, &dirname>(context->file_class, "dirname");
		singleton_method<Arg::Count, Arg::Values, &rb_join>(context->file_class, "join");

		set_const(context->file_class, Symbol::get("SEPARATOR"), String::get("/"));
	}
};

<|endoftext|>


<commit_before>/////////////////////////////////////////
//
//   OpenLieroX
//
//   Auxiliary Software class library
//
//   based on the work of JasonB
//   enhanced by Dark Charlie and Albert Zeyer
//
//   code under LGPL
//
/////////////////////////////////////////

// Font class
// Created 15/7/01
// Jason Boettcher

#include "LieroX.h"
#include "GfxPrimitives.h"
#include "Unicode.h"

//
// IMPORTANT: all surfaces in CFont are alpha blended which means they can (and mostly do) have another format than screen.
// Because of this we cannot use almost any of the color handling functions from GfxPrimitives
// Format of the surfaces is guaranted to be 32bit ARGB
//

// For font drawing use FontGenerator tool in /tools/fontgenerator

///////////////////
// Load a font
int CFont::Load(const std::string& fontname, bool _colour) {

	// Load the font
	LOAD_IMAGE_WITHALPHA(bmpFont, fontname);

	// Set the color key for this alpha surface (SDL_SetColorKey does not work for alpha blended surfaces)
	SetColorKeyAlpha(bmpFont, 255, 0, 255);

	Colorize = _colour;

	bmpWhite = gfxCreateSurfaceAlpha(bmpFont->w, bmpFont->h);
	bmpGreen = gfxCreateSurfaceAlpha(bmpFont->w, bmpFont->h);

	// Calculate the width of each character and number of characters
	Parse();

	// Pre-calculate some colours
	f_white = tLX->clNormalLabel;
	f_green = tLX->clChatText;

	// Precache some common font colors (but only if this font should be colorized)
	if (Colorize) {
		PreCalculate(bmpWhite, f_white);
		PreCalculate(bmpGreen, f_green);
	}

	return true;
}

///////////////////
// Shutdown the font
void CFont::Shutdown(void) {
	if (bmpWhite) {
		SDL_FreeSurface(bmpWhite);
		bmpWhite = NULL;
	}
	if (bmpGreen) {
		SDL_FreeSurface(bmpGreen);
		bmpGreen = NULL;
	}
}

//////////////////
// Helper function for CalculateWidth
// Checks, whether a vertical line is free
bool CFont::IsColumnFree(int x) {
	// it's only completelly see through
	for (int y = 0; y < bmpFont->h; y++) {
		if ((GetPixel(bmpFont, x, y) & ALPHASURFACE_AMASK) != 0)
			return false;
	}

	return true;
}

///////////////////
// Calculate character widths, number of characters and offsets
void CFont::Parse(void) {
	int x;
	UnicodeChar CurChar = FIRST_CHARACTER;
	int cur_w;

	// Lock the surface
	if (SDL_MUSTLOCK(bmpFont))
		SDL_LockSurface(bmpFont);

	Uint32 blue = SDL_MapRGB(bmpFont->format, 0, 0, 255);

	cur_w = 0;
	uint tmp_x = 0;
	for (x = 0; x < bmpFont->w; x++) {
		if (CurChar != ' ')
			// Ignore any free pixel columns before the character (but don't do this for spaces)
			while (IsColumnFree(x) && x < bmpFont->w)
				x++;

		// Read until a blue pixel or end of the image
		while (GetPixel(bmpFont, x, 0) != blue && x < bmpFont->w) {
			x++;
			cur_w++;
		}

		// Ignore any free pixel columns *after* the character
		tmp_x = x - 1; // -1 : blue line
		if (CurChar != ' ') {
			while (IsColumnFree(tmp_x)) {
				cur_w--;
				tmp_x--;
			}
		}

		// Blue pixel means end of the character
		FontWidth.push_back(cur_w);
		CharacterOffset.push_back(tmp_x - cur_w + 1);
		NumCharacters++;
		CurChar++;
		cur_w = 0;
	}

	// Unlock the surface
	if (SDL_MUSTLOCK(bmpFont))
		SDL_UnlockSurface(bmpFont);
}

///////////////////
// Precalculate a font's colour
void CFont::PreCalculate(SDL_Surface *bmpSurf, Uint32 colour) {
	Uint32 pixel;
	int x, y;

	FillSurface(bmpSurf, SDL_MapRGBA(bmpSurf->format, 255, 0, 255, 0));

	// Lock the surface
	if (SDL_MUSTLOCK(bmpSurf))
		SDL_LockSurface(bmpSurf);

	Uint8 R, G, B, A;
	Uint8 sr, sg, sb;
	GetColour3(colour, getMainPixelFormat(), &sr, &sg, &sb);

	// Outline font: replace white pixels with appropriate color, put black pixels
	if (OutlineFont) {
		for (y = 0; y < bmpSurf->h; y++) {
			for (x = 0; x < bmpSurf->w; x++) {
				pixel = GetPixel(bmpFont, x, y);
				GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

				if (R == 255 && G == 255 && B == 255)    // White
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, sr, sg, sb, A));
				else if (!R && !G && !B)   // Black
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, 0, 0, 0, A));
			}
		}
	// Not outline: replace black pixels with appropriate color
	} else {
		for (y = 0; y < bmpSurf->h; y++) {
			for (x = 0; x < bmpSurf->w; x++) {
				pixel = GetPixel(bmpFont, x, y);
				GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

				if (!R && !G && !B)   // Black
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, sr, sg, sb, A));
			}
		}
	}

	// Unlock the surface
	if (SDL_MUSTLOCK(bmpSurf))
		SDL_UnlockSurface(bmpSurf);
}

////////////////////
// Get height of multiline text
int CFont::GetHeight(const std::string& buf) {
	int numlines = 1;
	for (std::string::const_iterator i = buf.begin(); i != buf.end(); i++)
		if (*i == '\n') numlines++;
	return numlines * (bmpFont->h + VSpacing);
}

///////////////////
// Draws a font at X, Y, but visible only in specified rect
// HINT: not thread-safe
void CFont::DrawInRect(SDL_Surface *dst, int x, int y, int rectX, int rectY, int rectW, int rectH, Uint32 col, const std::string &txt)  {
	// Set the special clipping rectangle and then draw the font

	static SDL_Rect oldrect, newrect;
	SDL_GetClipRect(dst, &oldrect);  // Save the old rect

	// Fill in the details
	newrect.x = rectX;
	newrect.y = rectY;
	newrect.w = rectW;
	newrect.h = rectH;

	// Special clipping
	SDL_SetClipRect(dst, &newrect);

	// Blit the font
	DrawAdv(dst, x, y, 9999, col, txt);

	// Restore original clipping rect
	SDL_SetClipRect(dst, &oldrect);
}

///////////////////
// Draw a font (advanced)
void CFont::DrawAdv(SDL_Surface *dst, int x, int y, int max_w, Uint32 col, const std::string& txt) {

	// TODO: reduce the amount of local variables to 5

	int pos = 0; // Offset, x+pos is current character position in pixels
	int l;
	Uint32 pixel;
	int i, j;
	int w;
	int a, b; // a = offset in bmpFont

	// Clipping rectangle
	SDL_Rect oldrect = dst->clip_rect;
	SDL_Rect newrect = dst->clip_rect;

	int	top = oldrect.y;
	int left = oldrect.x;
	int right = MIN(oldrect.x + oldrect.w, x + max_w);
	int bottom = oldrect.y + oldrect.h;
	Uint8 R, G, B, A;

	// Set the newrect width and use this newrect temporarily to draw the font
	// We use this rect because of precached fonts which use SDL_Blit for drawing (and it takes care of cliprect)
	newrect.w = right - left;
	SDL_SetClipRect(dst, &newrect);

	// Look in the precached fonts if there's some for this color
	SDL_Surface *bmpCached = NULL;
	if (Colorize) {
		if (col == f_white)
			bmpCached = bmpWhite;
		else if (col == tLX->clBlack)
			bmpCached = bmpFont;
		else if (col == f_green)
			bmpCached = bmpGreen;
	}
	// Not colourize, bmpFont itself should be blitted without any changes, so it's precached
	else {
		bmpCached = bmpFont;
	}

	// Lock the surfaces
	if (SDL_MUSTLOCK(dst))
		SDL_LockSurface(dst);
	if (SDL_MUSTLOCK(bmpFont) && !bmpCached)       // If we use cached font, we do not access the pixels
		SDL_LockSurface(bmpFont);

	short clip_x, clip_y, clip_w, clip_h;

	// Adjust the color to the dest-suface format
	GetColour3(col, getMainPixelFormat(), &R, &G, &B);
	col = SDL_MapRGB(dst->format, R, G, B);

	pos = 0;
	for (std::string::const_iterator p = txt.begin(); p != txt.end();) {
		// Line break
		if (*p == '\n') {
			y += bmpFont->h + VSpacing;
			pos = 0;
			p++;

			// If the text wouldn't be drawn, just stop
			if (y > bottom)
				break;
			else
				continue;
		}

		// Translate and ignore unknown
		l = TranslateCharacter(p, txt.end());
		if (l == -1)
			continue;

		w = 0;
		a = CharacterOffset[l];

		// Precached fonts
		if (bmpCached) {
			DrawImageAdv(dst, bmpCached, a, 0, x + pos, y, FontWidth[l], bmpFont->h);
			pos += FontWidth[l] + Spacing;
			continue;
		}

		// Calculate clipping
		clip_x = MAX(left - x - pos, 0);
		clip_y = MAX(top - y, 0);
		clip_w = FontWidth[l];
		if (x + pos + clip_w >= right)
			clip_w = right - x - pos;
		clip_h = bmpFont->h;
		if (y + clip_h >= bottom)
			clip_h = bottom - y;

		short bpp = bmpFont->format->BytesPerPixel;
		Uint8 *src = (Uint8 *) bmpFont->pixels + a * bpp;
		Uint8 *px;

		// Outline font
		if (OutlineFont) {
			for (j = clip_y;j < clip_h;j++) {
				px = src;
				for (i = a + clip_x, b = clip_x;b < clip_w;i++, b++, px += bpp) {

					pixel = GetPixelFromAddr(px, bpp);
					GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

					// Put black pixels and colorize white ones
					if (R == 255 && G == 255 && B == 255)    // White
						PutPixelA(dst, x + pos + b, y + j, col, A);    // Put the pixel and blend it with background
					else if (!R && !G && !B)    // Black
						PutPixelA(dst, x + pos + b, y + j, tLX->clBlack, A);
				}
				src += bmpFont->pitch;
			}
		}
		// Not outline
		else {
			for (j = clip_y; j < clip_h; j++) {
				px = src;
				for (i = a + clip_x, b = clip_x; b < clip_w; i++, b++, px += bpp) {

					pixel = GetPixelFromAddr(px, bpp);
					GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

					// Put only black pixels
					if (!R && !G && !B)
						PutPixelA(dst, x + pos + b, y + j, col, A);
				}
				src += bmpFont->pitch;
			}
		}

		pos += FontWidth[l] + Spacing;
	}

	// Restore the original clipping rect
	SDL_SetClipRect(dst, &oldrect);

	// Unlock the surfaces
	if (SDL_MUSTLOCK(dst))
		SDL_UnlockSurface(dst);
	if (SDL_MUSTLOCK(bmpFont))
		SDL_UnlockSurface(bmpFont);

}

///////////////////
// Calculate the width of a string of text
int CFont::GetWidth(const std::string& buf) {
	int length = 0;
	short l;

	// Calculate the length of the text
	for (std::string::const_iterator p = buf.begin(); p != buf.end();) {
		l = TranslateCharacter(p, buf.end());
		if (l != -1)
			length += FontWidth[l] + Spacing;
	}

	return length;
}

/////////////////
// Get width of a single character
int CFont::GetCharacterWidth(UnicodeChar c)
{
	int l = TranslateCharacter(c);
	if (l != -1)
		return FontWidth[l];
	else
		return 0;
}

///////////////////
// Draws the text in centre alignment
void CFont::DrawCentre(SDL_Surface *dst, int x, int y, Uint32 col, const std::string& txt) {
	Draw(dst, x - GetWidth(txt) / 2, y, col, txt);
}

///////////////////
// Draw's the text in centre alignment
void CFont::DrawCentreAdv(SDL_Surface *dst, int x, int y, int min_x, int max_w, Uint32 col, const std::string& txt) {
	DrawAdv(dst, MAX(min_x, x - GetWidth(txt) / 2), y, max_w, col, txt);
}
<commit_msg>safty fixes for CFont<commit_after>/////////////////////////////////////////
//
//   OpenLieroX
//
//   Auxiliary Software class library
//
//   based on the work of JasonB
//   enhanced by Dark Charlie and Albert Zeyer
//
//   code under LGPL
//
/////////////////////////////////////////

// Font class
// Created 15/7/01
// Jason Boettcher

#include "LieroX.h"
#include "GfxPrimitives.h"
#include "Unicode.h"

//
// IMPORTANT: all surfaces in CFont are alpha blended which means they can (and mostly do) have another format than screen.
// Because of this we cannot use almost any of the color handling functions from GfxPrimitives
// Format of the surfaces is guaranted to be 32bit ARGB
//

// For font drawing use FontGenerator tool in /tools/fontgenerator

///////////////////
// Load a font
int CFont::Load(const std::string& fontname, bool _colour) {

	// Load the font
	LOAD_IMAGE_WITHALPHA(bmpFont, fontname);

	// Set the color key for this alpha surface (SDL_SetColorKey does not work for alpha blended surfaces)
	SetColorKeyAlpha(bmpFont, 255, 0, 255);

	Colorize = _colour;

	bmpWhite = gfxCreateSurfaceAlpha(bmpFont->w, bmpFont->h);
	bmpGreen = gfxCreateSurfaceAlpha(bmpFont->w, bmpFont->h);

	// Calculate the width of each character and number of characters
	Parse();

	// Pre-calculate some colours
	f_white = tLX->clNormalLabel;
	f_green = tLX->clChatText;

	// Precache some common font colors (but only if this font should be colorized)
	if (Colorize) {
		PreCalculate(bmpWhite, f_white);
		PreCalculate(bmpGreen, f_green);
	}

	return true;
}

///////////////////
// Shutdown the font
void CFont::Shutdown(void) {
	if (bmpWhite) {
		SDL_FreeSurface(bmpWhite);
		bmpWhite = NULL;
	}
	if (bmpGreen) {
		SDL_FreeSurface(bmpGreen);
		bmpGreen = NULL;
	}
}

//////////////////
// Helper function for CalculateWidth
// Checks, whether a vertical line is free
bool CFont::IsColumnFree(int x) {
	// it's only completelly see through
	for (int y = 0; y < bmpFont->h; y++) {
		if ((GetPixel(bmpFont, x, y) & ALPHASURFACE_AMASK) != 0)
			return false;
	}

	return true;
}

///////////////////
// Calculate character widths, number of characters and offsets
void CFont::Parse(void) {
	int x;
	UnicodeChar CurChar = FIRST_CHARACTER;
	int cur_w;

	// Lock the surface
	if (SDL_MUSTLOCK(bmpFont))
		SDL_LockSurface(bmpFont);

	Uint32 blue = SDL_MapRGB(bmpFont->format, 0, 0, 255);

	cur_w = 0;
	uint tmp_x = 0;
	for (x = 0; x < bmpFont->w; x++) {
		if (CurChar != ' ')
			// Ignore any free pixel columns before the character (but don't do this for spaces)
			while (IsColumnFree(x) && x < bmpFont->w)
				x++;

		// Read until a blue pixel or end of the image
		while (GetPixel(bmpFont, x, 0) != blue && x < bmpFont->w) {
			x++;
			cur_w++;
		}

		// Ignore any free pixel columns *after* the character
		if(cur_w > 0) {
			tmp_x = x - 1; // -1 : blue line
			if (CurChar != ' ') {
				while (IsColumnFree(tmp_x)) {
					cur_w--;
					tmp_x--;
					if(cur_w == 0) {
						printf("WARNING: cur_w becomes 0\n");
						break;
					}
				}
			}
		} else
			printf("WARNING: cur_w is <= 0\n");

		// Blue pixel means end of the character
		FontWidth.push_back(cur_w);
		CharacterOffset.push_back(tmp_x - cur_w + 1);
		NumCharacters++;
		CurChar++;
		cur_w = 0;
	}

	// Unlock the surface
	if (SDL_MUSTLOCK(bmpFont))
		SDL_UnlockSurface(bmpFont);
}

///////////////////
// Precalculate a font's colour
void CFont::PreCalculate(SDL_Surface *bmpSurf, Uint32 colour) {
	Uint32 pixel;
	int x, y;

	FillSurface(bmpSurf, SDL_MapRGBA(bmpSurf->format, 255, 0, 255, 0));

	// Lock the surface
	if (SDL_MUSTLOCK(bmpSurf))
		SDL_LockSurface(bmpSurf);

	Uint8 R, G, B, A;
	Uint8 sr, sg, sb;
	GetColour3(colour, getMainPixelFormat(), &sr, &sg, &sb);

	// Outline font: replace white pixels with appropriate color, put black pixels
	if (OutlineFont) {
		for (y = 0; y < bmpSurf->h; y++) {
			for (x = 0; x < bmpSurf->w; x++) {
				pixel = GetPixel(bmpFont, x, y);
				GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

				if (R == 255 && G == 255 && B == 255)    // White
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, sr, sg, sb, A));
				else if (!R && !G && !B)   // Black
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, 0, 0, 0, A));
			}
		}
	// Not outline: replace black pixels with appropriate color
	} else {
		for (y = 0; y < bmpSurf->h; y++) {
			for (x = 0; x < bmpSurf->w; x++) {
				pixel = GetPixel(bmpFont, x, y);
				GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

				if (!R && !G && !B)   // Black
					PutPixel(bmpSurf, x, y,
					         SDL_MapRGBA(bmpSurf->format, sr, sg, sb, A));
			}
		}
	}

	// Unlock the surface
	if (SDL_MUSTLOCK(bmpSurf))
		SDL_UnlockSurface(bmpSurf);
}

////////////////////
// Get height of multiline text
int CFont::GetHeight(const std::string& buf) {
	int numlines = 1;
	for (std::string::const_iterator i = buf.begin(); i != buf.end(); i++)
		if (*i == '\n') numlines++;
	return numlines * (bmpFont->h + VSpacing);
}

///////////////////
// Draws a font at X, Y, but visible only in specified rect
// HINT: not thread-safe
void CFont::DrawInRect(SDL_Surface *dst, int x, int y, int rectX, int rectY, int rectW, int rectH, Uint32 col, const std::string &txt)  {
	// Set the special clipping rectangle and then draw the font

	static SDL_Rect oldrect, newrect;
	SDL_GetClipRect(dst, &oldrect);  // Save the old rect

	// Fill in the details
	newrect.x = rectX;
	newrect.y = rectY;
	newrect.w = rectW;
	newrect.h = rectH;

	// Special clipping
	SDL_SetClipRect(dst, &newrect);

	// Blit the font
	DrawAdv(dst, x, y, 9999, col, txt);

	// Restore original clipping rect
	SDL_SetClipRect(dst, &oldrect);
}

///////////////////
// Draw a font (advanced)
void CFont::DrawAdv(SDL_Surface *dst, int x, int y, int max_w, Uint32 col, const std::string& txt) {

	// TODO: reduce the amount of local variables to 5

	int pos = 0; // Offset, x+pos is current character position in pixels
	int l;
	Uint32 pixel;
	int i, j;
	int w;
	int a, b; // a = offset in bmpFont

	// Clipping rectangle
	SDL_Rect oldrect = dst->clip_rect;
	SDL_Rect newrect = dst->clip_rect;

	int	top = oldrect.y;
	int left = oldrect.x;
	int right = MIN(oldrect.x + oldrect.w, x + max_w);
	int bottom = oldrect.y + oldrect.h;
	Uint8 R, G, B, A;

	// Set the newrect width and use this newrect temporarily to draw the font
	// We use this rect because of precached fonts which use SDL_Blit for drawing (and it takes care of cliprect)
	newrect.w = right - left;
	SDL_SetClipRect(dst, &newrect);

	// Look in the precached fonts if there's some for this color
	SDL_Surface *bmpCached = NULL;
	if (Colorize) {
		if (col == f_white)
			bmpCached = bmpWhite;
		else if (col == tLX->clBlack)
			bmpCached = bmpFont;
		else if (col == f_green)
			bmpCached = bmpGreen;
	}
	// Not colourize, bmpFont itself should be blitted without any changes, so it's precached
	else {
		bmpCached = bmpFont;
	}

	// Lock the surfaces
	if (SDL_MUSTLOCK(dst))
		SDL_LockSurface(dst);
	if (SDL_MUSTLOCK(bmpFont) && !bmpCached)       // If we use cached font, we do not access the pixels
		SDL_LockSurface(bmpFont);

	short clip_x, clip_y, clip_w, clip_h;

	// Adjust the color to the dest-suface format
	GetColour3(col, getMainPixelFormat(), &R, &G, &B);
	col = SDL_MapRGB(dst->format, R, G, B);

	pos = 0;
	for (std::string::const_iterator p = txt.begin(); p != txt.end();) {
		// Line break
		if (*p == '\n') {
			y += bmpFont->h + VSpacing;
			pos = 0;
			p++;

			// If the text wouldn't be drawn, just stop
			if (y > bottom)
				break;
			else
				continue;
		}

		// Translate and ignore unknown
		l = TranslateCharacter(p, txt.end());
		if (l == -1)
			continue;

		w = 0;
		a = CharacterOffset[l];

		// Precached fonts
		if (bmpCached) {
			DrawImageAdv(dst, bmpCached, a, 0, x + pos, y, FontWidth[l], bmpFont->h);
			pos += FontWidth[l] + Spacing;
			continue;
		}

		// Calculate clipping
		clip_x = MAX(left - x - pos, 0);
		clip_y = MAX(top - y, 0);
		clip_w = FontWidth[l];
		if (x + pos + clip_w >= right)
			clip_w = right - x - pos;
		clip_h = bmpFont->h;
		if (y + clip_h >= bottom)
			clip_h = bottom - y;

		short bpp = bmpFont->format->BytesPerPixel;
		Uint8 *src = (Uint8 *) bmpFont->pixels + a * bpp;
		Uint8 *px;

		// Outline font
		if (OutlineFont) {
			for (j = clip_y;j < clip_h;j++) {
				px = src;
				for (i = a + clip_x, b = clip_x;b < clip_w;i++, b++, px += bpp) {

					pixel = GetPixelFromAddr(px, bpp);
					GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

					// Put black pixels and colorize white ones
					if (R == 255 && G == 255 && B == 255)    // White
						PutPixelA(dst, x + pos + b, y + j, col, A);    // Put the pixel and blend it with background
					else if (!R && !G && !B)    // Black
						PutPixelA(dst, x + pos + b, y + j, tLX->clBlack, A);
				}
				src += bmpFont->pitch;
			}
		}
		// Not outline
		else {
			for (j = clip_y; j < clip_h; j++) {
				px = src;
				for (i = a + clip_x, b = clip_x; b < clip_w; i++, b++, px += bpp) {

					pixel = GetPixelFromAddr(px, bpp);
					GetColour4(pixel, bmpFont->format, &R, &G, &B, &A);

					// Put only black pixels
					if (!R && !G && !B)
						PutPixelA(dst, x + pos + b, y + j, col, A);
				}
				src += bmpFont->pitch;
			}
		}

		pos += FontWidth[l] + Spacing;
	}

	// Restore the original clipping rect
	SDL_SetClipRect(dst, &oldrect);

	// Unlock the surfaces
	if (SDL_MUSTLOCK(dst))
		SDL_UnlockSurface(dst);
	if (SDL_MUSTLOCK(bmpFont))
		SDL_UnlockSurface(bmpFont);

}

///////////////////
// Calculate the width of a string of text
int CFont::GetWidth(const std::string& buf) {
	int length = 0;
	short l;

	// Calculate the length of the text
	for (std::string::const_iterator p = buf.begin(); p != buf.end();) {
		l = TranslateCharacter(p, buf.end());
		if (l != -1)
			length += FontWidth[l] + Spacing;
	}

	return length;
}

/////////////////
// Get width of a single character
int CFont::GetCharacterWidth(UnicodeChar c)
{
	int l = TranslateCharacter(c);
	if (l != -1)
		return FontWidth[l];
	else
		return 0;
}

///////////////////
// Draws the text in centre alignment
void CFont::DrawCentre(SDL_Surface *dst, int x, int y, Uint32 col, const std::string& txt) {
	Draw(dst, x - GetWidth(txt) / 2, y, col, txt);
}

///////////////////
// Draw's the text in centre alignment
void CFont::DrawCentreAdv(SDL_Surface *dst, int x, int y, int min_x, int max_w, Uint32 col, const std::string& txt) {
	DrawAdv(dst, MAX(min_x, x - GetWidth(txt) / 2), y, max_w, col, txt);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * 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 above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * 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 "client_binary.h"

#ifdef HAVE_REMOTE_PROTOCOL

#include "server.h"
#include "utils.h"
#include "length.h"

#include <assert.h>
#include <sys/socket.h>

//
// Xapian binary client
//

BinaryClient::BinaryClient(XapiandServer *server_, ev::loop_ref *loop, int sock_, DatabasePool *database_pool_, ThreadPool *thread_pool_, double active_timeout_, double idle_timeout_)
	: BaseClient(server_, loop, sock_, database_pool_, thread_pool_, active_timeout_, idle_timeout_),
	  RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  running(false),
	  started(false)
{
	pthread_mutex_lock(&XapiandServer::static_mutex);
	int total_clients = XapiandServer::total_clients;
	int binary_clients = ++XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_CONN(this, "Got connection (sock=%d), %d binary client(s) of a total of %d connected.\n", sock, binary_clients, XapiandServer::total_clients);

	thread_pool->addTask(this);

	LOG_OBJ(this, "CREATED BINARY CLIENT! (%d clients)\n", binary_clients);
	assert(binary_clients <= total_clients);
}

BinaryClient::~BinaryClient()
{
	databases_map_t::const_iterator it(databases.begin());
	for (; it != databases.end(); it++) {
		Database *database = it->second;
		database_pool->checkin(&database);
	}

	pthread_mutex_lock(&XapiandServer::static_mutex);
	int binary_clients = --XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_OBJ(this, "DELETED BINARY CLIENT! (%d clients left)\n", binary_clients);
	assert(binary_clients >= 0);
}

void BinaryClient::on_read(const char *buf, ssize_t received)
{
	buffer.append(buf, received);
	while (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 = decode_length(&p, p_end, true);
		if (len == -1) {
			return;
		}
		std::string data = std::string(p, len);
		buffer.erase(0, p - o + len);

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

		messages_queue.push(msg);
	}
	pthread_mutex_lock(&qmtx);
	if (!messages_queue.empty()) {
		if (!running) {
			running = true;
			thread_pool->addTask(this);
		}
	}
	pthread_mutex_unlock(&qmtx);
}

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

	const char *msg_str = msg->dpos();
	size_t msg_size = msg->nbytes();

	std::string message = std::string(msg_str, msg_size);

	std::string buf(&msg->type, 1);
	buf += encode_length(msg_size);
	buf += message;
	LOG_BINARY_PROTO(this, "get_message: '%s'\n", repr(buf).c_str());

	result = message;

	message_type type = static_cast<message_type>(msg->type);

	delete msg;

	return type;
}

void BinaryClient::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;

	LOG_BINARY_PROTO(this, "send_message: '%s'\n", repr(buf).c_str());

	write(buf);
}

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

void BinaryClient::shutdown()
{
	destroy();
	// server->manager->async_shutdown.send();
}

Xapian::Database * BinaryClient::get_db(bool writable_)
{
	pthread_mutex_lock(&qmtx);
	if (endpoints.empty()) {
		pthread_mutex_unlock(&qmtx);
		return NULL;
	}
	Endpoints endpoints_ = endpoints;
	pthread_mutex_unlock(&qmtx);

	Database *database = NULL;
	if (!database_pool->checkout(&database, endpoints_, writable_)) {
		return NULL;
	}

	pthread_mutex_lock(&qmtx);
	databases[database->db] = database;
	pthread_mutex_unlock(&qmtx);

	return database->db;
}

void BinaryClient::release_db(Xapian::Database *db_)
{
	if (db_) {
		pthread_mutex_lock(&qmtx);
		Database *database = databases[db_];
		databases.erase(db_);
		pthread_mutex_unlock(&qmtx);

		database_pool->checkin(&database);
	}
}

void BinaryClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_, int flags)
{
	pthread_mutex_lock(&qmtx);
	endpoints.clear();
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		endpoints.insert(Endpoint(*i, std::string(), XAPIAND_BINARY_SERVERPORT));
	}
	dbpaths = dbpaths_;
	pthread_mutex_unlock(&qmtx);
}

void BinaryClient::run()
{
	while (true) {
		pthread_mutex_lock(&qmtx);
		if (started && messages_queue.empty()) {
			running = false;
			pthread_mutex_unlock(&qmtx);
			break;
		}
		pthread_mutex_unlock(&qmtx);

		try {
			if (started) {
				run_one();
			} else {
				started = true;
				msg_update(std::string());
			}
		} catch (const Xapian::NetworkError &e) {
			LOG_ERR(this, "ERROR: %s\n", e.get_msg().c_str());
		} catch (...) {
			LOG_ERR(this, "ERROR!\n");
		}
	}
}

#endif  /* HAVE_REMOTE_PROTOCOL */
<commit_msg>Endpoints accept only empty uri<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * 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 above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * 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 "client_binary.h"

#ifdef HAVE_REMOTE_PROTOCOL

#include "server.h"
#include "utils.h"
#include "length.h"

#include <assert.h>
#include <sys/socket.h>

//
// Xapian binary client
//

BinaryClient::BinaryClient(XapiandServer *server_, ev::loop_ref *loop, int sock_, DatabasePool *database_pool_, ThreadPool *thread_pool_, double active_timeout_, double idle_timeout_)
	: BaseClient(server_, loop, sock_, database_pool_, thread_pool_, active_timeout_, idle_timeout_),
	  RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  running(false),
	  started(false)
{
	pthread_mutex_lock(&XapiandServer::static_mutex);
	int total_clients = XapiandServer::total_clients;
	int binary_clients = ++XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_CONN(this, "Got connection (sock=%d), %d binary client(s) of a total of %d connected.\n", sock, binary_clients, XapiandServer::total_clients);

	thread_pool->addTask(this);

	LOG_OBJ(this, "CREATED BINARY CLIENT! (%d clients)\n", binary_clients);
	assert(binary_clients <= total_clients);
}

BinaryClient::~BinaryClient()
{
	databases_map_t::const_iterator it(databases.begin());
	for (; it != databases.end(); it++) {
		Database *database = it->second;
		database_pool->checkin(&database);
	}

	pthread_mutex_lock(&XapiandServer::static_mutex);
	int binary_clients = --XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_OBJ(this, "DELETED BINARY CLIENT! (%d clients left)\n", binary_clients);
	assert(binary_clients >= 0);
}

void BinaryClient::on_read(const char *buf, ssize_t received)
{
	buffer.append(buf, received);
	while (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 = decode_length(&p, p_end, true);
		if (len == -1) {
			return;
		}
		std::string data = std::string(p, len);
		buffer.erase(0, p - o + len);

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

		messages_queue.push(msg);
	}
	pthread_mutex_lock(&qmtx);
	if (!messages_queue.empty()) {
		if (!running) {
			running = true;
			thread_pool->addTask(this);
		}
	}
	pthread_mutex_unlock(&qmtx);
}

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

	const char *msg_str = msg->dpos();
	size_t msg_size = msg->nbytes();

	std::string message = std::string(msg_str, msg_size);

	std::string buf(&msg->type, 1);
	buf += encode_length(msg_size);
	buf += message;
	LOG_BINARY_PROTO(this, "get_message: '%s'\n", repr(buf).c_str());

	result = message;

	message_type type = static_cast<message_type>(msg->type);

	delete msg;

	return type;
}

void BinaryClient::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;

	LOG_BINARY_PROTO(this, "send_message: '%s'\n", repr(buf).c_str());

	write(buf);
}

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

void BinaryClient::shutdown()
{
	destroy();
	// server->manager->async_shutdown.send();
}

Xapian::Database * BinaryClient::get_db(bool writable_)
{
	pthread_mutex_lock(&qmtx);
	if (endpoints.empty()) {
		pthread_mutex_unlock(&qmtx);
		return NULL;
	}
	Endpoints endpoints_ = endpoints;
	pthread_mutex_unlock(&qmtx);

	Database *database = NULL;
	if (!database_pool->checkout(&database, endpoints_, writable_)) {
		return NULL;
	}

	pthread_mutex_lock(&qmtx);
	databases[database->db] = database;
	pthread_mutex_unlock(&qmtx);

	return database->db;
}

void BinaryClient::release_db(Xapian::Database *db_)
{
	if (db_) {
		pthread_mutex_lock(&qmtx);
		Database *database = databases[db_];
		databases.erase(db_);
		pthread_mutex_unlock(&qmtx);

		database_pool->checkin(&database);
	}
}

void BinaryClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_, int flags)
{
	pthread_mutex_lock(&qmtx);
	endpoints.clear();
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		endpoints.insert(Endpoint(*i));
	}
	dbpaths = dbpaths_;
	pthread_mutex_unlock(&qmtx);
}

void BinaryClient::run()
{
	while (true) {
		pthread_mutex_lock(&qmtx);
		if (started && messages_queue.empty()) {
			running = false;
			pthread_mutex_unlock(&qmtx);
			break;
		}
		pthread_mutex_unlock(&qmtx);

		try {
			if (started) {
				run_one();
			} else {
				started = true;
				msg_update(std::string());
			}
		} catch (const Xapian::NetworkError &e) {
			LOG_ERR(this, "ERROR: %s\n", e.get_msg().c_str());
		} catch (...) {
			LOG_ERR(this, "ERROR!\n");
		}
	}
}

#endif  /* HAVE_REMOTE_PROTOCOL */
<|endoftext|>