<commit_before>#include "http.h"
#include <fcntl.h>
#include <fstream>

#ifdef _WIN32
#   include <io.h>
#endif

namespace enji {

int cb_http_message_begin(http_parser* parser) {
    return 0;
}

int cb_http_url(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_url(at, len);
}

int cb_http_status(http_parser* parser, const char* at, size_t len) {
    std::cerr << "Got unhandled status: " << String(at, len) << std::endl;
    return 0;
}

int cb_http_header_field(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_field(at, len);
}

int cb_http_header_value(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_value(at, len);
}

int cb_http_headers_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_headers_complete();
}

int cb_http_body(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_body(at, len);
}

int cb_http_message_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_message_complete();
}

http_parser_settings& get_http_settings() {
    static http_parser_settings http_settings = {};
    http_settings.on_message_begin = cb_http_message_begin;
    http_settings.on_url = cb_http_url;
    http_settings.on_status = cb_http_status;
    http_settings.on_header_field = cb_http_header_field;
    http_settings.on_header_value = cb_http_header_value;
    http_settings.on_headers_complete = cb_http_headers_complete;
    http_settings.on_body = cb_http_body;
    http_settings.on_message_complete = cb_http_message_complete;
    return http_settings;
}

HttpConnection::HttpConnection(HttpServer* parent, size_t id)
:   Connection(parent, id),
    parent_(parent) {
    parser_.reset(new http_parser{});
    http_parser_init(parser_.get(), HTTP_REQUEST);
    parser_.get()->data = this;

    request_.reset(new HttpRequest{});
}

const HttpRequest& HttpConnection::request() const {
    return *request_.get();
}

int HttpConnection::on_http_url(const char* at, size_t len) {
    request_->url_ += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_field(const char* at, size_t len) {
    check_header_finished();
    read_header_.first += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_value(const char* at, size_t len) {
    read_header_.second += String{at, len};
    return 0;
}

int HttpConnection::on_http_headers_complete() {
    check_header_finished();
    return 0;
}

int HttpConnection::on_http_body(const char* at, size_t len) {
    request_->body_ += String{at, len};
    return 0;
}

const String MULTIPART_FORM_DATA = "multipart/form-data; boundary=";

int HttpConnection::on_message_complete() {
    message_completed_ = true;

    auto expect_iter = request_->headers_.find("Expect");

    if (expect_iter != request_->headers_.end()) {
        if (expect_iter->second == "100-continue") {
            message_completed_ = false;
            std::ostringstream buf;
            buf << "HTTP/1.1 100 Continue\r\n";
            write_chunk(buf);
        }
    }

    auto content_type_iter = request_->headers_.find("Content-Type");
    if (content_type_iter != request_->headers_.end()) {
        String boundary;
        auto multipart = content_type_iter->second.find(MULTIPART_FORM_DATA);
        if (multipart != String::npos) {
            auto boundary_start = multipart + MULTIPART_FORM_DATA.size();
            boundary = content_type_iter->second.substr(boundary_start);
        }

        std::vector<size_t> occurrences;
        size_t start = 0;

        boundary = "--" + boundary;

        while ((start = request_->body_.find(boundary, start)) != String::npos) {
            occurrences.push_back(start);
            start += boundary.length();
        }

        const char* body = &request_->body_.front();
        for (size_t occurence = 1; occurence < occurrences.size(); ++occurence) {
            auto file_content = String{
                body + occurrences[occurence - 1] + boundary.length() + 2, // Skip boundary + "\r\n"
                body + occurrences[occurence]
            };

            auto headers_finished = file_content.find("\r\n\r\n");
            auto headers = file_content.substr(0, headers_finished);

            std::regex regex("Content-Disposition: form-data; name=\"(.+)\"; filename=\"(.+)\"");
            std::smatch match_groups;
            std::regex_search(headers, match_groups, regex);

            File file;

            if (!match_groups.empty()) {
                file.name_ = match_groups[1].str();
                file.filename_ = match_groups[2].str();
            }

            if (file.name_.empty() && file.filename_.empty()) {
                continue;
            }

            auto file_body = file_content.substr(headers_finished + 4);

            file.body_ = std::move(file_body);

            request_->files_.emplace_back(std::move(file));
        }
    }

    return 0;
}

void HttpConnection::handle_input(TransferBlock data) {
    http_parser_execute(parser_.get(), &get_http_settings(), data.data, data.size);

    if (message_completed_) {
        request_->method_ = http_method_str(static_cast<http_method>(parser_.get()->method));
        parent_->call_handler(*request_.get(), this);
    }
}

void HttpConnection::check_header_finished() {
    if (!read_header_.first.empty() && !read_header_.second.empty()) {
        request_->headers_.insert(Header{std::move(read_header_)});
    }
}

HttpResponse::HttpResponse(HttpConnection* conn)
:   conn_{conn},
    response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    headers_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    body_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    full_response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary} {
}

HttpResponse::~HttpResponse() {
    close();
}

HttpResponse& HttpResponse::response(int code) {
    code_ = code;
    response_ << "HTTP/1.1 " << code << "\r\n";
    return *this;
}

HttpResponse& HttpResponse::add_headers(std::vector<std::pair<String, String>> headers) {
    for (auto&& h : headers) {
        add_header(h.first, h.second);
    }
    return *this;
}

HttpResponse& HttpResponse::add_header(const String& name, const String& value) {
    if (headers_sent_) {
        throw std::runtime_error("Can't add headers to response. Headers already sent");
    }
    headers_ << name << ": " << value << "\r\n";
    return *this;
}

bool stream2stream(std::stringstream& output, std::stringstream& input) {
    const size_t alloc_block = 32 * 1024;
    char tmp[alloc_block];
    bool written_smth = false;
    while (input) {
        input.read(tmp, sizeof(tmp));
        size_t size = input.gcount();
        output.write(tmp, size);
        if (size) {
            written_smth = true;
        }
    }
    return written_smth;
}

void stream2conn(Connection* conn, std::stringstream& buf) {
    while (buf) {
        size_t alloc_block = 32 * 1024;
        char* data = new char[alloc_block];
        buf.read(data, alloc_block);
        const size_t size = buf.gcount();
        TransferBlock block = {data, size};
        conn->write_chunk(block);
    }
}

HttpResponse& HttpResponse::body(const String& value) {
    body_ << value;
    return *this;
}

HttpResponse& HttpResponse::body(std::stringstream& buf) {
    stream2stream(body_, buf);
    return *this;
}

HttpResponse& HttpResponse::body(const void* data, size_t length) {
    body_.write(static_cast<const char*>(data), length);
    return *this;
}

void HttpResponse::flush() {
    if (!headers_sent_) {
        if (!stream2stream(full_response_, response_)) {
            full_response_ << "HTTP/1.1 200\r\n";
        }

        body_.seekp(0, std::ios::end);
        auto body_size = body_.tellp();

        std::ostringstream body_size_stream;
        body_size_stream << body_size;
        add_header("Content-length", body_size_stream.str());

        stream2stream(full_response_, headers_);
        headers_sent_ = true;

        full_response_ << "\r\n";
    }

    stream2stream(full_response_, body_);
    stream2conn(conn_, full_response_);
}

void HttpResponse::close() {
    flush();
    conn_->close();
}

HttpRoute::HttpRoute(const char* path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(const char* path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

std::smatch HttpRoute::match(const String& url) const {
    std::smatch match_groups;
    std::regex_search(url, match_groups, path_match_);
    return match_groups;
}

void HttpRoute::call_handler(const HttpRequest& req, HttpResponse& out) {
    handler_(req, out);
}

HttpServer::HttpServer(ServerOptions&& options)
:   Server{std::move(options)} {
    create_connection([this]() {
        return std::make_shared<HttpConnection>(this, counter_++); });
}

void HttpServer::routes(std::vector<HttpRoute>&& routes) {
    routes_ = std::move(routes);
}

void HttpServer::add_route(HttpRoute&& route) {
    routes_.emplace_back(route);
}

void HttpServer::call_handler(HttpRequest& request, HttpConnection* bind) {
    bool matched = false;
    HttpResponse out{bind};

    for (auto&& route : routes_) {
        auto matches = route.match(request.url());
        if (!matches.empty()) {
            request.set_match(matches);
            route.call_handler(request, out);
            matched = true;
        }
    }

    if (!matched) {
        out.response(404);
    }

    std::cout << request.method() << " " << request.url() << " " << out.code() << std::endl;
}

String match1_filename(const HttpRequest& req) {
   return req.match()[1].str();
}

HttpRoute::Handler serve_static(std::function<String(const HttpRequest& req)> request2file, const Config& config) {
    return HttpRoute::Handler{
        [&] (const HttpRequest& req, HttpResponse& out)
    {
        static_file(request2file(req), out, config);
    }};
}

HttpRoute::Handler serve_static(const String& root_dir, std::function<String(const HttpRequest& req)> request2file) {
    return HttpRoute::Handler{
        [&] (const HttpRequest& req, HttpResponse& out)
    {
        const auto request_file = request2file(req);
        response_file(path_join(root_dir, request_file), out);
    }};
}

void static_file(const String& filename, HttpResponse& out, const Config& config) {
    response_file(path_join(config["STATIC_ROOT_DIR"].str(), filename), out);
}

void response_file(const String& filename, HttpResponse& out) {
    uv_fs_t open_req;

#ifdef _WIN32
    int open_mode = _S_IREAD;
#else
    int open_mode = S_IRUSR;
#endif

    uv_fs_open(nullptr, &open_req, filename.c_str(), O_RDONLY, open_mode, nullptr);
    auto open_req_exit = Defer{[&open_req] { uv_fs_req_cleanup(&open_req); }};
    const auto fd = static_cast<uv_file>(open_req.result);

    if (fd < 0) {
        out.response(404);
        return;
    }

    uv_fs_t read_req;
    const size_t alloc_block = 64 * 1024;
    size_t offset = 0;
    char mem[alloc_block];
    while (true) {
        uv_buf_t buf[] = {uv_buf_init(mem, sizeof(mem))};
        int read = uv_fs_read(nullptr, &read_req, fd, buf, 1, offset, nullptr);
        auto read_req_exit = Defer{[&read_req] { uv_fs_req_cleanup(&read_req); }};
        out.body(buf[0].base, read);
        offset += read;
        if (static_cast<unsigned int>(read) < buf[0].len) {
            break;
        }
    }

    uv_fs_t close_req;
    uv_fs_close(nullptr, &close_req, fd, nullptr);
    auto close_req_exit = Defer{[&close_req] { uv_fs_req_cleanup(&close_req); }};
}

void temporary_redirect(const String& redirect_to, HttpResponse& out) {
    out.response(307);
    out.add_header("Location", redirect_to);
    out.close();
}

} // namespace enji<commit_msg>Ok, try this captures<commit_after>#include "http.h"
#include <fcntl.h>
#include <fstream>

#ifdef _WIN32
#   include <io.h>
#endif

namespace enji {

int cb_http_message_begin(http_parser* parser) {
    return 0;
}

int cb_http_url(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_url(at, len);
}

int cb_http_status(http_parser* parser, const char* at, size_t len) {
    std::cerr << "Got unhandled status: " << String(at, len) << std::endl;
    return 0;
}

int cb_http_header_field(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_field(at, len);
}

int cb_http_header_value(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_header_value(at, len);
}

int cb_http_headers_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_headers_complete();
}

int cb_http_body(http_parser* parser, const char* at, size_t len) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_http_body(at, len);
}

int cb_http_message_complete(http_parser* parser) {
    HttpConnection& handler = *reinterpret_cast<HttpConnection*>(parser->data);
    return handler.on_message_complete();
}

http_parser_settings& get_http_settings() {
    static http_parser_settings http_settings = {};
    http_settings.on_message_begin = cb_http_message_begin;
    http_settings.on_url = cb_http_url;
    http_settings.on_status = cb_http_status;
    http_settings.on_header_field = cb_http_header_field;
    http_settings.on_header_value = cb_http_header_value;
    http_settings.on_headers_complete = cb_http_headers_complete;
    http_settings.on_body = cb_http_body;
    http_settings.on_message_complete = cb_http_message_complete;
    return http_settings;
}

HttpConnection::HttpConnection(HttpServer* parent, size_t id)
:   Connection(parent, id),
    parent_(parent) {
    parser_.reset(new http_parser{});
    http_parser_init(parser_.get(), HTTP_REQUEST);
    parser_.get()->data = this;

    request_.reset(new HttpRequest{});
}

const HttpRequest& HttpConnection::request() const {
    return *request_.get();
}

int HttpConnection::on_http_url(const char* at, size_t len) {
    request_->url_ += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_field(const char* at, size_t len) {
    check_header_finished();
    read_header_.first += String{at, len};
    return 0;
}

int HttpConnection::on_http_header_value(const char* at, size_t len) {
    read_header_.second += String{at, len};
    return 0;
}

int HttpConnection::on_http_headers_complete() {
    check_header_finished();
    return 0;
}

int HttpConnection::on_http_body(const char* at, size_t len) {
    request_->body_ += String{at, len};
    return 0;
}

const String MULTIPART_FORM_DATA = "multipart/form-data; boundary=";

int HttpConnection::on_message_complete() {
    message_completed_ = true;

    auto expect_iter = request_->headers_.find("Expect");

    if (expect_iter != request_->headers_.end()) {
        if (expect_iter->second == "100-continue") {
            message_completed_ = false;
            std::ostringstream buf;
            buf << "HTTP/1.1 100 Continue\r\n";
            write_chunk(buf);
        }
    }

    auto content_type_iter = request_->headers_.find("Content-Type");
    if (content_type_iter != request_->headers_.end()) {
        String boundary;
        auto multipart = content_type_iter->second.find(MULTIPART_FORM_DATA);
        if (multipart != String::npos) {
            auto boundary_start = multipart + MULTIPART_FORM_DATA.size();
            boundary = content_type_iter->second.substr(boundary_start);
        }

        std::vector<size_t> occurrences;
        size_t start = 0;

        boundary = "--" + boundary;

        while ((start = request_->body_.find(boundary, start)) != String::npos) {
            occurrences.push_back(start);
            start += boundary.length();
        }

        const char* body = &request_->body_.front();
        for (size_t occurence = 1; occurence < occurrences.size(); ++occurence) {
            auto file_content = String{
                body + occurrences[occurence - 1] + boundary.length() + 2, // Skip boundary + "\r\n"
                body + occurrences[occurence]
            };

            auto headers_finished = file_content.find("\r\n\r\n");
            auto headers = file_content.substr(0, headers_finished);

            std::regex regex("Content-Disposition: form-data; name=\"(.+)\"; filename=\"(.+)\"");
            std::smatch match_groups;
            std::regex_search(headers, match_groups, regex);

            File file;

            if (!match_groups.empty()) {
                file.name_ = match_groups[1].str();
                file.filename_ = match_groups[2].str();
            }

            if (file.name_.empty() && file.filename_.empty()) {
                continue;
            }

            auto file_body = file_content.substr(headers_finished + 4);

            file.body_ = std::move(file_body);

            request_->files_.emplace_back(std::move(file));
        }
    }

    return 0;
}

void HttpConnection::handle_input(TransferBlock data) {
    http_parser_execute(parser_.get(), &get_http_settings(), data.data, data.size);

    if (message_completed_) {
        request_->method_ = http_method_str(static_cast<http_method>(parser_.get()->method));
        parent_->call_handler(*request_.get(), this);
    }
}

void HttpConnection::check_header_finished() {
    if (!read_header_.first.empty() && !read_header_.second.empty()) {
        request_->headers_.insert(Header{std::move(read_header_)});
    }
}

HttpResponse::HttpResponse(HttpConnection* conn)
:   conn_{conn},
    response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    headers_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    body_{std::stringstream::in | std::stringstream::out | std::stringstream::binary},
    full_response_{std::stringstream::in | std::stringstream::out | std::stringstream::binary} {
}

HttpResponse::~HttpResponse() {
    close();
}

HttpResponse& HttpResponse::response(int code) {
    code_ = code;
    response_ << "HTTP/1.1 " << code << "\r\n";
    return *this;
}

HttpResponse& HttpResponse::add_headers(std::vector<std::pair<String, String>> headers) {
    for (auto&& h : headers) {
        add_header(h.first, h.second);
    }
    return *this;
}

HttpResponse& HttpResponse::add_header(const String& name, const String& value) {
    if (headers_sent_) {
        throw std::runtime_error("Can't add headers to response. Headers already sent");
    }
    headers_ << name << ": " << value << "\r\n";
    return *this;
}

bool stream2stream(std::stringstream& output, std::stringstream& input) {
    const size_t alloc_block = 32 * 1024;
    char tmp[alloc_block];
    bool written_smth = false;
    while (input) {
        input.read(tmp, sizeof(tmp));
        size_t size = input.gcount();
        output.write(tmp, size);
        if (size) {
            written_smth = true;
        }
    }
    return written_smth;
}

void stream2conn(Connection* conn, std::stringstream& buf) {
    while (buf) {
        size_t alloc_block = 32 * 1024;
        char* data = new char[alloc_block];
        buf.read(data, alloc_block);
        const size_t size = buf.gcount();
        TransferBlock block = {data, size};
        conn->write_chunk(block);
    }
}

HttpResponse& HttpResponse::body(const String& value) {
    body_ << value;
    return *this;
}

HttpResponse& HttpResponse::body(std::stringstream& buf) {
    stream2stream(body_, buf);
    return *this;
}

HttpResponse& HttpResponse::body(const void* data, size_t length) {
    body_.write(static_cast<const char*>(data), length);
    return *this;
}

void HttpResponse::flush() {
    if (!headers_sent_) {
        if (!stream2stream(full_response_, response_)) {
            full_response_ << "HTTP/1.1 200\r\n";
        }

        body_.seekp(0, std::ios::end);
        auto body_size = body_.tellp();

        std::ostringstream body_size_stream;
        body_size_stream << body_size;
        add_header("Content-length", body_size_stream.str());

        stream2stream(full_response_, headers_);
        headers_sent_ = true;

        full_response_ << "\r\n";
    }

    stream2stream(full_response_, body_);
    stream2conn(conn_, full_response_);
}

void HttpResponse::close() {
    flush();
    conn_->close();
}

HttpRoute::HttpRoute(const char* path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, Handler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(const char* path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

HttpRoute::HttpRoute(String&& path, FuncHandler handler)
:   path_{path},
    handler_{handler},
    path_match_(path) {
}

std::smatch HttpRoute::match(const String& url) const {
    std::smatch match_groups;
    std::regex_search(url, match_groups, path_match_);
    return match_groups;
}

void HttpRoute::call_handler(const HttpRequest& req, HttpResponse& out) {
    handler_(req, out);
}

HttpServer::HttpServer(ServerOptions&& options)
:   Server{std::move(options)} {
    create_connection([this]() {
        return std::make_shared<HttpConnection>(this, counter_++); });
}

void HttpServer::routes(std::vector<HttpRoute>&& routes) {
    routes_ = std::move(routes);
}

void HttpServer::add_route(HttpRoute&& route) {
    routes_.emplace_back(route);
}

void HttpServer::call_handler(HttpRequest& request, HttpConnection* bind) {
    bool matched = false;
    HttpResponse out{bind};

    for (auto&& route : routes_) {
        auto matches = route.match(request.url());
        if (!matches.empty()) {
            request.set_match(matches);
            route.call_handler(request, out);
            matched = true;
        }
    }

    if (!matched) {
        out.response(404);
    }

    std::cout << request.method() << " " << request.url() << " " << out.code() << std::endl;
}

String match1_filename(const HttpRequest& req) {
   return req.match()[1].str();
}

HttpRoute::Handler serve_static(std::function<String(const HttpRequest& req)> request2file, const Config& config) {
    return HttpRoute::Handler{
        [request2file_bind{request2file}, config_bind{config}]
        (const HttpRequest& req, HttpResponse& out)
    {
        static_file(request2file_bind(req), out, config_bind);
    }};
}

HttpRoute::Handler serve_static(const String& root_dir, std::function<String(const HttpRequest& req)> request2file) {
    return HttpRoute::Handler{
        [root_dir_bind{root_dir}, request2file_bind{request2file}]
        (const HttpRequest& req, HttpResponse& out)
    {
        const auto request_file = request2file_bind(req);
        response_file(path_join(root_dir_bind, request_file), out);
    }};
}

void static_file(const String& filename, HttpResponse& out, const Config& config) {
    response_file(path_join(config["STATIC_ROOT_DIR"].str(), filename), out);
}

void response_file(const String& filename, HttpResponse& out) {
    uv_fs_t open_req;

#ifdef _WIN32
    int open_mode = _S_IREAD;
#else
    int open_mode = S_IRUSR;
#endif

    uv_fs_open(nullptr, &open_req, filename.c_str(), O_RDONLY, open_mode, nullptr);
    auto open_req_exit = Defer{[&open_req] { uv_fs_req_cleanup(&open_req); }};
    const auto fd = static_cast<uv_file>(open_req.result);

    if (fd < 0) {
        out.response(404);
        return;
    }

    uv_fs_t read_req;
    const size_t alloc_block = 64 * 1024;
    size_t offset = 0;
    char mem[alloc_block];
    while (true) {
        uv_buf_t buf[] = {uv_buf_init(mem, sizeof(mem))};
        int read = uv_fs_read(nullptr, &read_req, fd, buf, 1, offset, nullptr);
        auto read_req_exit = Defer{[&read_req] { uv_fs_req_cleanup(&read_req); }};
        out.body(buf[0].base, read);
        offset += read;
        if (static_cast<unsigned int>(read) < buf[0].len) {
            break;
        }
    }

    uv_fs_t close_req;
    uv_fs_close(nullptr, &close_req, fd, nullptr);
    auto close_req_exit = Defer{[&close_req] { uv_fs_req_cleanup(&close_req); }};
}

void temporary_redirect(const String& redirect_to, HttpResponse& out) {
    out.response(307);
    out.add_header("Location", redirect_to);
    out.close();
}

} // namespace enji<|endoftext|>


<commit_before>#ifndef FILEUTILS_HPP
#define FILEUTILS_HPP
#include<QString>
#include<QStringList>
#include<QFile>
#include<QFileInfo>
#include<QDir>

namespace fileutils{
	/*Directoryのコピーを行う*/
    bool cp_R(const QString from,const QString to){
		if(!QFileInfo(from).isDir())return false;
		if(to.isEmpty()) return false;
		if(!QDir().mkdir(to)) return false;

		QDir dir(from);
		QSringList list = dir.entryList();
		QStringListIterator i(list);
		QString b,bf;
		while(i.hasNext()){
			b = i.next();
			bf = from + "/" + b;
			//std::cout<<"=="<<bf.toStdString()<<std::endl;
			if(bf == from + "/" + QString(".") or bf == from + "/" + QString("..")) continue;
			if(QFileInfo(bf).isDir()){
				cp_R(bf,to + "/" + b);
			}else{
				if(!QFile::copy(bf,to + "/" + b)) return false;
			}
		}
		return true;
	}
}
#endif
<commit_msg>修正途中に間違えて消した部分を修復<commit_after>#ifndef FILEUTILS_HPP
#define FILEUTILS_HPP
#include<QString>
#include<QStringList>
#include<QFile>
#include<QFileInfo>
#include<QDir>

namespace fileutils{
	/*Directoryのコピーを行う*/
    bool cp_R(const QString from,const QString to){
		if(!QFileInfo(from).isDir())return false;
		if(to.isEmpty()) return false;
		if(!QDir().mkdir(to)) return false;

		QDir dir(from);
		QStringList list = dir.entryList();
		QStringListIterator i(list);
		QString b,bf;
		while(i.hasNext()){
			b = i.next();
			bf = from + "/" + b;
			//std::cout<<"=="<<bf.toStdString()<<std::endl;
			if(bf == from + "/" + QString(".") or bf == from + "/" + QString("..")) continue;
			if(QFileInfo(bf).isDir()){
				cp_R(bf,to + "/" + b);
			}else{
				if(!QFile::copy(bf,to + "/" + b)) return false;
			}
		}
		return true;
	}
}
#endif
<|endoftext|>


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

#include "precompiled.h"

#include <cctype>
#include <sstream>

#include <gumbo.h>

#include "font_manager.h"
#include "font_util.h"
#include "fplbase/fpl_common.h"

namespace flatui {

using mathfu::vec3;
using mathfu::vec3_packed;

// Replace whitespace with a single space.
// This emulates how HTML processes raw text fields.
// Appends trimmed text to `out`.
// Returns reference to `out`.
std::string &TrimHtmlWhitespace(const char *text, bool trim_leading_whitespace,
                                std::string *out) {
  const char *t = text;

  if (trim_leading_whitespace) {
    while (std::isspace(*t)) ++t;
  }

  for (;;) {
    // Output non-whitespace.
    while (!std::isspace(*t) && *t != '\0') out->push_back(*t++);

    // Break out if at end of input. Note that we do *not* trim all
    // trailing whitespace.
    if (*t == '\0') break;

    // Skip whitespace.
    while (std::isspace(*t)) ++t;

    // Insert single space to compensate for trimmed whitespace.
    out->push_back(' ');
  }
  return *out;
}

// Remove trailing whitespace. Then add a `prefix` if there is any preceeding
// text.
static std::string &StartHtmlLine(const char *prefix, std::string *out) {
  // Trim trailing whitespace.
  while (std::isspace(out->back())) out->pop_back();

  // Append the new lines in `prefix`.
  if (!out->empty()) {
    out->append(prefix);
  }
  return *out;
}

// The very first text output we want to trim the leading whitespace.
// We should also trim if the previous text ends in whitespace.
static bool ShouldTrimLeadingWhitespace(const std::vector<HtmlSection> &s) {
  if (s.size() <= 1) return true;

  const std::string &prev_text =
      s.back().text.empty() ? s[s.size() - 2].text : s.back().text;
  return std::isspace(prev_text.back());
}

static void GumboTreeToHtmlSections(const GumboNode *node,
                                    std::vector<HtmlSection> *s) {
  switch (node->type) {
    // Process non-text elements, possibly recursing into child nodes.
    case GUMBO_NODE_ELEMENT: {
      const GumboElement &element = node->v.element;

      // Tree prefix processing.
      switch (element.tag) {
        case GUMBO_TAG_A:
          // Start a new section for the anchor.
          if (!s->back().text.empty()) {
            s->push_back(HtmlSection());
          }
          break;

        case GUMBO_TAG_P:
        case GUMBO_TAG_H1:  // fallthrough
        case GUMBO_TAG_H2:  // fallthrough
        case GUMBO_TAG_H3:  // fallthrough
        case GUMBO_TAG_H4:  // fallthrough
        case GUMBO_TAG_H5:  // fallthrough
        case GUMBO_TAG_H6:  // fallthrough
          StartHtmlLine("\n\n", &s->back().text);
          break;

        default:
          break;
      }
      const size_t node_section = s->size() - 1;

      // Tree children processing via recursion.
      for (unsigned int i = 0; i < element.children.length; ++i) {
        const GumboNode *child =
            static_cast<GumboNode *>(element.children.data[i]);
        GumboTreeToHtmlSections(child, s);
      }

      // Tree postfix processing.
      switch (element.tag) {
        case GUMBO_TAG_A: {
          // Record the link address.
          GumboAttribute *href =
              gumbo_get_attribute(&element.attributes, "href");
          if (href != nullptr) {
            (*s)[node_section].link = href->value;
          }

          // Start a new section for the non-anchor.
          s->push_back(HtmlSection());
          break;
        }

        case GUMBO_TAG_HR:
        case GUMBO_TAG_P:  // fallthrough
          s->back().text.append("\n\n");
          break;

        case GUMBO_TAG_H1:  // fallthrough
        case GUMBO_TAG_H2:  // fallthrough
        case GUMBO_TAG_H3:  // fallthrough
        case GUMBO_TAG_H4:  // fallthrough
        case GUMBO_TAG_H5:  // fallthrough
        case GUMBO_TAG_H6:  // fallthrough
        case GUMBO_TAG_BR:
          s->back().text.append("\n");
          break;

        default:
          break;
      }
      break;
    }

    // Append text without excessive whitespaces.
    case GUMBO_NODE_TEXT:
      TrimHtmlWhitespace(node->v.text.text, ShouldTrimLeadingWhitespace(*s),
                         &s->back().text);
      break;

    // Ignore other node types.
    default:
      break;
  }
}

std::vector<HtmlSection> ParseHtml(const char *html) {
  // Ensure there is an HtmlSection that can be appended to.
  std::vector<HtmlSection> s(1);

  // Parse html into tree, with Gumbo, then process the tree.
  GumboOutput *gumbo = gumbo_parse(html);
  GumboTreeToHtmlSections(gumbo->root, &s);
  gumbo_destroy_output(&kGumboDefaultOptions, gumbo);

  // Prune empty last section.
  if (s.back().text.empty()) {
    s.pop_back();
  }
  return s;
}

static size_t NumUnderlineVertices(const FontBuffer &buffer) {
  const int32_t kUnderlineVerticesPerGlyph = 2;
  const int32_t kExtraUnderlineVerticesPerInfo = 2;
  size_t num_verts = 0;
  // The first strip doesn't need a triangle to stich.
  num_verts -= kExtraUnderlineVerticesPerInfo;
  auto &slices = buffer.get_slices();
  for (size_t i = 0; i < slices.size(); ++i) {
    auto regions = slices.at(i).get_underline_info();
    for (size_t i = 0; i < regions.size(); ++i) {
      auto info = regions[i];
      num_verts += (info.end_vertex_index_ - info.start_vertex_index_ + 2) *
                   kUnderlineVerticesPerGlyph;
      num_verts += kExtraUnderlineVerticesPerInfo;
    }
  }
  return num_verts;
}

std::vector<vec3_packed> GenerateUnderlineVertices(const FontBuffer &buffer,
                                                   const mathfu::vec2 &pos) {
  std::vector<vec3_packed> vec(NumUnderlineVertices(buffer));
  auto &slices = buffer.get_slices();
  auto &vertices = buffer.get_vertices();
  bool degenerated_triangle = false;
  vec.clear();
  for (size_t i = 0; i < slices.size(); ++i) {
    if (slices.at(i).get_underline()) {
      // Generate underline strips.
      const int32_t kVerticesPerGlyph = 4;
      auto regions = slices.at(i).get_underline_info();
      for (size_t i = 0; i < regions.size(); ++i) {
        auto info = regions[i];
        auto y_start = info.y_pos_.x() + pos.y();
        auto y_end = y_start + info.y_pos_.y();

        if (degenerated_triangle) {
          // Add degenerated triangle to connect multiple strips.
          auto start_pos = vec3(vertices.at(info.start_vertex_index_ *
                                            kVerticesPerGlyph).position_);
          vec.push_back(vec.back());
          vec.push_back(
              vec3_packed(vec3(start_pos.x() + pos.x(), y_start, 0.f)));
        }

        // Add vertices.
        for (auto idx = info.start_vertex_index_; idx <= info.end_vertex_index_;
             ++idx) {
          auto strip_pos = vec3(vertices.at(idx * kVerticesPerGlyph).position_);
          vec.push_back(
              vec3_packed(vec3(strip_pos.x() + pos.x(), y_start, 0.f)));
          vec.push_back(vec3_packed(vec3(strip_pos.x() + pos.x(), y_end, 0.f)));
        }

        // Add last 2 vertices.
        auto end_pos =
            vec3(vertices.at(info.end_vertex_index_ * kVerticesPerGlyph +
                             kVerticesPerGlyph - 1).position_);
        vec.push_back(vec3_packed(vec3(end_pos.x() + pos.x(), y_start, 0.f)));
        vec.push_back(vec3_packed(vec3(end_pos.x() + pos.x(), y_end, 0.f)));

        degenerated_triangle = true;
      }
    }
  }
  assert(NumUnderlineVertices(buffer) == vec.size());
  return vec;
}

}  // namespace flatui
<commit_msg>Fix SIGABRT in GenerateUnderlineVertices when the buffer has no underline.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "precompiled.h"

#include <cctype>
#include <sstream>

#include <gumbo.h>

#include "font_manager.h"
#include "font_util.h"
#include "fplbase/fpl_common.h"

namespace flatui {

using mathfu::vec3;
using mathfu::vec3_packed;

// Replace whitespace with a single space.
// This emulates how HTML processes raw text fields.
// Appends trimmed text to `out`.
// Returns reference to `out`.
std::string &TrimHtmlWhitespace(const char *text, bool trim_leading_whitespace,
                                std::string *out) {
  const char *t = text;

  if (trim_leading_whitespace) {
    while (std::isspace(*t)) ++t;
  }

  for (;;) {
    // Output non-whitespace.
    while (!std::isspace(*t) && *t != '\0') out->push_back(*t++);

    // Break out if at end of input. Note that we do *not* trim all
    // trailing whitespace.
    if (*t == '\0') break;

    // Skip whitespace.
    while (std::isspace(*t)) ++t;

    // Insert single space to compensate for trimmed whitespace.
    out->push_back(' ');
  }
  return *out;
}

// Remove trailing whitespace. Then add a `prefix` if there is any preceeding
// text.
static std::string &StartHtmlLine(const char *prefix, std::string *out) {
  // Trim trailing whitespace.
  while (std::isspace(out->back())) out->pop_back();

  // Append the new lines in `prefix`.
  if (!out->empty()) {
    out->append(prefix);
  }
  return *out;
}

// The very first text output we want to trim the leading whitespace.
// We should also trim if the previous text ends in whitespace.
static bool ShouldTrimLeadingWhitespace(const std::vector<HtmlSection> &s) {
  if (s.size() <= 1) return true;

  const std::string &prev_text =
      s.back().text.empty() ? s[s.size() - 2].text : s.back().text;
  return std::isspace(prev_text.back());
}

static void GumboTreeToHtmlSections(const GumboNode *node,
                                    std::vector<HtmlSection> *s) {
  switch (node->type) {
    // Process non-text elements, possibly recursing into child nodes.
    case GUMBO_NODE_ELEMENT: {
      const GumboElement &element = node->v.element;

      // Tree prefix processing.
      switch (element.tag) {
        case GUMBO_TAG_A:
          // Start a new section for the anchor.
          if (!s->back().text.empty()) {
            s->push_back(HtmlSection());
          }
          break;

        case GUMBO_TAG_P:
        case GUMBO_TAG_H1:  // fallthrough
        case GUMBO_TAG_H2:  // fallthrough
        case GUMBO_TAG_H3:  // fallthrough
        case GUMBO_TAG_H4:  // fallthrough
        case GUMBO_TAG_H5:  // fallthrough
        case GUMBO_TAG_H6:  // fallthrough
          StartHtmlLine("\n\n", &s->back().text);
          break;

        default:
          break;
      }
      const size_t node_section = s->size() - 1;

      // Tree children processing via recursion.
      for (unsigned int i = 0; i < element.children.length; ++i) {
        const GumboNode *child =
            static_cast<GumboNode *>(element.children.data[i]);
        GumboTreeToHtmlSections(child, s);
      }

      // Tree postfix processing.
      switch (element.tag) {
        case GUMBO_TAG_A: {
          // Record the link address.
          GumboAttribute *href =
              gumbo_get_attribute(&element.attributes, "href");
          if (href != nullptr) {
            (*s)[node_section].link = href->value;
          }

          // Start a new section for the non-anchor.
          s->push_back(HtmlSection());
          break;
        }

        case GUMBO_TAG_HR:
        case GUMBO_TAG_P:  // fallthrough
          s->back().text.append("\n\n");
          break;

        case GUMBO_TAG_H1:  // fallthrough
        case GUMBO_TAG_H2:  // fallthrough
        case GUMBO_TAG_H3:  // fallthrough
        case GUMBO_TAG_H4:  // fallthrough
        case GUMBO_TAG_H5:  // fallthrough
        case GUMBO_TAG_H6:  // fallthrough
        case GUMBO_TAG_BR:
          s->back().text.append("\n");
          break;

        default:
          break;
      }
      break;
    }

    // Append text without excessive whitespaces.
    case GUMBO_NODE_TEXT:
      TrimHtmlWhitespace(node->v.text.text, ShouldTrimLeadingWhitespace(*s),
                         &s->back().text);
      break;

    // Ignore other node types.
    default:
      break;
  }
}

std::vector<HtmlSection> ParseHtml(const char *html) {
  // Ensure there is an HtmlSection that can be appended to.
  std::vector<HtmlSection> s(1);

  // Parse html into tree, with Gumbo, then process the tree.
  GumboOutput *gumbo = gumbo_parse(html);
  GumboTreeToHtmlSections(gumbo->root, &s);
  gumbo_destroy_output(&kGumboDefaultOptions, gumbo);

  // Prune empty last section.
  if (s.back().text.empty()) {
    s.pop_back();
  }
  return s;
}

static size_t NumUnderlineVertices(const FontBuffer &buffer) {
  const int32_t kUnderlineVerticesPerGlyph = 2;
  const int32_t kExtraUnderlineVerticesPerInfo = 2;
  size_t num_verts = 0;
  auto &slices = buffer.get_slices();
  for (size_t i = 0; i < slices.size(); ++i) {
    auto regions = slices.at(i).get_underline_info();
    for (size_t i = 0; i < regions.size(); ++i) {
      auto info = regions[i];
      num_verts += (info.end_vertex_index_ - info.start_vertex_index_ + 2) *
                   kUnderlineVerticesPerGlyph;
      num_verts += kExtraUnderlineVerticesPerInfo;
    }
  }
  if (num_verts) {
    // Subtract the amount of the first strip which doesn't need a triangle to
    // stitch.
    num_verts -= kExtraUnderlineVerticesPerInfo;
  }
  return num_verts;
}

std::vector<vec3_packed> GenerateUnderlineVertices(const FontBuffer &buffer,
                                                   const mathfu::vec2 &pos) {
  std::vector<vec3_packed> vec;
  auto num_verts = NumUnderlineVertices(buffer);
  if (!num_verts) {
    return vec;
  }
  vec.reserve(num_verts);
  auto &slices = buffer.get_slices();
  auto &vertices = buffer.get_vertices();
  bool degenerated_triangle = false;
  for (size_t i = 0; i < slices.size(); ++i) {
    if (slices.at(i).get_underline()) {
      // Generate underline strips.
      const int32_t kVerticesPerGlyph = 4;
      auto regions = slices.at(i).get_underline_info();
      for (size_t i = 0; i < regions.size(); ++i) {
        auto info = regions[i];
        auto y_start = info.y_pos_.x() + pos.y();
        auto y_end = y_start + info.y_pos_.y();

        if (degenerated_triangle) {
          // Add degenerated triangle to connect multiple strips.
          auto start_pos = vec3(vertices.at(info.start_vertex_index_ *
                                            kVerticesPerGlyph).position_);
          vec.push_back(vec.back());
          vec.push_back(
              vec3_packed(vec3(start_pos.x() + pos.x(), y_start, 0.f)));
        }

        // Add vertices.
        for (auto idx = info.start_vertex_index_; idx <= info.end_vertex_index_;
             ++idx) {
          auto strip_pos = vec3(vertices.at(idx * kVerticesPerGlyph).position_);
          vec.push_back(
              vec3_packed(vec3(strip_pos.x() + pos.x(), y_start, 0.f)));
          vec.push_back(vec3_packed(vec3(strip_pos.x() + pos.x(), y_end, 0.f)));
        }

        // Add last 2 vertices.
        auto end_pos =
            vec3(vertices.at(info.end_vertex_index_ * kVerticesPerGlyph +
                             kVerticesPerGlyph - 1).position_);
        vec.push_back(vec3_packed(vec3(end_pos.x() + pos.x(), y_start, 0.f)));
        vec.push_back(vec3_packed(vec3(end_pos.x() + pos.x(), y_end, 0.f)));

        degenerated_triangle = true;
      }
    }
  }
  assert(num_verts == vec.size());
  return vec;
}

}  // namespace flatui
<|endoftext|>


<commit_before>/*
 *  Game.cpp
 *  OpenLieroX
 *
 *  Created by Albert Zeyer on 09.12.09.
 *  code under LGPL
 *
 */

#include "Game.h"
#include "AuxLib.h"
#include "Debug.h"
#include "LieroX.h"
#include "GfxPrimitives.h"
#include "Entity.h"
#include "EventQueue.h"
#include "InputEvents.h"
#include "DedicatedControl.h"
#include "CrashHandler.h"
#include "Timer.h"
#include "NewNetEngine.h"
#include "OLXCommand.h"
#include "IRC.h"
#include "CClient.h"
#include "CServer.h"
#include "Physics.h"
#include "DeprecatedGUI/Menu.h"
#include "Cache.h"
#include "gusanos/gusanos.h"
#include "gusanos/gusgame.h"
#include "gusanos/ninjarope.h"
#include "game/WormInputHandler.h"
#include "CWormHuman.h"
#include "gusanos/glua.h"
#include "gusanos/lua51/luaapi/context.h"

Game game;

static bool inMainGameLoop = false;
static std::string quitEngineFlagReason;

void Game::prepareGameloop() {
	// Pre-game initialization
	if(!bDedicated) FillSurface(VideoPostProcessor::videoSurface(), tLX->clBlack);

	while(cClient->getStatus() != NET_CONNECTED) {
		notes << "client not connected yet - waiting" << endl;
		SDL_Delay(10);
		SyncServerAndClient();
	}

	if(cServer->getState() == SVS_LOBBY) {
		notes << "prepareGameloop: starting game" << endl;
		std::string errMsg;
		if(!cServer->StartGame(&errMsg)) {
			errors << "starting game in local game failed for reason: " << errMsg << endl;
			DeprecatedGUI::Menu_MessageBox("Error", "Error while starting game: " + errMsg);
			GotoLocalMenu();
			return;
		}
	}
	else
		warnings << "prepareGameloop: server was not in lobby" << endl;

	// we need the gamescript in physics init
	while(gameScript() == NULL) {
		notes << "gamescript not loaded yet - waiting" << endl;
		SDL_Delay(10);
		SyncServerAndClient();
	}

	if(gameScript()->gusEngineUsed() && !gusGame.level().gusIsLoaded()) {
		// WARNING: This may be temporary
		// Right now, we load the gus mod in the map loader (gusGame.changeLevel).
		// Thus, when we don't load a gus level, we must load the mod manually.

#ifdef USE_GRID
		game.objects.resize(0, 0, gusGame.level().GetWidth(), gusGame.level().GetHeight());
#endif

		gusGame.refreshResources( gameScript()->directory() );
		gusGame.loadMod();
		gusGame.runInitScripts();
	}

	PhysicsEngine::Init();

	ClearEntities();

	ProcessEvents();
	notes << "MaxFPS is " << tLXOptions->nMaxFPS << endl;

	//cCache.ClearExtraEntries(); // Do not clear anything before game started, it may be slow

	notes << "GameLoopStart" << endl;
	inMainGameLoop = true;
	if( DedicatedControl::Get() )
		DedicatedControl::Get()->GameLoopStart_Signal();

	CrashHandler::recoverAfterCrash = tLXOptions->bRecoverAfterCrash && GetGameVersion().releasetype == Version::RT_NORMAL;

	ResetQuitEngineFlag();
	oldtime = GetTime();
}

void Game::frameOuter() {
	tLX->currentTime = GetTime();
	SetCrashHandlerReturnPoint("main game loop");

	// Timing
	tLX->fDeltaTime = tLX->currentTime - oldtime;
	tLX->fRealDeltaTime = tLX->fDeltaTime;
	oldtime = tLX->currentTime;

	// cap the delta
	if(tLX->fDeltaTime.seconds() > 0.5f) {
		warnings << "deltatime " << tLX->fDeltaTime.seconds() << " is too high" << endl;
		// only if not in new net mode because it would screw up the gamestate there
		if(!NewNet::Active())
			tLX->fDeltaTime = 0.5f; // don't simulate more than 500ms, it could crash the game
	}

	ProcessEvents();

	// Main frame
	frameInner();

	doVideoFrameInMainThread();
	CapFPS();
}

///////////////////
// Game loop
void Game::frameInner()
{
	HandlePendingCommands();

	if(bDedicated)
		DedicatedControl::Get()->GameLoop_Frame();

    if(tLX->bQuitEngine)
        return;

	// Check if user pressed screenshot key
	if (tLX->cTakeScreenshot.isDownOnce())  {
		PushScreenshot("scrshots", "");
	}

	// Switch between window and fullscreen mode
	// Switch only if delta time is low enough. This is because when the game does not
	// respond for >30secs and the user presses cSwitchMode in the meantime, the mainlock-detector
	// would switch to window and here we would switch again to fullscreen which is stupid.
	if( tLX->cSwitchMode.isUp() && tLX && tLX->fRealDeltaTime < 1.0f )  {
		// Set to fullscreen
		tLXOptions->bFullscreen = !tLXOptions->bFullscreen;

		// Set the new video mode
		doSetVideoModeInMainThread();

		tLX->cSwitchMode.reset();
	}

#ifdef WITH_G15
	if (OLXG15)
		OLXG15->gameFrame();
#endif //WITH_G15

	if(tLXOptions->bEnableChat)
		ProcessIRC();

	gusLogicFrame();

	// Local
	switch (tLX->iGameType)  {
		case GME_LOCAL:
			cClient->Frame();
			cServer->Frame();

			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

			// Hosting
		case GME_HOST:
			cClient->Frame();
			cServer->Frame();

			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

			// Joined
		case GME_JOIN:
			cClient->Frame();
			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

	} // SWITCH

	cClient->resetDebugStr();

	EnableSystemMouseCursor(false);
}

void Game::cleanupAfterGameloopEnd() {
	CrashHandler::recoverAfterCrash = false;

	gusGame.reset(GusGame::ServerQuit);

	PhysicsEngine::UnInit();

	notes << "GameLoopEnd: " << quitEngineFlagReason << endl;
	inMainGameLoop = false;
	if( DedicatedControl::Get() )
		DedicatedControl::Get()->GameLoopEnd_Signal();

	cCache.ClearExtraEntries(); // Game ended - clear cache
}

void ResetQuitEngineFlag() {
	tLX->bQuitEngine = false;
}

void SetQuitEngineFlag(const std::string& reason) {
	Warning_QuitEngineFlagSet("SetQuitEngineFlag(" + reason + "): ");
	quitEngineFlagReason = reason;
	tLX->bQuitEngine = true;
	// If we call this from within the menu, the menu should shutdown.
	// It will be restarted then in the next frame.
	// If we are not in the menu (i.e. in maingameloop), this has no
	// effect as we set it to true in Menu_Start().
	if(DeprecatedGUI::tMenu)
		DeprecatedGUI::tMenu->bMenuRunning = false;
	// If we were in menu, because we forced the menu restart above,
	// we must set this, otherwise OLX would quit (because of current maingamelogic).
	if(DeprecatedGUI::bGame)
		*DeprecatedGUI::bGame = true;
}

bool Warning_QuitEngineFlagSet(const std::string& preText) {
	if(tLX->bQuitEngine) {
		hints << preText << endl;
		warnings << "bQuitEngine is set because: " << quitEngineFlagReason << endl;
		return true;
	}
	return false;
}

void Game::onNewWorm(CWorm* w) {
	if(!game.gameScript()->gusEngineUsed()) return;

#ifdef USE_GRID
	objects.insertImmediately(w, Grid::WormColLayer, Grid::WormRenderLayer);
	if(w->getNinjaRopeObj()) objects.insertImmediately(w->getNinjaRopeObj(), 1, 1);
#else
	objects.insert(WORMS_COLLISION_LAYER,WORMS_RENDER_LAYER, w);
	if(w->getNinjaRopeObj()) objects.insert( 1,1, (CGameObject*)w->getNinjaRopeObj() );
#endif
}

void Game::onRemoveWorm(CWorm* w) {
/*#ifdef USE_GRID
	objects.(w, Grid::WormColLayer, Grid::WormRenderLayer);
	if(w->getNinjaRopeObj()) objects.insertImmediately(w->getNinjaRopeObj(), 1, 1);
#else
	objects.insert(WORMS_COLLISION_LAYER,WORMS_RENDER_LAYER, w);
	if(w->getNinjaRopeObj()) objects.insert( 1,1, (CGameObject*)w->getNinjaRopeObj() );
#endif	*/
}

void Game::onNewPlayer(CWormInputHandler* player) {
	players.push_back( player );
}

void Game::onNewPlayer_Lua(CWormInputHandler* p) {
	if(game.gameScript()->gusEngineUsed()) {
		EACH_CALLBACK(i, playerInit)
		{
			(lua.call(*i), p->getLuaReference())();
		}
	}
}

void Game::onNewHumanPlayer(CWormHumanInputHandler* player) {
	localPlayers.push_back( player );
	player->local = true;
}

void Game::onNewHumanPlayer_Lua(CWormHumanInputHandler* player) {
	if(game.gameScript()->gusEngineUsed()) {
		EACH_CALLBACK(i, localplayerInit)
		{
			(lua.call(*i), player->getLuaReference())();
		}
	}
}

void Game::reset() {
	// Delete all players
	for ( std::list<CWormInputHandler*>::iterator iter = game.players.begin(); iter != game.players.end(); ++iter)
	{
		// handled by CClient for now
		//(*iter)->deleteThis();
	}
	game.players.clear();
	game.localPlayers.clear();

	// Delete all objects
#ifdef USE_GRID
	game.objects.clear();
#else
	for ( ObjectsList::Iterator iter = game.objects.begin(); (bool)iter; ++iter)
	{
		(*iter)->deleteThis();
	}
	objects.clear();
#endif
}

CGameScript* Game::gameScript() {
	if(tLX) {
		if(tLX->iGameType == GME_JOIN) return cClient->getGameScript().get();
		return cServer->getGameScript().get();
	}
	return NULL;
}
<commit_msg>small security check (for now)<commit_after>/*
 *  Game.cpp
 *  OpenLieroX
 *
 *  Created by Albert Zeyer on 09.12.09.
 *  code under LGPL
 *
 */

#include "Game.h"
#include "AuxLib.h"
#include "Debug.h"
#include "LieroX.h"
#include "GfxPrimitives.h"
#include "Entity.h"
#include "EventQueue.h"
#include "InputEvents.h"
#include "DedicatedControl.h"
#include "CrashHandler.h"
#include "Timer.h"
#include "NewNetEngine.h"
#include "OLXCommand.h"
#include "IRC.h"
#include "CClient.h"
#include "CServer.h"
#include "Physics.h"
#include "DeprecatedGUI/Menu.h"
#include "Cache.h"
#include "gusanos/gusanos.h"
#include "gusanos/gusgame.h"
#include "gusanos/ninjarope.h"
#include "game/WormInputHandler.h"
#include "CWormHuman.h"
#include "gusanos/glua.h"
#include "gusanos/lua51/luaapi/context.h"

Game game;

static bool inMainGameLoop = false;
static std::string quitEngineFlagReason;

void Game::prepareGameloop() {
	// Pre-game initialization
	if(!bDedicated) FillSurface(VideoPostProcessor::videoSurface(), tLX->clBlack);

	while(cClient->getStatus() != NET_CONNECTED) {
		notes << "client not connected yet - waiting" << endl;
		SDL_Delay(10);
		SyncServerAndClient();
	}

	if(cServer->getState() == SVS_LOBBY) {
		notes << "prepareGameloop: starting game" << endl;
		std::string errMsg;
		if(!cServer->StartGame(&errMsg)) {
			errors << "starting game in local game failed for reason: " << errMsg << endl;
			DeprecatedGUI::Menu_MessageBox("Error", "Error while starting game: " + errMsg);
			GotoLocalMenu();
			return;
		}
	}
	else
		warnings << "prepareGameloop: server was not in lobby" << endl;

	// we need the gamescript in physics init
	while(gameScript() == NULL) {
		notes << "gamescript not loaded yet - waiting" << endl;
		SDL_Delay(10);
		SyncServerAndClient();
	}

	if(gameScript()->gusEngineUsed() && !gusGame.level().gusIsLoaded()) {
		// WARNING: This may be temporary
		// Right now, we load the gus mod in the map loader (gusGame.changeLevel).
		// Thus, when we don't load a gus level, we must load the mod manually.

#ifdef USE_GRID
		game.objects.resize(0, 0, gusGame.level().GetWidth(), gusGame.level().GetHeight());
#endif

		gusGame.refreshResources( gameScript()->directory() );
		gusGame.loadMod();
		gusGame.runInitScripts();
	}

	PhysicsEngine::Init();

	ClearEntities();

	ProcessEvents();
	notes << "MaxFPS is " << tLXOptions->nMaxFPS << endl;

	//cCache.ClearExtraEntries(); // Do not clear anything before game started, it may be slow

	notes << "GameLoopStart" << endl;
	inMainGameLoop = true;
	if( DedicatedControl::Get() )
		DedicatedControl::Get()->GameLoopStart_Signal();

	CrashHandler::recoverAfterCrash = tLXOptions->bRecoverAfterCrash && GetGameVersion().releasetype == Version::RT_NORMAL;

	ResetQuitEngineFlag();
	oldtime = GetTime();
}

void Game::frameOuter() {
	tLX->currentTime = GetTime();
	SetCrashHandlerReturnPoint("main game loop");

	// Timing
	tLX->fDeltaTime = tLX->currentTime - oldtime;
	tLX->fRealDeltaTime = tLX->fDeltaTime;
	oldtime = tLX->currentTime;

	// cap the delta
	if(tLX->fDeltaTime.seconds() > 0.5f) {
		warnings << "deltatime " << tLX->fDeltaTime.seconds() << " is too high" << endl;
		// only if not in new net mode because it would screw up the gamestate there
		if(!NewNet::Active())
			tLX->fDeltaTime = 0.5f; // don't simulate more than 500ms, it could crash the game
	}

	ProcessEvents();

	// Main frame
	frameInner();

	doVideoFrameInMainThread();
	CapFPS();
}

///////////////////
// Game loop
void Game::frameInner()
{
	HandlePendingCommands();

	if(bDedicated)
		DedicatedControl::Get()->GameLoop_Frame();

    if(tLX->bQuitEngine)
        return;

	// Check if user pressed screenshot key
	if (tLX->cTakeScreenshot.isDownOnce())  {
		PushScreenshot("scrshots", "");
	}

	// Switch between window and fullscreen mode
	// Switch only if delta time is low enough. This is because when the game does not
	// respond for >30secs and the user presses cSwitchMode in the meantime, the mainlock-detector
	// would switch to window and here we would switch again to fullscreen which is stupid.
	if( tLX->cSwitchMode.isUp() && tLX && tLX->fRealDeltaTime < 1.0f )  {
		// Set to fullscreen
		tLXOptions->bFullscreen = !tLXOptions->bFullscreen;

		// Set the new video mode
		doSetVideoModeInMainThread();

		tLX->cSwitchMode.reset();
	}

#ifdef WITH_G15
	if (OLXG15)
		OLXG15->gameFrame();
#endif //WITH_G15

	if(tLXOptions->bEnableChat)
		ProcessIRC();

	if(gameScript()->gusEngineUsed() || gusGame.level().gusIsLoaded())
		gusLogicFrame();

	// Local
	switch (tLX->iGameType)  {
		case GME_LOCAL:
			cClient->Frame();
			cServer->Frame();

			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

			// Hosting
		case GME_HOST:
			cClient->Frame();
			cServer->Frame();

			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

			// Joined
		case GME_JOIN:
			cClient->Frame();
			if(tLX && !tLX->bQuitEngine)
				cClient->Draw(VideoPostProcessor::videoSurface());
			break;

	} // SWITCH

	cClient->resetDebugStr();

	EnableSystemMouseCursor(false);
}

void Game::cleanupAfterGameloopEnd() {
	CrashHandler::recoverAfterCrash = false;

	gusGame.reset(GusGame::ServerQuit);

	PhysicsEngine::UnInit();

	notes << "GameLoopEnd: " << quitEngineFlagReason << endl;
	inMainGameLoop = false;
	if( DedicatedControl::Get() )
		DedicatedControl::Get()->GameLoopEnd_Signal();

	cCache.ClearExtraEntries(); // Game ended - clear cache
}

void ResetQuitEngineFlag() {
	tLX->bQuitEngine = false;
}

void SetQuitEngineFlag(const std::string& reason) {
	Warning_QuitEngineFlagSet("SetQuitEngineFlag(" + reason + "): ");
	quitEngineFlagReason = reason;
	tLX->bQuitEngine = true;
	// If we call this from within the menu, the menu should shutdown.
	// It will be restarted then in the next frame.
	// If we are not in the menu (i.e. in maingameloop), this has no
	// effect as we set it to true in Menu_Start().
	if(DeprecatedGUI::tMenu)
		DeprecatedGUI::tMenu->bMenuRunning = false;
	// If we were in menu, because we forced the menu restart above,
	// we must set this, otherwise OLX would quit (because of current maingamelogic).
	if(DeprecatedGUI::bGame)
		*DeprecatedGUI::bGame = true;
}

bool Warning_QuitEngineFlagSet(const std::string& preText) {
	if(tLX->bQuitEngine) {
		hints << preText << endl;
		warnings << "bQuitEngine is set because: " << quitEngineFlagReason << endl;
		return true;
	}
	return false;
}

void Game::onNewWorm(CWorm* w) {
	if(!game.gameScript()->gusEngineUsed()) return;

#ifdef USE_GRID
	objects.insertImmediately(w, Grid::WormColLayer, Grid::WormRenderLayer);
	if(w->getNinjaRopeObj()) objects.insertImmediately(w->getNinjaRopeObj(), 1, 1);
#else
	objects.insert(WORMS_COLLISION_LAYER,WORMS_RENDER_LAYER, w);
	if(w->getNinjaRopeObj()) objects.insert( 1,1, (CGameObject*)w->getNinjaRopeObj() );
#endif
}

void Game::onRemoveWorm(CWorm* w) {
/*#ifdef USE_GRID
	objects.(w, Grid::WormColLayer, Grid::WormRenderLayer);
	if(w->getNinjaRopeObj()) objects.insertImmediately(w->getNinjaRopeObj(), 1, 1);
#else
	objects.insert(WORMS_COLLISION_LAYER,WORMS_RENDER_LAYER, w);
	if(w->getNinjaRopeObj()) objects.insert( 1,1, (CGameObject*)w->getNinjaRopeObj() );
#endif	*/
}

void Game::onNewPlayer(CWormInputHandler* player) {
	players.push_back( player );
}

void Game::onNewPlayer_Lua(CWormInputHandler* p) {
	if(game.gameScript()->gusEngineUsed()) {
		EACH_CALLBACK(i, playerInit)
		{
			(lua.call(*i), p->getLuaReference())();
		}
	}
}

void Game::onNewHumanPlayer(CWormHumanInputHandler* player) {
	localPlayers.push_back( player );
	player->local = true;
}

void Game::onNewHumanPlayer_Lua(CWormHumanInputHandler* player) {
	if(game.gameScript()->gusEngineUsed()) {
		EACH_CALLBACK(i, localplayerInit)
		{
			(lua.call(*i), player->getLuaReference())();
		}
	}
}

void Game::reset() {
	// Delete all players
	for ( std::list<CWormInputHandler*>::iterator iter = game.players.begin(); iter != game.players.end(); ++iter)
	{
		// handled by CClient for now
		//(*iter)->deleteThis();
	}
	game.players.clear();
	game.localPlayers.clear();

	// Delete all objects
#ifdef USE_GRID
	game.objects.clear();
#else
	for ( ObjectsList::Iterator iter = game.objects.begin(); (bool)iter; ++iter)
	{
		(*iter)->deleteThis();
	}
	objects.clear();
#endif
}

CGameScript* Game::gameScript() {
	if(tLX) {
		if(tLX->iGameType == GME_JOIN) return cClient->getGameScript().get();
		return cServer->getGameScript().get();
	}
	return NULL;
}
<|endoftext|>


<commit_before>#include "Screen.hpp"

#include <algorithm>

#include "gl/util.hpp"

namespace nt {
namespace gl {

void Screen::render(const Box2<int>& screenArea) const
{
    glEnable(GL_SCISSOR_TEST);
    glScissor(screenArea);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    for (std::vector<ViewportEntry>::const_iterator i = viewports.begin(); i != viewports.end(); ++i) {
        Box2<int> viewportArea;

        viewportArea.setLeft(
            static_cast<int>(screenArea.left() * i->extent.left())
        );
        viewportArea.setRight(
            static_cast<int>(screenArea.right() * i->extent.right())
        );
        viewportArea.setTop(
            static_cast<int>(screenArea.top() * i->extent.top())
        );
        viewportArea.setBottom(
            static_cast<int>(screenArea.bottom() * i->extent.bottom())
        );

        i->viewport->render(viewportArea);
    }
}

void Screen::addViewport(const Viewport* const viewport)
{
    addViewport(viewport, Box2<double>(0, 1, 0, 1));
}

void Screen::addViewport(const Viewport* const viewport, const Box2<double>& extent)
{
    ViewportEntry entry;
    entry.viewport = viewport;
    entry.extent = extent;
    viewports.push_back(entry);
}

void Screen::removeViewport(const Viewport* const viewport)
{
    viewports.erase(
        std::remove_if(begin(viewports), end(viewports),
            [&](ViewportEntry& entry) { return entry.viewport == viewport; }
        ),
        end(viewports)
    );
}

} // namespace gl
} // namespace nt
<commit_msg>Screen: Fixed viewport area<commit_after>#include "Screen.hpp"

#include <algorithm>

#include "gl/util.hpp"

namespace nt {
namespace gl {

void Screen::render(const Box2<int>& screenArea) const
{
    glEnable(GL_SCISSOR_TEST);
    glScissor(screenArea);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    for (std::vector<ViewportEntry>::const_iterator i = viewports.begin(); i != viewports.end(); ++i) {
        Box2<int> viewportArea;

        viewportArea.setLeft(
            static_cast<int>(screenArea.width() * i->extent.left())
        );
        viewportArea.setRight(
            static_cast<int>(screenArea.width() * i->extent.right())
        );
        viewportArea.setTop(
            static_cast<int>(screenArea.height() * i->extent.top())
        );
        viewportArea.setBottom(
            static_cast<int>(screenArea.height() * i->extent.bottom())
        );

        i->viewport->render(viewportArea);
    }
}

void Screen::addViewport(const Viewport* const viewport)
{
    addViewport(viewport, Box2<double>(0, 1, 0, 1));
}

void Screen::addViewport(const Viewport* const viewport, const Box2<double>& extent)
{
    ViewportEntry entry;
    entry.viewport = viewport;
    entry.extent = extent;
    viewports.push_back(entry);
}

void Screen::removeViewport(const Viewport* const viewport)
{
    viewports.erase(
        std::remove_if(begin(viewports), end(viewports),
            [&](ViewportEntry& entry) { return entry.viewport == viewport; }
        ),
        end(viewports)
    );
}

} // namespace gl
} // namespace nt
<|endoftext|>


<commit_before>///
/// @file  A.cpp
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <gourdon.hpp>
#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <fast_div.hpp>
#include <generate.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <imath.hpp>
#include <print.hpp>
#include <S2Status.hpp>
#include <S2.hpp>

#include <stdint.h>
#include <vector>

using namespace std;
using namespace primecount;

namespace {

template <typename T, typename Primes>
T A_OpenMP(T x,
           int64_t y,
           int64_t start,
           Primes& primes,
           int threads)
{
  T sum = 0;
  int64_t x13 = iroot<3>(x);
  int64_t thread_threshold = 1000;
  threads = ideal_num_threads(threads, x13, thread_threshold);

  PiTable pi(isqrt(x));
  int64_t pi_x13 = pi[x13];
  S2Status status(x);

  #pragma omp parallel for schedule(dynamic) num_threads(threads) reduction(+: sum)
  for (int64_t b = pi[start] + 1; b <= pi_x13; b++)
  {
    int64_t prime = primes[b];
    T x2 = x / prime;
    int64_t j = b + 1;
    int64_t max_j = pi[isqrt(x2)];

    for (; j <= max_j; j++)
    {
      int64_t xn = fast_div64(x2, primes[j]);
      if (xn < y)
        sum += pi[xn] * 2;
      else
        sum += pi[xn];
    }

    if (is_print())
      status.print(b, pi_x13);
  }

  return sum;
}

} // namespace

namespace primecount {

int64_t A(int64_t x,
          int64_t y,
          int threads)
{
  print("");
  print("=== A(x, y) ===");
  print(x, y, threads);

  double time = get_time();
  int64_t y2 = y * y;
  int64_t start = max(iroot<4>(x), x / y2);
  int64_t max_prime = (int64_t) isqrt(x / start);

  auto primes = generate_primes<int32_t>(max_prime);
  int64_t a = A_OpenMP((intfast64_t) x, y, start, primes, threads);

  print("A", a, time);
  return a;
}

#ifdef HAVE_INT128_T

int128_t A(int128_t x,
           int64_t y,
           int threads)
{
  print("");
  print("=== A(x, y) ===");
  print(x, y, threads);

  double time = get_time();
  int128_t a;

  int128_t y2 = (int128_t) y * y;
  int128_t start = max(iroot<4>(x), x / y2);
  int64_t max_prime = (int64_t) isqrt(x / start);

  // uses less memory
  if (max_prime <= numeric_limits<uint32_t>::max())
  {
    auto primes = generate_primes<uint32_t>(max_prime);
    a = A_OpenMP((intfast128_t) x, y, start, primes, threads);
  }
  else
  {
    auto primes = generate_primes<int64_t>(max_prime);
    a = A_OpenMP((intfast128_t) x, y, start, primes, threads);
  }

  print("A", a, time);
  return a;
}

#endif

} // namespace
<commit_msg>Minor speedup<commit_after>///
/// @file  A.cpp
/// @brief Simple demonstration implementation of the A(x, y) formula
///        in Xavier Gourdon's prime counting algorithm. This
///        implementation uses O(x^(1/2)) memory instead of O(x^(1/3))
///        in order to simplify the implementation.
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <gourdon.hpp>
#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <fast_div.hpp>
#include <generate.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <imath.hpp>
#include <print.hpp>
#include <S2Status.hpp>

#include <stdint.h>
#include <vector>

using namespace std;
using namespace primecount;

namespace {

template <typename T, typename Primes>
T A_OpenMP(T x,
           int64_t y,
           int64_t start,
           Primes& primes,
           int threads)
{
  T sum = 0;
  int64_t x13 = iroot<3>(x);
  int64_t thread_threshold = 1000;
  threads = ideal_num_threads(threads, x13, thread_threshold);

  PiTable pi(isqrt(x));
  int64_t pi_x13 = pi[x13];
  S2Status status(x);

  #pragma omp parallel for schedule(dynamic) num_threads(threads) reduction(+: sum)
  for (int64_t b = pi[start] + 1; b <= pi_x13; b++)
  {
    int64_t prime = primes[b];
    T x2 = x / prime;
    int64_t j = b + 1;
    int64_t max_j = pi[isqrt(x2)];

    // x / (p * q) >= y
    for (; j <= max_j; j++)
    {
      int64_t xn = fast_div64(x2, primes[j]);
      if (xn < y)
        break;
      sum += pi[xn];
    }

    // x / (p * q) < y
    for (; j <= max_j; j++)
    {
      int64_t xn = fast_div64(x2, primes[j]);
      sum += pi[xn] * 2;
    }

    if (is_print())
      status.print(b, pi_x13);
  }

  return sum;
}

} // namespace

namespace primecount {

int64_t A(int64_t x,
          int64_t y,
          int threads)
{
  print("");
  print("=== A(x, y) ===");
  print(x, y, threads);

  double time = get_time();
  int64_t y2 = y * y;
  int64_t start = max(iroot<4>(x), x / y2);
  int64_t max_prime = (int64_t) isqrt(x / start);

  auto primes = generate_primes<int32_t>(max_prime);
  int64_t a = A_OpenMP((intfast64_t) x, y, start, primes, threads);

  print("A", a, time);
  return a;
}

#ifdef HAVE_INT128_T

int128_t A(int128_t x,
           int64_t y,
           int threads)
{
  print("");
  print("=== A(x, y) ===");
  print(x, y, threads);

  double time = get_time();
  int128_t a;

  int128_t y2 = (int128_t) y * y;
  int128_t start = max(iroot<4>(x), x / y2);
  int64_t max_prime = (int64_t) isqrt(x / start);

  // uses less memory
  if (max_prime <= numeric_limits<uint32_t>::max())
  {
    auto primes = generate_primes<uint32_t>(max_prime);
    a = A_OpenMP((intfast128_t) x, y, start, primes, threads);
  }
  else
  {
    auto primes = generate_primes<int64_t>(max_prime);
    a = A_OpenMP((intfast128_t) x, y, start, primes, threads);
  }

  print("A", a, time);
  return a;
}

#endif

} // namespace
<|endoftext|>


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

#ifndef __HDFS_HPP__
#define __HDFS_HPP__

#include <sstream>
#include <vector>

#include <stout/check.hpp>
#include <stout/error.hpp>
#include <stout/nothing.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/strings.hpp>

// TODO(benh): We should get the hostname:port (or ip:port) of the
// server via:
//
//  hadoop dfsadmin -report | grep Name: | awk '{ print $2 }'
//
// The advantage of doing this is then we can explicitly use the
// 'hdfs://hostname' prefix when we're trying to do copies to avoid
// silent failures when HDFS is down and the tools just copies
// locally.
//
// Note that if HDFS is not on port 9000 then we'll also need to do an
// HTTP GET on hostname:port and grab the information in the
// <title>...</title> (this is the best hack I can think of to get
// 'fs.default.name' given the tools available).
class HDFS
{
public:
  // Look for `hadoop' first where proposed, otherwise, look for
  // HADOOP_HOME, otherwise, assume it's on the PATH.
  explicit HDFS(const std::string& _hadoop)
    : hadoop(os::exists(_hadoop)
             ? _hadoop
             : (os::getenv("HADOOP_HOME").isSome()
                ? path::join(os::getenv("HADOOP_HOME").get(), "bin/hadoop")
                : "hadoop")) {}

  // Look for `hadoop' in HADOOP_HOME or assume it's on the PATH.
  HDFS()
    : hadoop(os::getenv("HADOOP_HOME").isSome()
             ? path::join(os::getenv("HADOOP_HOME").get(), "bin/hadoop")
             : "hadoop") {}

  // Check if hadoop client is available at the path that was set.
  // This can be done by executing `hadoop version` command and
  // checking for status code == 0.
  Try<bool> available()
  {
    Try<std::string> command = strings::format("%s version", hadoop);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error(out.error());
    }

    return true;
  }

  Try<bool> exists(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -test -e '%s'", hadoop, path);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error(out.error());
    }

    return true;
  }

  Try<Bytes> du(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -du '%s'", hadoop, path);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    //
    // TODO(marco): this was the existing logic, but not sure it is
    // actually needed.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error("HDFS du failed: " + out.error());
    }

    // We expect 2 space-separated output fields; a number of bytes then the
    // name of the path we gave. The 'hadoop' command can emit various WARN
    // or other log messages, so we make an effort to scan for the field we
    // want.
    foreach (const std::string& line, strings::tokenize(out.get(), "\n")) {
      // Note that we use tokenize() rather than split() since fields can be
      // delimited by multiple spaces.
      std::vector<std::string> fields = strings::tokenize(line, " ");

      if (fields.size() == 2 && fields[1] == path) {
        Result<size_t> size = numify<size_t>(fields[0]);
        if (size.isError()) {
          return Error("HDFS du returned unexpected format: " + size.error());
        } else if (size.isNone()) {
          return Error("HDFS du returned unexpected format");
        }

        return Bytes(size.get());
      }
    }

    return Error("HDFS du returned an unexpected format: '" + out.get() + "'");
  }

  Try<Nothing> rm(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -rm '%s'", hadoop, path);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

  Try<Nothing> copyFromLocal(
      const std::string& from,
      std::string to)
  {
    if (!os::exists(from)) {
      return Error("Failed to find " + from);
    }

    to = absolutePath(to);

    // Copy to HDFS.
    Try<std::string> command = strings::format(
        "%s fs -copyFromLocal '%s' '%s'", hadoop, from, to);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

  Try<Nothing> copyToLocal(
      std::string from,
      const std::string& to)
  {
    from = absolutePath(from);

    // Copy from HDFS.
    Try<std::string> command = strings::format(
        "%s fs -copyToLocal '%s' '%s'", hadoop, from, to);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

private:
  // Normalize an HDFS path such that it is either an absolute path
  // or a full hdfs:// URL.
  std::string absolutePath(const std::string& hdfsPath)
  {
    if (strings::startsWith(hdfsPath, "hdfs://") ||
        strings::startsWith(hdfsPath, "/")) {
      return hdfsPath;
    }

    return path::join("", hdfsPath);
  }

  const std::string hadoop;
};

#endif // __HDFS_HPP__
<commit_msg>Fixed the license header in hdfs.hpp.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

#ifndef __HDFS_HPP__
#define __HDFS_HPP__

#include <sstream>
#include <vector>

#include <stout/check.hpp>
#include <stout/error.hpp>
#include <stout/nothing.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/strings.hpp>

// TODO(benh): We should get the hostname:port (or ip:port) of the
// server via:
//
//  hadoop dfsadmin -report | grep Name: | awk '{ print $2 }'
//
// The advantage of doing this is then we can explicitly use the
// 'hdfs://hostname' prefix when we're trying to do copies to avoid
// silent failures when HDFS is down and the tools just copies
// locally.
//
// Note that if HDFS is not on port 9000 then we'll also need to do an
// HTTP GET on hostname:port and grab the information in the
// <title>...</title> (this is the best hack I can think of to get
// 'fs.default.name' given the tools available).
class HDFS
{
public:
  // Look for `hadoop' first where proposed, otherwise, look for
  // HADOOP_HOME, otherwise, assume it's on the PATH.
  explicit HDFS(const std::string& _hadoop)
    : hadoop(os::exists(_hadoop)
             ? _hadoop
             : (os::getenv("HADOOP_HOME").isSome()
                ? path::join(os::getenv("HADOOP_HOME").get(), "bin/hadoop")
                : "hadoop")) {}

  // Look for `hadoop' in HADOOP_HOME or assume it's on the PATH.
  HDFS()
    : hadoop(os::getenv("HADOOP_HOME").isSome()
             ? path::join(os::getenv("HADOOP_HOME").get(), "bin/hadoop")
             : "hadoop") {}

  // Check if hadoop client is available at the path that was set.
  // This can be done by executing `hadoop version` command and
  // checking for status code == 0.
  Try<bool> available()
  {
    Try<std::string> command = strings::format("%s version", hadoop);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error(out.error());
    }

    return true;
  }

  Try<bool> exists(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -test -e '%s'", hadoop, path);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error(out.error());
    }

    return true;
  }

  Try<Bytes> du(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -du '%s'", hadoop, path);

    CHECK_SOME(command);

    // We are piping stderr to stdout so that we can see the error (if
    // any) in the logs emitted by `os::shell()` in case of failure.
    //
    // TODO(marco): this was the existing logic, but not sure it is
    // actually needed.
    Try<std::string> out = os::shell(command.get() + " 2>&1");

    if (out.isError()) {
      return Error("HDFS du failed: " + out.error());
    }

    // We expect 2 space-separated output fields; a number of bytes then the
    // name of the path we gave. The 'hadoop' command can emit various WARN
    // or other log messages, so we make an effort to scan for the field we
    // want.
    foreach (const std::string& line, strings::tokenize(out.get(), "\n")) {
      // Note that we use tokenize() rather than split() since fields can be
      // delimited by multiple spaces.
      std::vector<std::string> fields = strings::tokenize(line, " ");

      if (fields.size() == 2 && fields[1] == path) {
        Result<size_t> size = numify<size_t>(fields[0]);
        if (size.isError()) {
          return Error("HDFS du returned unexpected format: " + size.error());
        } else if (size.isNone()) {
          return Error("HDFS du returned unexpected format");
        }

        return Bytes(size.get());
      }
    }

    return Error("HDFS du returned an unexpected format: '" + out.get() + "'");
  }

  Try<Nothing> rm(std::string path)
  {
    path = absolutePath(path);

    Try<std::string> command = strings::format(
        "%s fs -rm '%s'", hadoop, path);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

  Try<Nothing> copyFromLocal(
      const std::string& from,
      std::string to)
  {
    if (!os::exists(from)) {
      return Error("Failed to find " + from);
    }

    to = absolutePath(to);

    // Copy to HDFS.
    Try<std::string> command = strings::format(
        "%s fs -copyFromLocal '%s' '%s'", hadoop, from, to);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

  Try<Nothing> copyToLocal(
      std::string from,
      const std::string& to)
  {
    from = absolutePath(from);

    // Copy from HDFS.
    Try<std::string> command = strings::format(
        "%s fs -copyToLocal '%s' '%s'", hadoop, from, to);

    CHECK_SOME(command);

    Try<std::string> out = os::shell(command.get());

    if (out.isError()) {
      return Error(out.error());
    }

    return Nothing();
  }

private:
  // Normalize an HDFS path such that it is either an absolute path
  // or a full hdfs:// URL.
  std::string absolutePath(const std::string& hdfsPath)
  {
    if (strings::startsWith(hdfsPath, "hdfs://") ||
        strings::startsWith(hdfsPath, "/")) {
      return hdfsPath;
    }

    return path::join("", hdfsPath);
  }

  const std::string hadoop;
};

#endif // __HDFS_HPP__
<|endoftext|>


<commit_before>/*
 * fileio.cpp
 *
 *  Created on: Oct 21, 2009
 *      Author: Craig Rasmussen
 */

#include "fileio.hpp"
#include "connections/weight_conversions.hpp"
#include "structures/Buffer.hpp"
#include "utils/BufferUtilsMPI.hpp"
#include "utils/ExpandLeadingTilde.hpp"
#include "utils/PVLog.hpp"
#include "utils/conversions.hpp"

#include <assert.h>
#include <iostream>

#undef DEBUG_OUTPUT

namespace PV {

int PV_stat(const char *path, struct stat *buf) {
   // Call stat library function, trying up to MAX_FILESYSTEMCALL_TRIES times if an error is
   // returned.
   // If an error results on all MAX_FILESYSTEMCALL_TRIES times, returns -1 (the error return value)
   // for stat()
   // and errno is the error of the last attempt.
   char *realPath = strdup(expandLeadingTilde(path).c_str());
   int attempt    = 0;
   int retval     = -1;
   while (retval != 0) {
      errno  = 0;
      retval = stat(realPath, buf);
      if (retval == 0)
         break;
      attempt++;
      WarnLog().printf(
            "stat() failure for \"%s\" on attempt %d: %s\n", path, attempt, strerror(errno));
      if (attempt < MAX_FILESYSTEMCALL_TRIES) {
         sleep(1);
      }
      else {
         break;
      }
   }
   if (retval != 0) {
      ErrorLog().printf(
            "PV_stat exceeded MAX_FILESYSTEMCALL_TRIES = %d for \"%s\"\n",
            MAX_FILESYSTEMCALL_TRIES,
            path);
   }
   free(realPath);
   return retval;
}

static inline int makeDirectory(char const *dir) {
   mode_t dirmode = S_IRWXU | S_IRWXG | S_IRWXO;
   int status     = 0;

   char *workingDir = strdup(dir);
   FatalIf(workingDir == nullptr, "makeDirectory: unable to duplicate path \"%s\".", dir);

   int len = strlen(workingDir);
   if (workingDir[len - 1] == '/')
      workingDir[len - 1] = '\0';

   for (char *p = workingDir + 1; *p; p++)
      if (*p == '/') {
         *p = '\0';
         status |= mkdir(workingDir, dirmode);
         if (status != 0 && errno != EEXIST) {
            return status;
         }
         *p = '/';
      }
   status |= mkdir(workingDir, dirmode);
   if (errno == EEXIST) {
      status = 0;
   }
   return status;
}

void ensureDirExists(MPIBlock const *mpiBlock, char const *dirname) {
   // If rank zero, see if path exists, and try to create it if it doesn't.
   // If not rank zero, the routine does nothing.
   if (mpiBlock->getRank() != 0) {
      return;
   }

   std::string expandedDirName = expandLeadingTilde(dirname);
   struct stat pathstat;
   int statresult = stat(expandedDirName.c_str(), &pathstat);

   // If path exists and is a directory, nothing to do.
   // If path exists but is not a directory, fatal error.
   if (statresult == 0) {
      FatalIf(!S_ISDIR(pathstat.st_mode), "Path \"%s\" exists but is not a directory\n", dirname);
      return;
   }

   // Fatal error if checking the path gave an error other than No such file or directory
   FatalIf(
         errno != ENOENT,
         "Checking status of directory \"%s\" gave error \"%s\".\n",
         dirname,
         strerror(errno));

   InfoLog().printf("Directory \"%s\" does not exist; attempting to create\n", dirname);

   // Try up to MAX_FILESYSTEMCALL_TRIES times until it works
   for (int attemptNum = 0; attemptNum < MAX_FILESYSTEMCALL_TRIES; attemptNum++) {
      int mkdirstatus = makeDirectory(expandedDirName.c_str());
      if (mkdirstatus != 0) {
         if (attemptNum == MAX_FILESYSTEMCALL_TRIES - 1) {
            Fatal().printf(
                  "Directory \"%s\" could not be created: %s; Exiting\n", dirname, strerror(errno));
         }
         else {
            getOutputStream().flush();
            WarnLog().printf(
                  "Directory \"%s\" could not be created: %s; Retrying %d out of %d\n",
                  dirname,
                  strerror(errno),
                  attemptNum + 1,
                  MAX_FILESYSTEMCALL_TRIES);
            sleep(1);
         }
      }
      else {
         InfoLog().printf("Successfully created directory \"%s/\".\n", dirname);
         errno = 0;
         break;
      }
   }
}

} // namespace PV
<commit_msg>Adds missing free() for a strdup-created string<commit_after>/*
 * fileio.cpp
 *
 *  Created on: Oct 21, 2009
 *      Author: Craig Rasmussen
 */

#include "fileio.hpp"
#include "connections/weight_conversions.hpp"
#include "structures/Buffer.hpp"
#include "utils/BufferUtilsMPI.hpp"
#include "utils/ExpandLeadingTilde.hpp"
#include "utils/PVLog.hpp"
#include "utils/conversions.hpp"

#include <assert.h>
#include <iostream>

#undef DEBUG_OUTPUT

namespace PV {

int PV_stat(const char *path, struct stat *buf) {
   // Call stat library function, trying up to MAX_FILESYSTEMCALL_TRIES times if an error is
   // returned.
   // If an error results on all MAX_FILESYSTEMCALL_TRIES times, returns -1 (the error return value)
   // for stat()
   // and errno is the error of the last attempt.
   char *realPath = strdup(expandLeadingTilde(path).c_str());
   int attempt    = 0;
   int retval     = -1;
   while (retval != 0) {
      errno  = 0;
      retval = stat(realPath, buf);
      if (retval == 0)
         break;
      attempt++;
      WarnLog().printf(
            "stat() failure for \"%s\" on attempt %d: %s\n", path, attempt, strerror(errno));
      if (attempt < MAX_FILESYSTEMCALL_TRIES) {
         sleep(1);
      }
      else {
         break;
      }
   }
   if (retval != 0) {
      ErrorLog().printf(
            "PV_stat exceeded MAX_FILESYSTEMCALL_TRIES = %d for \"%s\"\n",
            MAX_FILESYSTEMCALL_TRIES,
            path);
   }
   free(realPath);
   return retval;
}

static inline int makeDirectory(char const *dir) {
   mode_t dirmode = S_IRWXU | S_IRWXG | S_IRWXO;
   int status     = 0;

   char *workingDir = strdup(dir);
   FatalIf(workingDir == nullptr, "makeDirectory: unable to duplicate path \"%s\".", dir);

   int len = strlen(workingDir);
   if (workingDir[len - 1] == '/')
      workingDir[len - 1] = '\0';

   for (char *p = workingDir + 1; *p; p++)
      if (*p == '/') {
         *p = '\0';
         status |= mkdir(workingDir, dirmode);
         if (status != 0 && errno != EEXIST) {
            return status;
         }
         *p = '/';
      }
   status |= mkdir(workingDir, dirmode);
   if (errno == EEXIST) {
      status = 0;
   }
   free(workingDir);
   return status;
}

void ensureDirExists(MPIBlock const *mpiBlock, char const *dirname) {
   // If rank zero, see if path exists, and try to create it if it doesn't.
   // If not rank zero, the routine does nothing.
   if (mpiBlock->getRank() != 0) {
      return;
   }

   std::string expandedDirName = expandLeadingTilde(dirname);
   struct stat pathstat;
   int statresult = stat(expandedDirName.c_str(), &pathstat);

   // If path exists and is a directory, nothing to do.
   // If path exists but is not a directory, fatal error.
   if (statresult == 0) {
      FatalIf(!S_ISDIR(pathstat.st_mode), "Path \"%s\" exists but is not a directory\n", dirname);
      return;
   }

   // Fatal error if checking the path gave an error other than No such file or directory
   FatalIf(
         errno != ENOENT,
         "Checking status of directory \"%s\" gave error \"%s\".\n",
         dirname,
         strerror(errno));

   InfoLog().printf("Directory \"%s\" does not exist; attempting to create\n", dirname);

   // Try up to MAX_FILESYSTEMCALL_TRIES times until it works
   for (int attemptNum = 0; attemptNum < MAX_FILESYSTEMCALL_TRIES; attemptNum++) {
      int mkdirstatus = makeDirectory(expandedDirName.c_str());
      if (mkdirstatus != 0) {
         if (attemptNum == MAX_FILESYSTEMCALL_TRIES - 1) {
            Fatal().printf(
                  "Directory \"%s\" could not be created: %s; Exiting\n", dirname, strerror(errno));
         }
         else {
            getOutputStream().flush();
            WarnLog().printf(
                  "Directory \"%s\" could not be created: %s; Retrying %d out of %d\n",
                  dirname,
                  strerror(errno),
                  attemptNum + 1,
                  MAX_FILESYSTEMCALL_TRIES);
            sleep(1);
         }
      }
      else {
         InfoLog().printf("Successfully created directory \"%s/\".\n", dirname);
         errno = 0;
         break;
      }
   }
}

} // namespace PV
<|endoftext|>