<commit_before>/**
 * 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.
 */
#ifndef __STOUT_FOREACH_HPP__
#define __STOUT_FOREACH_HPP__

#include <boost/foreach.hpp>

#include <boost/tuple/tuple.hpp>

namespace __foreach__ {

// NOTE: This is a copied from Boost
// (boost/tuple/detail/tuple_basic_no_partial_spec.hpp) because the
// new 'boost::tuples::ignore' does not work in our 'foreachkey' and
// 'foreachvalue'.
struct swallow_assign {
  template<typename T>
  swallow_assign const& operator=(const T&) const {
    return *this;
  }
};

swallow_assign const ignore = swallow_assign();

} // namespace __foreach__ {

#define BOOST_FOREACH_PAIR(VARFIRST, VARSECOND, COL)                                            \
    BOOST_FOREACH_PREAMBLE()                                                                    \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else   \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else     \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else       \
    for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_onetime) = true;                                \
              BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL);                                    \
              BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0)                            \
        if  (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_onetime))) {} else                       \
        for (VARFIRST = BOOST_FOREACH_DEREF(COL).first;                                         \
	     !BOOST_FOREACH_ID(_foreach_onetime);                                                                 \
	     BOOST_FOREACH_ID(_foreach_onetime) = true)                                                           \
            if  (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else                  \
            for (VARSECOND = BOOST_FOREACH_DEREF(COL).second;                                   \
		 !BOOST_FOREACH_ID(_foreach_continue);                                                            \
		 BOOST_FOREACH_ID(_foreach_continue) = true)

#define foreach BOOST_FOREACH
#define foreachpair BOOST_FOREACH_PAIR

#define foreachkey(VAR, COL)                    \
  foreachpair (VAR, __foreach__::ignore, COL)

#define foreachvalue(VAR, COL)                  \
  foreachpair (__foreach__::ignore, VAR, COL)

#endif // __STOUT_FOREACH_HPP__
<commit_msg>Removed unneeded boost::tuple include.<commit_after>/**
 * 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.
 */
#ifndef __STOUT_FOREACH_HPP__
#define __STOUT_FOREACH_HPP__

#include <boost/foreach.hpp>

namespace __foreach__ {

// NOTE: This is a copied from Boost
// (boost/tuple/detail/tuple_basic_no_partial_spec.hpp) because the
// new 'boost::tuples::ignore' does not work in our 'foreachkey' and
// 'foreachvalue'.
struct swallow_assign {
  template<typename T>
  swallow_assign const& operator=(const T&) const {
    return *this;
  }
};

swallow_assign const ignore = swallow_assign();

} // namespace __foreach__ {

#define BOOST_FOREACH_PAIR(VARFIRST, VARSECOND, COL)                                            \
    BOOST_FOREACH_PREAMBLE()                                                                    \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else   \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else     \
    if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else       \
    for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_onetime) = true;                                \
              BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL);                                    \
              BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0)                            \
        if  (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_onetime))) {} else                       \
        for (VARFIRST = BOOST_FOREACH_DEREF(COL).first;                                         \
	     !BOOST_FOREACH_ID(_foreach_onetime);                                                                 \
	     BOOST_FOREACH_ID(_foreach_onetime) = true)                                                           \
            if  (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else                  \
            for (VARSECOND = BOOST_FOREACH_DEREF(COL).second;                                   \
		 !BOOST_FOREACH_ID(_foreach_continue);                                                            \
		 BOOST_FOREACH_ID(_foreach_continue) = true)

#define foreach BOOST_FOREACH
#define foreachpair BOOST_FOREACH_PAIR

#define foreachkey(VAR, COL)                    \
  foreachpair (VAR, __foreach__::ignore, COL)

#define foreachvalue(VAR, COL)                  \
  foreachpair (__foreach__::ignore, VAR, COL)

#endif // __STOUT_FOREACH_HPP__
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013-2014 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "engineuwsgi.h"

#include "bodyuwsgi.h"
#include "bodybuffereduwsgi.h"

#include <QSocketNotifier>

#include <Cutelyst/common.h>
#include <Cutelyst/application.h>
#include <Cutelyst/context.h>
#include <Cutelyst/response.h>
#include <Cutelyst/request_p.h>

Q_LOGGING_CATEGORY(CUTELYST_UWSGI, "cutelyst.uwsgi")

using namespace Cutelyst;

typedef struct {
    QFile *bodyFile;
    BodyUWSGI *bodyUWSGI;
    BodyBufferedUWSGI *bodyBufferedUWSGI;
    RequestPrivate *priv;
    Request *request;
} CachedRequest;

EngineUwsgi::EngineUwsgi(Application *app) :
    m_app(app)
{
    connect(this, &EngineUwsgi::postFork,
            this, &EngineUwsgi::forked);
}

EngineUwsgi::~EngineUwsgi()
{
}

void EngineUwsgi::setThread(QThread *thread)
{
    moveToThread(thread);
    connect(thread, &QThread::started,
            this, &EngineUwsgi::forked, Qt::DirectConnection);
}

void EngineUwsgi::finalizeBody(Context *ctx, QIODevice *body, void *engineData)
{
    Q_UNUSED(ctx)

    struct wsgi_request *wsgi_req = static_cast<wsgi_request*>(engineData);
    body->seek(0);
    char block[4096];
    while (!body->atEnd()) {
        qint64 in = body->read(block, sizeof(block));
        if (in <= 0)
            break;

        if (uwsgi_response_write_body_do(wsgi_req, block, in) != UWSGI_OK) {
            qCWarning(CUTELYST_UWSGI) << "Failed to write body";
            break;
        }
    }
}

void EngineUwsgi::readRequestUWSGI(wsgi_request *wsgi_req)
{
    for(;;) {
        int ret = uwsgi_wait_read_req(wsgi_req);
        if (ret <= 0) {
            uwsgi_log("Failed wait read\n");
            goto end;
        }

        int status = wsgi_req->socket->proto(wsgi_req);
        if (status < 0) {
            uwsgi_log("Failed broken socket\n");
            goto end;
        } else if (status == 0) {
            break;
        }
    }

    // empty request ?
    if (!wsgi_req->uh->pktsize) {
        qCDebug(CUTELYST_UWSGI) << "Empty request. skip.";
        uwsgi_log("Failed empty request\n");
        goto end;
    }

    // get uwsgi variables
    if (uwsgi_parse_vars(wsgi_req)) {
        uwsgi_log("Invalid request. skip.\n");
        qCDebug(CUTELYST_UWSGI) << "Invalid request. skip.";
        goto end;
    }

    processRequest(wsgi_req);

    wsgi_req->end_of_request = uwsgi_micros();
    qCDebug(CUTELYST_STATS) << QString("Request took: %1s").arg(QString::number((wsgi_req->end_of_request-wsgi_req->start_of_request)/1000000.0, 'f')).toLatin1().data();

end:
    uwsgi_close_request(wsgi_req);
}

void EngineUwsgi::processRequest(wsgi_request *req)
{
    CachedRequest *cache = static_cast<CachedRequest *>(req->async_environ);

    RequestPrivate *priv = cache->priv;
    priv->reset();

    priv->https = req->https_len;
    // wsgi_req->uri containg the whole URI it /foo/bar?query=null
    // so we use path_info, maybe it would be better to just build our
    // Request->uri() from it, but we need to run a performance test
    priv->path = QString::fromLatin1(req->path_info + 1, req->path_info_len - 1);

    priv->serverAddress = QString::fromLatin1(req->host, req->host_len);
    priv->query = QByteArray::fromRawData(req->query_string, req->query_string_len);

    priv->method = QString::fromLatin1(req->method, req->method_len);
    priv->protocol = QString::fromLatin1(req->protocol, req->protocol_len);
    priv->remoteAddress = QHostAddress(QString::fromLatin1(req->remote_addr, req->remote_addr_len));
    priv->remoteUser = QString::fromLatin1(req->remote_user, req->remote_user_len);

    uint16_t remote_port_len;
    char *remote_port = uwsgi_get_var(req, (char *) "REMOTE_PORT", 11, &remote_port_len);
    priv->remotePort = QByteArray::fromRawData(remote_port, remote_port_len).toUInt();

    Headers headers;
    for (int i = 0; i < req->var_cnt; i += 2) {
        if (req->hvec[i].iov_len < 6) {
            continue;
        }

        if (!uwsgi_startswith((char *) req->hvec[i].iov_base,
                              const_cast<char *>("HTTP_"), 5)) {
            QString key = QString::fromLatin1(httpCase((char *) req->hvec[i].iov_base+5, req->hvec[i].iov_len-5));
            QString value = QString::fromLatin1((char *) req->hvec[i + 1].iov_base, req->hvec[i + 1].iov_len);
            headers.insertMulti(key, value);
        }
    }

    if (req->content_type_len > 0) {
        headers.insertMulti(QStringLiteral("Content-Type"),
                            QString::fromLatin1(req->content_type, req->content_type_len));
    }

    if (req->encoding_len > 0) {
        headers.insertMulti(QStringLiteral("Content-Encoding"),
                            QString::fromLatin1(req->encoding, req->encoding_len));
    }
    priv->headers = headers;

    QIODevice *body;
    if (req->post_file) {
//        qCDebug(CUTELYST_UWSGI) << "Post file available:" << req->post_file;
        QFile *upload = cache->bodyFile;
        if (upload->open(req->post_file, QIODevice::ReadOnly)) {
            body = upload;
        } else {
//            qCDebug(CUTELYST_UWSGI) << "Could not open post file:" << upload->errorString();
            body = cache->bodyBufferedUWSGI;
        }
    } else if (uwsgi.post_buffering) {
//        qCDebug(CUTELYST_UWSGI) << "Post buffering size:" << uwsgi.post_buffering;
        body = cache->bodyUWSGI;
    } else {
        // BodyBufferedUWSGI is an IO device which will
        // only consume the body when some of it's functions
        // is called, this is because here we can't seek
        // the body.
        body = cache->bodyBufferedUWSGI;
    }
    priv->body = body;

    handleRequest(cache->request, false);

    body->close();
}

QByteArray EngineUwsgi::httpCase(char *key, int key_len) const
{
    bool lastWasUnderscore = true;
    for (int i = 0 ; i < key_len ; ++i) {
        QChar buf = key[i];
        if (buf == '_') {
            key[i] = '-';
            lastWasUnderscore = true;
        } else if(lastWasUnderscore) {
            lastWasUnderscore = false;
        } else {
            key[i] = buf.toLower().toLatin1();
        }
    }

    return QByteArray::fromRawData(key, key_len);
}

void EngineUwsgi::reload()
{
    qCDebug(CUTELYST_UWSGI) << "Reloading application due application request";
    uwsgi_reload(uwsgi.argv);
}

void EngineUwsgi::addUnusedRequest(wsgi_request *wsgi_req)
{
    CachedRequest *cache = static_cast<CachedRequest *>(wsgi_req->async_environ);
    if (cache) {
        // TODO move to a class
        delete cache;
    }
    cache = new CachedRequest;
    cache->bodyFile = new QFile(this);
    cache->bodyUWSGI = new BodyUWSGI(wsgi_req, this);
    cache->bodyBufferedUWSGI = new BodyBufferedUWSGI(wsgi_req, this);
    cache->priv = new RequestPrivate;
    cache->priv->engine = this;
    cache->priv->requestPtr = wsgi_req;
    cache->request = new Request(cache->priv);
    wsgi_req->async_environ = cache;

    m_unusedReq.append(wsgi_req);
}

void EngineUwsgi::watchSocket(struct uwsgi_socket *uwsgi_sock)
{
    QSocketNotifier *socketNotifier = new QSocketNotifier(uwsgi_sock->fd, QSocketNotifier::Read, this);
    connect(this, &EngineUwsgi::enableSockets,
            socketNotifier, &QSocketNotifier::setEnabled);
    connect(socketNotifier, &QSocketNotifier::activated,
            [=](int fd) {
        struct wsgi_request *wsgi_req = m_unusedReq.takeFirst();
        if (wsgi_req == NULL) {
            uwsgi_async_queue_is_full(uwsgi_now());
            return;
        }

        // fill wsgi_request structure
        wsgi_req_setup(wsgi_req, wsgi_req->async_id, uwsgi_sock);

        // mark core as used
        uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 1;

        // accept the connection
        if (wsgi_req_simple_accept(wsgi_req, fd)) {
            uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
            m_unusedReq.append(wsgi_req);
            return;
        }

        wsgi_req->start_of_request = uwsgi_micros();
        wsgi_req->start_of_request_in_sec = wsgi_req->start_of_request/1000000;

#ifdef UWSGI_GO_CHEAP_CODE
        // enter harakiri mode
        if (uwsgi.harakiri_options.workers > 0) {
            set_harakiri(uwsgi.harakiri_options.workers);
        }
#endif // UWSGI_GO_CHEAP_CODE

        CachedRequest *cache = static_cast<CachedRequest *>(wsgi_req->async_environ);
        readRequestUWSGI(wsgi_req);
        wsgi_req->async_environ = cache;

        m_unusedReq.append(wsgi_req);
    });
}

void EngineUwsgi::reuseEngineRequests(EngineUwsgi *engine)
{
    Q_FOREACH (struct wsgi_request *req, engine->unusedRequestQueue()) {
        addUnusedRequest(req);
    }
}

void EngineUwsgi::stop()
{
    Q_EMIT enableSockets(false);

    if (thread() != qApp->thread()) {
        thread()->quit();
    }
}

QList<wsgi_request *> EngineUwsgi::unusedRequestQueue() const
{
    return m_unusedReq;
}

bool EngineUwsgi::finalizeHeaders(Context *ctx, void *engineData)
{
    struct wsgi_request *wsgi_req = static_cast<wsgi_request*>(engineData);
    Response *res = ctx->res();

    QByteArray status = statusCode(res->status());
    if (uwsgi_response_prepare_headers(wsgi_req,
                                       status.data(),
                                       status.size())) {
        return false;
    }

    QList<HeaderValuePair> headers = res->headers().headersForResponse();
    Q_FOREACH (const HeaderValuePair &pair, headers) {
        QByteArray key = pair.key.toLatin1();
        QByteArray value = pair.value.toLatin1();
        if (uwsgi_response_add_header(wsgi_req,
                                      key.data(),
                                      key.size(),
                                      value.data(),
                                      value.size())) {
            return false;
        }
    }

    return true;
}

bool EngineUwsgi::init()
{
    return true;
}

void EngineUwsgi::forked()
{
    if (QThread::currentThread() != qApp->thread()) {
        m_app = qobject_cast<Application *>(m_app->metaObject()->newInstance());
        if (!m_app) {
            uwsgi_log("*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
                      "make sure your constructor has Q_INVOKABLE macro.\n");
            Q_EMIT engineDisabled(this);
            return;
        }

        // Move the application and it's children to this thread
        m_app->moveToThread(thread());
        m_app->setParent(this);

        // init and postfork
        if (!initApplication(m_app, true)) {
            uwsgi_log("Failed to init application on a different thread than main.\n");
            Q_EMIT engineDisabled(this);
            return;
        }
    } else if (!postForkApplication()) {
#ifdef UWSGI_GO_CHEAP_CODE
        // We need to tell the master process that the
        // application failed to setup and that it shouldn't
        // try to respawn the worker
        exit(UWSGI_GO_CHEAP_CODE);
#endif // UWSGI_GO_CHEAP_CODE
    }

    // Start Monitoring Sockets
    struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
    while(uwsgi_sock) {
        watchSocket(uwsgi_sock);
        uwsgi_sock = uwsgi_sock->next;
    }
}
<commit_msg>Do not try to insert headers as multi values<commit_after>/*
 * Copyright (C) 2013-2014 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "engineuwsgi.h"

#include "bodyuwsgi.h"
#include "bodybuffereduwsgi.h"

#include <QSocketNotifier>

#include <Cutelyst/common.h>
#include <Cutelyst/application.h>
#include <Cutelyst/context.h>
#include <Cutelyst/response.h>
#include <Cutelyst/request_p.h>

Q_LOGGING_CATEGORY(CUTELYST_UWSGI, "cutelyst.uwsgi")

using namespace Cutelyst;

typedef struct {
    QFile *bodyFile;
    BodyUWSGI *bodyUWSGI;
    BodyBufferedUWSGI *bodyBufferedUWSGI;
    RequestPrivate *priv;
    Request *request;
} CachedRequest;

EngineUwsgi::EngineUwsgi(Application *app) :
    m_app(app)
{
    connect(this, &EngineUwsgi::postFork,
            this, &EngineUwsgi::forked);
}

EngineUwsgi::~EngineUwsgi()
{
}

void EngineUwsgi::setThread(QThread *thread)
{
    moveToThread(thread);
    connect(thread, &QThread::started,
            this, &EngineUwsgi::forked, Qt::DirectConnection);
}

void EngineUwsgi::finalizeBody(Context *ctx, QIODevice *body, void *engineData)
{
    Q_UNUSED(ctx)

    struct wsgi_request *wsgi_req = static_cast<wsgi_request*>(engineData);
    body->seek(0);
    char block[4096];
    while (!body->atEnd()) {
        qint64 in = body->read(block, sizeof(block));
        if (in <= 0)
            break;

        if (uwsgi_response_write_body_do(wsgi_req, block, in) != UWSGI_OK) {
            qCWarning(CUTELYST_UWSGI) << "Failed to write body";
            break;
        }
    }
}

void EngineUwsgi::readRequestUWSGI(wsgi_request *wsgi_req)
{
    for(;;) {
        int ret = uwsgi_wait_read_req(wsgi_req);
        if (ret <= 0) {
            uwsgi_log("Failed wait read\n");
            goto end;
        }

        int status = wsgi_req->socket->proto(wsgi_req);
        if (status < 0) {
            uwsgi_log("Failed broken socket\n");
            goto end;
        } else if (status == 0) {
            break;
        }
    }

    // empty request ?
    if (!wsgi_req->uh->pktsize) {
        qCDebug(CUTELYST_UWSGI) << "Empty request. skip.";
        uwsgi_log("Failed empty request\n");
        goto end;
    }

    // get uwsgi variables
    if (uwsgi_parse_vars(wsgi_req)) {
        uwsgi_log("Invalid request. skip.\n");
        qCDebug(CUTELYST_UWSGI) << "Invalid request. skip.";
        goto end;
    }

    processRequest(wsgi_req);

    wsgi_req->end_of_request = uwsgi_micros();
    qCDebug(CUTELYST_STATS) << QString("Request took: %1s").arg(QString::number((wsgi_req->end_of_request-wsgi_req->start_of_request)/1000000.0, 'f')).toLatin1().data();

end:
    uwsgi_close_request(wsgi_req);
}

void EngineUwsgi::processRequest(wsgi_request *req)
{
    CachedRequest *cache = static_cast<CachedRequest *>(req->async_environ);

    RequestPrivate *priv = cache->priv;
    priv->reset();

    priv->https = req->https_len;
    // wsgi_req->uri containg the whole URI it /foo/bar?query=null
    // so we use path_info, maybe it would be better to just build our
    // Request->uri() from it, but we need to run a performance test
    priv->path = QString::fromLatin1(req->path_info + 1, req->path_info_len - 1);

    priv->serverAddress = QString::fromLatin1(req->host, req->host_len);
    priv->query = QByteArray::fromRawData(req->query_string, req->query_string_len);

    priv->method = QString::fromLatin1(req->method, req->method_len);
    priv->protocol = QString::fromLatin1(req->protocol, req->protocol_len);
    priv->remoteAddress = QHostAddress(QString::fromLatin1(req->remote_addr, req->remote_addr_len));
    priv->remoteUser = QString::fromLatin1(req->remote_user, req->remote_user_len);

    uint16_t remote_port_len;
    char *remote_port = uwsgi_get_var(req, (char *) "REMOTE_PORT", 11, &remote_port_len);
    priv->remotePort = QByteArray::fromRawData(remote_port, remote_port_len).toUInt();

    Headers headers;
    for (int i = 0; i < req->var_cnt; i += 2) {
        if (req->hvec[i].iov_len < 6) {
            continue;
        }

        if (!uwsgi_startswith((char *) req->hvec[i].iov_base,
                              const_cast<char *>("HTTP_"), 5)) {
            QString key = QString::fromLatin1(httpCase((char *) req->hvec[i].iov_base+5, req->hvec[i].iov_len-5));
            QString value = QString::fromLatin1((char *) req->hvec[i + 1].iov_base, req->hvec[i + 1].iov_len);
            headers.insert(key, value);
        }
    }

    if (req->content_type_len > 0) {
        headers.insert(QStringLiteral("Content-Type"),
                       QString::fromLatin1(req->content_type, req->content_type_len));
    }

    if (req->encoding_len > 0) {
        headers.insert(QStringLiteral("Content-Encoding"),
                       QString::fromLatin1(req->encoding, req->encoding_len));
    }
    priv->headers = headers;

    QIODevice *body;
    if (req->post_file) {
//        qCDebug(CUTELYST_UWSGI) << "Post file available:" << req->post_file;
        QFile *upload = cache->bodyFile;
        if (upload->open(req->post_file, QIODevice::ReadOnly)) {
            body = upload;
        } else {
//            qCDebug(CUTELYST_UWSGI) << "Could not open post file:" << upload->errorString();
            body = cache->bodyBufferedUWSGI;
        }
    } else if (uwsgi.post_buffering) {
//        qCDebug(CUTELYST_UWSGI) << "Post buffering size:" << uwsgi.post_buffering;
        body = cache->bodyUWSGI;
    } else {
        // BodyBufferedUWSGI is an IO device which will
        // only consume the body when some of it's functions
        // is called, this is because here we can't seek
        // the body.
        body = cache->bodyBufferedUWSGI;
    }
    priv->body = body;

    handleRequest(cache->request, false);

    body->close();
}

QByteArray EngineUwsgi::httpCase(char *key, int key_len) const
{
    bool lastWasUnderscore = true;
    for (int i = 0 ; i < key_len ; ++i) {
        QChar buf = key[i];
        if (buf == '_') {
            key[i] = '-';
            lastWasUnderscore = true;
        } else if(lastWasUnderscore) {
            lastWasUnderscore = false;
        } else {
            key[i] = buf.toLower().toLatin1();
        }
    }

    return QByteArray::fromRawData(key, key_len);
}

void EngineUwsgi::reload()
{
    qCDebug(CUTELYST_UWSGI) << "Reloading application due application request";
    uwsgi_reload(uwsgi.argv);
}

void EngineUwsgi::addUnusedRequest(wsgi_request *wsgi_req)
{
    CachedRequest *cache = static_cast<CachedRequest *>(wsgi_req->async_environ);
    if (cache) {
        // TODO move to a class
        delete cache;
    }
    cache = new CachedRequest;
    cache->bodyFile = new QFile(this);
    cache->bodyUWSGI = new BodyUWSGI(wsgi_req, this);
    cache->bodyBufferedUWSGI = new BodyBufferedUWSGI(wsgi_req, this);
    cache->priv = new RequestPrivate;
    cache->priv->engine = this;
    cache->priv->requestPtr = wsgi_req;
    cache->request = new Request(cache->priv);
    wsgi_req->async_environ = cache;

    m_unusedReq.append(wsgi_req);
}

void EngineUwsgi::watchSocket(struct uwsgi_socket *uwsgi_sock)
{
    QSocketNotifier *socketNotifier = new QSocketNotifier(uwsgi_sock->fd, QSocketNotifier::Read, this);
    connect(this, &EngineUwsgi::enableSockets,
            socketNotifier, &QSocketNotifier::setEnabled);
    connect(socketNotifier, &QSocketNotifier::activated,
            [=](int fd) {
        struct wsgi_request *wsgi_req = m_unusedReq.takeFirst();
        if (wsgi_req == NULL) {
            uwsgi_async_queue_is_full(uwsgi_now());
            return;
        }

        // fill wsgi_request structure
        wsgi_req_setup(wsgi_req, wsgi_req->async_id, uwsgi_sock);

        // mark core as used
        uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 1;

        // accept the connection
        if (wsgi_req_simple_accept(wsgi_req, fd)) {
            uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
            m_unusedReq.append(wsgi_req);
            return;
        }

        wsgi_req->start_of_request = uwsgi_micros();
        wsgi_req->start_of_request_in_sec = wsgi_req->start_of_request/1000000;

#ifdef UWSGI_GO_CHEAP_CODE
        // enter harakiri mode
        if (uwsgi.harakiri_options.workers > 0) {
            set_harakiri(uwsgi.harakiri_options.workers);
        }
#endif // UWSGI_GO_CHEAP_CODE

        CachedRequest *cache = static_cast<CachedRequest *>(wsgi_req->async_environ);
        readRequestUWSGI(wsgi_req);
        wsgi_req->async_environ = cache;

        m_unusedReq.append(wsgi_req);
    });
}

void EngineUwsgi::reuseEngineRequests(EngineUwsgi *engine)
{
    Q_FOREACH (struct wsgi_request *req, engine->unusedRequestQueue()) {
        addUnusedRequest(req);
    }
}

void EngineUwsgi::stop()
{
    Q_EMIT enableSockets(false);

    if (thread() != qApp->thread()) {
        thread()->quit();
    }
}

QList<wsgi_request *> EngineUwsgi::unusedRequestQueue() const
{
    return m_unusedReq;
}

bool EngineUwsgi::finalizeHeaders(Context *ctx, void *engineData)
{
    struct wsgi_request *wsgi_req = static_cast<wsgi_request*>(engineData);
    Response *res = ctx->res();

    QByteArray status = statusCode(res->status());
    if (uwsgi_response_prepare_headers(wsgi_req,
                                       status.data(),
                                       status.size())) {
        return false;
    }

    QList<HeaderValuePair> headers = res->headers().headersForResponse();
    Q_FOREACH (const HeaderValuePair &pair, headers) {
        QByteArray key = pair.key.toLatin1();
        QByteArray value = pair.value.toLatin1();
        if (uwsgi_response_add_header(wsgi_req,
                                      key.data(),
                                      key.size(),
                                      value.data(),
                                      value.size())) {
            return false;
        }
    }

    return true;
}

bool EngineUwsgi::init()
{
    return true;
}

void EngineUwsgi::forked()
{
    if (QThread::currentThread() != qApp->thread()) {
        m_app = qobject_cast<Application *>(m_app->metaObject()->newInstance());
        if (!m_app) {
            uwsgi_log("*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
                      "make sure your constructor has Q_INVOKABLE macro.\n");
            Q_EMIT engineDisabled(this);
            return;
        }

        // Move the application and it's children to this thread
        m_app->moveToThread(thread());
        m_app->setParent(this);

        // init and postfork
        if (!initApplication(m_app, true)) {
            uwsgi_log("Failed to init application on a different thread than main.\n");
            Q_EMIT engineDisabled(this);
            return;
        }
    } else if (!postForkApplication()) {
#ifdef UWSGI_GO_CHEAP_CODE
        // We need to tell the master process that the
        // application failed to setup and that it shouldn't
        // try to respawn the worker
        exit(UWSGI_GO_CHEAP_CODE);
#endif // UWSGI_GO_CHEAP_CODE
    }

    // Start Monitoring Sockets
    struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
    while(uwsgi_sock) {
        watchSocket(uwsgi_sock);
        uwsgi_sock = uwsgi_sock->next;
    }
}
<|endoftext|>


<commit_before>/*
  Copyright (C) 2008-2016 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "textinput.h"
#include <QStyleOptionFrame>
#include <IrcCommandParser>
#include <IrcBufferModel>
#include <IrcConnection>
#include <QMessageBox>
#include <QSettings>
#include <QCheckBox>
#include <IrcBuffer>
#include <QKeyEvent>
#include <QPainter>

TextInput::TextInput(QWidget* parent) : QLineEdit(parent)
{
    setAttribute(Qt::WA_MacShowFocusRect, false);

    d.hint = "...";
    d.index = 0;
    d.buffer = 0;
    d.parser = 0;

    d.completer = new IrcCompleter(this);
    connect(this, SIGNAL(bufferChanged(IrcBuffer*)), d.completer, SLOT(setBuffer(IrcBuffer*)));
    connect(this, SIGNAL(parserChanged(IrcCommandParser*)), d.completer, SLOT(setParser(IrcCommandParser*)));
    connect(d.completer, SIGNAL(completed(QString,int)), this, SLOT(doComplete(QString,int)));
    connect(this, SIGNAL(textEdited(QString)), d.completer, SLOT(reset()));

    connect(this, SIGNAL(returnPressed()), this, SLOT(sendInput()));
    connect(this, SIGNAL(textChanged(QString)), this, SLOT(updateHint(QString)));
}

IrcBuffer* TextInput::buffer() const
{
    return d.buffer;
}

IrcCommandParser* TextInput::parser() const
{
    return d.parser;
}

static void bind(IrcBuffer* buffer, IrcCommandParser* parser)
{
    if (buffer && parser) {
        IrcBufferModel* model = buffer->model();
        QObject::connect(model, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList)));
        QObject::connect(buffer, SIGNAL(titleChanged(QString)), parser, SLOT(setTarget(QString)));

        parser->setTarget(buffer->title());
        parser->setChannels(buffer->model()->channels());
    } else if (parser) {
        parser->reset();
    }
}

static void unbind(IrcBuffer* buffer, IrcCommandParser* parser)
{
    if (buffer && parser) {
        IrcBufferModel* model = buffer->model();
        QObject::disconnect(model, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList)));
        QObject::disconnect(buffer, SIGNAL(titleChanged(QString)), parser, SLOT(setTarget(QString)));
    }
}

void TextInput::setBuffer(IrcBuffer* buffer)
{
    if (d.buffer != buffer) {
        unbind(d.buffer, d.parser);
        bind(buffer, d.parser);
        if (d.buffer)
            d.states.insert(d.buffer, saveState());
        d.buffer = buffer;
        if (buffer)
            restoreState(d.states.value(buffer));
        emit bufferChanged(buffer);
    }
}

void TextInput::setParser(IrcCommandParser* parser)
{
    if (d.parser != parser) {
        unbind(d.buffer, d.parser);
        bind(d.buffer, parser);
        d.parser = parser;
        emit parserChanged(parser);
    }
}

bool TextInput::event(QEvent* event)
{
    if (event->type() == QEvent::KeyPress) {
        switch (static_cast<QKeyEvent*>(event)->key()) {
        case Qt::Key_Tab:
            tryComplete(IrcCompleter::Forward);
            return true;
        case Qt::Key_Backtab:
            tryComplete(IrcCompleter::Backward);
            return true;
        case Qt::Key_Up:
            goBackward();
            return true;
        case Qt::Key_Down:
            goForward();
            return true;
        default:
            break;
        }
    }
    return QLineEdit::event(event);
}

// copied from qlineedit.cpp:
#define vMargin 1
#define hMargin 2

void TextInput::paintEvent(QPaintEvent* event)
{
    QLineEdit::paintEvent(event);

    if (!d.hint.isEmpty()) {
        QStyleOptionFrameV2 option;
        initStyleOption(&option);

        QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &option, this);
        int left, top, right, bottom;
        getTextMargins(&left, &top, &right, &bottom);
        left += qMax(0, -fontMetrics().minLeftBearing());
        r.adjust(left, top, -right, -bottom);
        r.adjust(hMargin, vMargin, -hMargin, -vMargin);

        QString txt = text();
        if (!txt.isEmpty()) {
            if (!txt.endsWith(" "))
                txt += " ";
            r.adjust(fontMetrics().width(txt), 0, 0, 0);
        }

        QPainter painter(this);
        QColor color = palette().text().color();
        color.setAlpha(128);
        painter.setPen(color);

        QString hint = fontMetrics().elidedText(d.hint, Qt::ElideRight, r.width());
        painter.drawText(r, alignment(), hint);
    }
}

void TextInput::updateHint(const QString& text)
{
    QString match;
    QStringList params;
    QStringList suggestions;
    if (d.parser) {
        if (text.startsWith('/')) {
            QStringList words = text.mid(1).split(" ");
            QString command = words.value(0);
            params = words.mid(1);
            foreach (const QString& available, d.parser->commands()) {
                if (!command.compare(available, Qt::CaseInsensitive)) {
                    match = available;
                    break;
                } else if (params.isEmpty() && available.startsWith(command, Qt::CaseInsensitive)) {
                    suggestions += available;
                }
            }
        }
    }

    if (!match.isEmpty()) {
        QStringList syntax = d.parser->syntax(match).split(" ", QString::SkipEmptyParts).mid(1);
        if (!params.isEmpty())
            d.hint = QStringList(syntax.mid(params.count() - 1)).join(" ");
        else
            d.hint = syntax.join(" ");
    } else if (suggestions.isEmpty()) {
        d.hint = text.isEmpty() ? "..." : "";
    } else if (suggestions.count() == 1) {
        d.hint = d.parser->syntax(suggestions.first());
    } else {
        d.hint = suggestions.join(" ");
    }
}

void TextInput::goBackward()
{
    if (!text().isEmpty() && !d.history.contains(text()))
        d.current = text();
    if (d.index > 0)
        setText(d.history.value(--d.index));
}

void TextInput::goForward()
{
    if (d.index < d.history.count())
        setText(d.history.value(++d.index));
    if (text().isEmpty())
        setText(d.current);
}

void TextInput::sendInput()
{
    IrcBuffer* b = buffer();
    IrcCommandParser* p = parser();
    IrcConnection* c = b ? b->connection() : 0;
    if (!c || !p)
        return;

    const QStringList lines = text().split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts);
#if QT_VERSION >= 0x050200
    if (lines.count() > 2) {
        QSettings settings;
        bool warn = settings.value("warn", true).toBool();
        if (warn) {
            QMessageBox msgBox;
            msgBox.setText(tr("The input contains more than two lines."));
            msgBox.setInformativeText(tr("IRC is not a suitable medium for pasting multiple lines of text. Consider using a pastebin site instead.\n\n"
                                         "Do you still want to proceed and send %1 lines of text?\n").arg(lines.count()));
            msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            msgBox.setDefaultButton(QMessageBox::No);
            QCheckBox* checkBox = new QCheckBox(tr("Do not show again"), &msgBox);
            msgBox.setCheckBox(checkBox);
            int res = msgBox.exec();
            settings.setValue("warn", !checkBox->isChecked());
            if (res != QMessageBox::Yes)
                return;
        }
    }
#endif

    if (!text().isEmpty()) {
        d.current.clear();
        d.history.append(text());
        d.index = d.history.count();
    }

    bool error = false;
    foreach (const QString& line, lines) {
        if (!line.trimmed().isEmpty()) {
            IrcCommand* cmd = p->parse(line);
            if (cmd) {
                cmd->setProperty("TextInput", true);
                b->sendCommand(cmd);
                if (cmd->type() == IrcCommand::Message || cmd->type() == IrcCommand::Notice || cmd->type() == IrcCommand::CtcpAction) {
                    IrcMessage* msg = cmd->toMessage(c->nickName(), c);
                    if (msg) {
                        b->receiveMessage(msg);
                        msg->deleteLater();
                    }
                }
            } else {
                error = true;
            }
        }
    }
    if (!error)
        clear();
}

void TextInput::tryComplete(IrcCompleter::Direction direction)
{
    d.completer->complete(text(), cursorPosition(), direction);
}

void TextInput::doComplete(const QString& text, int cursor)
{
    setText(text);
    setCursorPosition(cursor);
}

QByteArray TextInput::saveState() const
{
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << d.index << d.current << d.history;
    out << text() << cursorPosition() << selectionStart() << selectedText().length();
    return data;
}

void TextInput::restoreState(const QByteArray& state)
{
    QDataStream in(state);
    in >> d.index >> d.current >> d.history;
    QString txt;
    int pos, start, len;
    in >> txt >> pos >> start >> len;
    setText(txt);
    setCursorPosition(pos);
    if (start != -1)
        setSelection(start, len);
}
<commit_msg>Replace deprecated QStyleOptionFrameV2 with QStyleOptionFrame<commit_after>/*
  Copyright (C) 2008-2016 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "textinput.h"
#include <QStyleOptionFrame>
#include <IrcCommandParser>
#include <IrcBufferModel>
#include <IrcConnection>
#include <QMessageBox>
#include <QSettings>
#include <QCheckBox>
#include <IrcBuffer>
#include <QKeyEvent>
#include <QPainter>

TextInput::TextInput(QWidget* parent) : QLineEdit(parent)
{
    setAttribute(Qt::WA_MacShowFocusRect, false);

    d.hint = "...";
    d.index = 0;
    d.buffer = 0;
    d.parser = 0;

    d.completer = new IrcCompleter(this);
    connect(this, SIGNAL(bufferChanged(IrcBuffer*)), d.completer, SLOT(setBuffer(IrcBuffer*)));
    connect(this, SIGNAL(parserChanged(IrcCommandParser*)), d.completer, SLOT(setParser(IrcCommandParser*)));
    connect(d.completer, SIGNAL(completed(QString,int)), this, SLOT(doComplete(QString,int)));
    connect(this, SIGNAL(textEdited(QString)), d.completer, SLOT(reset()));

    connect(this, SIGNAL(returnPressed()), this, SLOT(sendInput()));
    connect(this, SIGNAL(textChanged(QString)), this, SLOT(updateHint(QString)));
}

IrcBuffer* TextInput::buffer() const
{
    return d.buffer;
}

IrcCommandParser* TextInput::parser() const
{
    return d.parser;
}

static void bind(IrcBuffer* buffer, IrcCommandParser* parser)
{
    if (buffer && parser) {
        IrcBufferModel* model = buffer->model();
        QObject::connect(model, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList)));
        QObject::connect(buffer, SIGNAL(titleChanged(QString)), parser, SLOT(setTarget(QString)));

        parser->setTarget(buffer->title());
        parser->setChannels(buffer->model()->channels());
    } else if (parser) {
        parser->reset();
    }
}

static void unbind(IrcBuffer* buffer, IrcCommandParser* parser)
{
    if (buffer && parser) {
        IrcBufferModel* model = buffer->model();
        QObject::disconnect(model, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList)));
        QObject::disconnect(buffer, SIGNAL(titleChanged(QString)), parser, SLOT(setTarget(QString)));
    }
}

void TextInput::setBuffer(IrcBuffer* buffer)
{
    if (d.buffer != buffer) {
        unbind(d.buffer, d.parser);
        bind(buffer, d.parser);
        if (d.buffer)
            d.states.insert(d.buffer, saveState());
        d.buffer = buffer;
        if (buffer)
            restoreState(d.states.value(buffer));
        emit bufferChanged(buffer);
    }
}

void TextInput::setParser(IrcCommandParser* parser)
{
    if (d.parser != parser) {
        unbind(d.buffer, d.parser);
        bind(d.buffer, parser);
        d.parser = parser;
        emit parserChanged(parser);
    }
}

bool TextInput::event(QEvent* event)
{
    if (event->type() == QEvent::KeyPress) {
        switch (static_cast<QKeyEvent*>(event)->key()) {
        case Qt::Key_Tab:
            tryComplete(IrcCompleter::Forward);
            return true;
        case Qt::Key_Backtab:
            tryComplete(IrcCompleter::Backward);
            return true;
        case Qt::Key_Up:
            goBackward();
            return true;
        case Qt::Key_Down:
            goForward();
            return true;
        default:
            break;
        }
    }
    return QLineEdit::event(event);
}

// copied from qlineedit.cpp:
#define vMargin 1
#define hMargin 2

void TextInput::paintEvent(QPaintEvent* event)
{
    QLineEdit::paintEvent(event);

    if (!d.hint.isEmpty()) {
        QStyleOptionFrame option;
        initStyleOption(&option);

        QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &option, this);
        int left, top, right, bottom;
        getTextMargins(&left, &top, &right, &bottom);
        left += qMax(0, -fontMetrics().minLeftBearing());
        r.adjust(left, top, -right, -bottom);
        r.adjust(hMargin, vMargin, -hMargin, -vMargin);

        QString txt = text();
        if (!txt.isEmpty()) {
            if (!txt.endsWith(" "))
                txt += " ";
            r.adjust(fontMetrics().width(txt), 0, 0, 0);
        }

        QPainter painter(this);
        QColor color = palette().text().color();
        color.setAlpha(128);
        painter.setPen(color);

        QString hint = fontMetrics().elidedText(d.hint, Qt::ElideRight, r.width());
        painter.drawText(r, alignment(), hint);
    }
}

void TextInput::updateHint(const QString& text)
{
    QString match;
    QStringList params;
    QStringList suggestions;
    if (d.parser) {
        if (text.startsWith('/')) {
            QStringList words = text.mid(1).split(" ");
            QString command = words.value(0);
            params = words.mid(1);
            foreach (const QString& available, d.parser->commands()) {
                if (!command.compare(available, Qt::CaseInsensitive)) {
                    match = available;
                    break;
                } else if (params.isEmpty() && available.startsWith(command, Qt::CaseInsensitive)) {
                    suggestions += available;
                }
            }
        }
    }

    if (!match.isEmpty()) {
        QStringList syntax = d.parser->syntax(match).split(" ", QString::SkipEmptyParts).mid(1);
        if (!params.isEmpty())
            d.hint = QStringList(syntax.mid(params.count() - 1)).join(" ");
        else
            d.hint = syntax.join(" ");
    } else if (suggestions.isEmpty()) {
        d.hint = text.isEmpty() ? "..." : "";
    } else if (suggestions.count() == 1) {
        d.hint = d.parser->syntax(suggestions.first());
    } else {
        d.hint = suggestions.join(" ");
    }
}

void TextInput::goBackward()
{
    if (!text().isEmpty() && !d.history.contains(text()))
        d.current = text();
    if (d.index > 0)
        setText(d.history.value(--d.index));
}

void TextInput::goForward()
{
    if (d.index < d.history.count())
        setText(d.history.value(++d.index));
    if (text().isEmpty())
        setText(d.current);
}

void TextInput::sendInput()
{
    IrcBuffer* b = buffer();
    IrcCommandParser* p = parser();
    IrcConnection* c = b ? b->connection() : 0;
    if (!c || !p)
        return;

    const QStringList lines = text().split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts);
#if QT_VERSION >= 0x050200
    if (lines.count() > 2) {
        QSettings settings;
        bool warn = settings.value("warn", true).toBool();
        if (warn) {
            QMessageBox msgBox;
            msgBox.setText(tr("The input contains more than two lines."));
            msgBox.setInformativeText(tr("IRC is not a suitable medium for pasting multiple lines of text. Consider using a pastebin site instead.\n\n"
                                         "Do you still want to proceed and send %1 lines of text?\n").arg(lines.count()));
            msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            msgBox.setDefaultButton(QMessageBox::No);
            QCheckBox* checkBox = new QCheckBox(tr("Do not show again"), &msgBox);
            msgBox.setCheckBox(checkBox);
            int res = msgBox.exec();
            settings.setValue("warn", !checkBox->isChecked());
            if (res != QMessageBox::Yes)
                return;
        }
    }
#endif

    if (!text().isEmpty()) {
        d.current.clear();
        d.history.append(text());
        d.index = d.history.count();
    }

    bool error = false;
    foreach (const QString& line, lines) {
        if (!line.trimmed().isEmpty()) {
            IrcCommand* cmd = p->parse(line);
            if (cmd) {
                cmd->setProperty("TextInput", true);
                b->sendCommand(cmd);
                if (cmd->type() == IrcCommand::Message || cmd->type() == IrcCommand::Notice || cmd->type() == IrcCommand::CtcpAction) {
                    IrcMessage* msg = cmd->toMessage(c->nickName(), c);
                    if (msg) {
                        b->receiveMessage(msg);
                        msg->deleteLater();
                    }
                }
            } else {
                error = true;
            }
        }
    }
    if (!error)
        clear();
}

void TextInput::tryComplete(IrcCompleter::Direction direction)
{
    d.completer->complete(text(), cursorPosition(), direction);
}

void TextInput::doComplete(const QString& text, int cursor)
{
    setText(text);
    setCursorPosition(cursor);
}

QByteArray TextInput::saveState() const
{
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << d.index << d.current << d.history;
    out << text() << cursorPosition() << selectionStart() << selectedText().length();
    return data;
}

void TextInput::restoreState(const QByteArray& state)
{
    QDataStream in(state);
    in >> d.index >> d.current >> d.history;
    QString txt;
    int pos, start, len;
    in >> txt >> pos >> start >> len;
    setText(txt);
    setCursorPosition(pos);
    if (start != -1)
        setSelection(start, len);
}
<|endoftext|>


<commit_before>#include "catch.hpp"
#include <iostream>
#include "Database_Creator.hpp"
#include "Database_Sorter.hpp"

SCENARIO("Sort", "[s]") {
	Database_Creator database_creator(3000);
	Database_Sorter database_sorter("F:\\1\\database.txt", "F:\\1\\sorted_database.txt", 4000);
	database_sorter.sortDatabase();
}
<commit_msg>Update init.cpp<commit_after>#include "catch.hpp"
#include <iostream>
#include "Database_Creator.hpp"
#include "Database_Sorter.hpp"

person readPerson(std::string file_name, size_t index) {
	person result;
	std::ifstream file(file_name);
	for (size_t i = 0; i < index + 1; i++) {
		file >> result;
	}
	file.close();
	return result;
}
bool isALessThanB(char A, char B, bool is_capital) {
	std::string alphabet;
	if (is_capital) {
		alphabet = " АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
	}
	else {
		alphabet = " абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	}
	size_t A_i;
	size_t B_i;
	for (size_t i = 0; i < alphabet.size(); i++) {
		if (alphabet[i] == A) {
			A_i = i;
		}
		if (alphabet[i] == B) {
			B_i = i;
		}
	}
	return A_i < B_i;
}
bool isANotMoreThanB(person A, person B) {
	for (size_t i = 0; i < A.surname.length(); i++) {
		char A_char = A.surname[i];
		char B_char = (i < B.surname.length()) ? B.surname[i] : ' ';
		if (isALessThanB(A.surname[i], B.surname[i], i == 0)) {
			return true;
		}
		else {
			if (A.surname[i] != B.surname[i]) {
				return false;
			}
		}
	}
	return true;
}

SCENARIO("Sort", "[s]") {
	size_t n_persons = 10000000;
	size_t RAM_amount = 40 * 1000 * 1000;
	std::string names_file_name = "F:\\1\\names.txt";
	std::string surnames_file_name = "F:\\1\\surnames.txt";
	std::string database_file_name = "F:\\1\\database.txt";
	std::string output_file_name = "F:\\1\\sorted_database.txt";
	{
		//Database_Creator database_creator(database_file_name, names_file_name, surnames_file_name, n_persons);
		Database_Sorter database_sorter(database_file_name, output_file_name, RAM_amount);
		size_t start = clock();
		database_sorter.sortDatabase();
		size_t result = clock() - start;
		std::cout << result << std::endl;
		system("pause");
	}
	for (size_t i = 0; i < 0; i++) {
		size_t person1_i = rand() % n_persons;
		size_t person2_i = person1_i + rand() % (n_persons - person1_i);
		person person1 = readPerson(output_file_name, person1_i);
		person person2 = readPerson(output_file_name, person2_i);
		REQUIRE(isANotMoreThanB(person1, person2));
	}
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2012 Juergen Riegel <FreeCAD@juergen-riegel.net>        *
 *   Copyright (c) 2013 Stefan Tröger  <stefantroeger@gmx.net>             *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development m_solvertem.         *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
#endif

#include <Standard_Failure.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <GeomAbs_SurfaceType.hxx>
#include <gp_Pln.hxx>
#include <GeomAbs_CurveType.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS_Vertex.hxx>
#include <gp_Pnt.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <TopoDS_Edge.hxx>
#include <gp_Cylinder.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <TopoDS_Face.hxx>

#include <Base/Placement.h>
#include <Base/Console.h>

#include "Constraint.h"
#include "ConstraintPy.h"
#include "Item.h"
#include "ItemPart.h"
#include "ItemAssembly.h"

using namespace Assembly;

namespace Assembly {

PROPERTY_SOURCE(Assembly::Constraint, App::DocumentObject)

Constraint::Constraint()
{
    ADD_PROPERTY(First, (0));
    ADD_PROPERTY(Second,(0));
}

short Constraint::mustExecute() const
{
    //if (Sketch.isTouched() ||
    //    Length.isTouched())
    //    return 1;
    return 0;
}

App::DocumentObjectExecReturn *Constraint::execute(void)
{
    touch();
    return App::DocumentObject::StdReturn;
}

boost::shared_ptr<Geometry3D> Constraint::initLink(App::PropertyLinkSub& link) {

    //check if we have Assembly::ItemPart
    if( link.getValue()->getTypeId() != ItemPart::getClassTypeId() ) {
      Base::Console().Message("Link is not ItemPart, the constraint is invalid\n");
      return boost::shared_ptr<Geometry3D>();
    };

    Assembly::ItemPart* part = static_cast<Assembly::ItemPart*>(link.getValue());
    if(!part)
      return boost::shared_ptr<Geometry3D>();

    //get the relevant solver in which the part needs to be added
    part->ensureInitialisation();

    return part->getGeometry3D(link.getSubValues()[0].c_str());
}

void Constraint::init(Assembly::ItemAssembly* ass)
{
    m_first_geom = initLink(First);
    m_second_geom = initLink(Second);
}

PyObject *Constraint::getPyObject(void)
{
    if (PythonObject.is(Py::_None())){
        // ref counter is set to 1
        PythonObject = Py::Object(new ConstraintPy(this),true);
    }
    return Py::new_reference_to(PythonObject);
}

}

<commit_msg>throw exception at unsuported geometrie instead of crashing<commit_after>/***************************************************************************
 *   Copyright (c) 2012 Juergen Riegel <FreeCAD@juergen-riegel.net>        *
 *   Copyright (c) 2013 Stefan Tröger  <stefantroeger@gmx.net>             *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development m_solvertem.         *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
#endif

#include <Standard_Failure.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <GeomAbs_SurfaceType.hxx>
#include <gp_Pln.hxx>
#include <GeomAbs_CurveType.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS_Vertex.hxx>
#include <gp_Pnt.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <TopoDS_Edge.hxx>
#include <gp_Cylinder.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <TopoDS_Face.hxx>

#include <Base/Placement.h>
#include <Base/Console.h>

#include "Constraint.h"
#include "ConstraintPy.h"
#include "Item.h"
#include "ItemPart.h"
#include "ItemAssembly.h"

using namespace Assembly;

namespace Assembly {

struct ConstraintInitException : std::exception {
  const char* what() const throw() { return "Constraint cout not be initialised: unsoported geometry";}
};

PROPERTY_SOURCE(Assembly::Constraint, App::DocumentObject)

Constraint::Constraint()
{
    ADD_PROPERTY(First, (0));
    ADD_PROPERTY(Second,(0));
}

short Constraint::mustExecute() const
{
    //if (Sketch.isTouched() ||
    //    Length.isTouched())
    //    return 1;
    return 0;
}

App::DocumentObjectExecReturn *Constraint::execute(void)
{
    touch();
    return App::DocumentObject::StdReturn;
}

boost::shared_ptr<Geometry3D> Constraint::initLink(App::PropertyLinkSub& link) {

    //check if we have Assembly::ItemPart
    if( link.getValue()->getTypeId() != ItemPart::getClassTypeId() ) {
      Base::Console().Message("Link is not ItemPart, the constraint is invalid\n");
      return boost::shared_ptr<Geometry3D>();
    };

    Assembly::ItemPart* part = static_cast<Assembly::ItemPart*>(link.getValue());
    if(!part)
      return boost::shared_ptr<Geometry3D>();

    //get the relevant solver in which the part needs to be added
    part->ensureInitialisation();

    return part->getGeometry3D(link.getSubValues()[0].c_str());
}

void Constraint::init(Assembly::ItemAssembly* ass)
{
    m_first_geom = initLink(First);
    m_second_geom = initLink(Second);

    if(!m_first_geom || !m_second_geom)
      throw ConstraintInitException();
}

PyObject *Constraint::getPyObject(void)
{
    if (PythonObject.is(Py::_None())){
        // ref counter is set to 1
        PythonObject = Py::Object(new ConstraintPy(this),true);
    }
    return Py::new_reference_to(PythonObject);
}

}

<|endoftext|>


<commit_before>// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/SkeletalMesh.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/Skeleton.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <vector>
#include <Nazara/Utility/Debug.hpp>

struct NzSkeletalMeshImpl
{
	std::vector<NzVertexWeight> vertexWeights;
	std::vector<NzWeight> weights;
	NzAxisAlignedBox aabb;
	nzUInt8* bindPoseBuffer;
	const NzIndexBuffer* indexBuffer = nullptr;
	NzVertexBuffer* vertexBuffer;
};

NzSkeletalMesh::NzSkeletalMesh(const NzMesh* parent) :
NzSubMesh(parent)
{
}

NzSkeletalMesh::~NzSkeletalMesh()
{
	Destroy();
}

bool NzSkeletalMesh::Create(NzVertexBuffer* vertexBuffer, unsigned int weightCount)
{
	Destroy();

	#if NAZARA_UTILITY_SAFE
	if (!vertexBuffer)
	{
		NazaraError("Invalid vertex buffer");
		return false;
	}

	if (weightCount == 0)
	{
		NazaraError("Weight count must be over 0");
		return false;
	}
	#endif

	vertexBuffer->AddResourceReference();

	unsigned int vertexCount = vertexBuffer->GetVertexCount();

	m_impl = new NzSkeletalMeshImpl;
	m_impl->bindPoseBuffer = new nzUInt8[vertexCount*vertexBuffer->GetTypeSize()];
	m_impl->vertexBuffer = vertexBuffer;
	m_impl->vertexWeights.resize(vertexCount);
	m_impl->weights.resize(weightCount);

	return true;
}

void NzSkeletalMesh::Destroy()
{
	if (m_impl)
	{
		if (m_impl->indexBuffer)
			m_impl->indexBuffer->RemoveResourceReference();

		if (m_impl->vertexBuffer)
			m_impl->vertexBuffer->RemoveResourceReference();

		delete[] m_impl->bindPoseBuffer;
		delete m_impl;
		m_impl = nullptr;
	}
}

void NzSkeletalMesh::Finish()
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	Skin();
}

const NzAxisAlignedBox& NzSkeletalMesh::GetAABB() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return NzAxisAlignedBox::Null;
	}
	#endif

	return m_impl->aabb;
}

nzAnimationType NzSkeletalMesh::GetAnimationType() const
{
	return nzAnimationType_Skeletal;
}

void* NzSkeletalMesh::GetBindPoseBuffer()
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->bindPoseBuffer;
}

const void* NzSkeletalMesh::GetBindPoseBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->bindPoseBuffer;
}

const NzIndexBuffer* NzSkeletalMesh::GetIndexBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->indexBuffer;
}

const NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->vertexBuffer;
}

NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex)
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->vertexWeights[vertexIndex];
}

const NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->vertexWeights[vertexIndex];
}

NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex)
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->weights[weightIndex];
}

const NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->weights[weightIndex];
}

unsigned int NzSkeletalMesh::GetWeightCount() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return 0;
	}
	#endif

	return m_impl->weights.size();
}

bool NzSkeletalMesh::IsAnimated() const
{
	return true;
}

bool NzSkeletalMesh::IsValid() const
{
	return m_impl != nullptr;
}

void NzSkeletalMesh::Skin() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	Skin(m_parent->GetSkeleton());
}

void NzSkeletalMesh::Skin(const NzSkeleton* skeleton) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	void* outputBuffer = m_impl->vertexBuffer->Map(nzBufferAccess_DiscardAndWrite);
	if (!outputBuffer)
	{
		NazaraError("Failed to map vertex buffer");
		return;
	}

	NzVertexStruct_XYZ_Normal_UV_Tangent* inputVertex = reinterpret_cast<NzVertexStruct_XYZ_Normal_UV_Tangent*>(m_impl->bindPoseBuffer);
	NzVertexStruct_XYZ_Normal_UV_Tangent* outputVertex = reinterpret_cast<NzVertexStruct_XYZ_Normal_UV_Tangent*>(outputBuffer);

	const NzJoint* joints = skeleton->GetJoints();
	unsigned int vertexCount = m_impl->vertexBuffer->GetVertexCount();
	for (unsigned int i = 0; i < vertexCount; ++i)
	{
		NzVector3f finalPosition(NzVector3f::Zero());
		NzVector3f finalNormal(NzVector3f::Zero());
		NzVector3f finalTangent(NzVector3f::Zero());

		unsigned int weightCount = m_impl->vertexWeights[i].weights.size();
		for (unsigned int j = 0; j < weightCount; ++j)
		{
			const NzWeight& weight = m_impl->weights[m_impl->vertexWeights[i].weights[j]];

			NzMatrix4f mat(joints[weight.jointIndex].GetInverseBindMatrix());
			mat.ConcatenateAffine(joints[weight.jointIndex].GetTransformMatrix());
			mat *= weight.weight;

			finalPosition += mat.Transform(inputVertex->position);
			finalNormal += mat.Transform(inputVertex->normal, 0.f);
			finalTangent += mat.Transform(inputVertex->tangent, 0.f);
		}

		finalNormal.Normalize();
		finalTangent.Normalize();

		outputVertex->normal = finalNormal;
		outputVertex->position = finalPosition;
		outputVertex->tangent = finalTangent;
		outputVertex->uv = inputVertex->uv;

		inputVertex++;
		outputVertex++;
	}

	if (!m_impl->vertexBuffer->Unmap())
		NazaraWarning("Failed to unmap vertex buffer");

	m_impl->aabb = skeleton->GetAABB();
}

void NzSkeletalMesh::SetIndexBuffer(const NzIndexBuffer* indexBuffer)
{
	if (m_impl->indexBuffer)
		m_impl->indexBuffer->RemoveResourceReference();

	if (indexBuffer)
		indexBuffer->AddResourceReference();

	m_impl->indexBuffer = indexBuffer;
}
<commit_msg>Made skinning vertex access clearer<commit_after>// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/SkeletalMesh.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/Skeleton.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <vector>
#include <Nazara/Utility/Debug.hpp>

struct NzSkeletalMeshImpl
{
	std::vector<NzVertexWeight> vertexWeights;
	std::vector<NzWeight> weights;
	NzAxisAlignedBox aabb;
	nzUInt8* bindPoseBuffer;
	const NzIndexBuffer* indexBuffer = nullptr;
	NzVertexBuffer* vertexBuffer;
};

NzSkeletalMesh::NzSkeletalMesh(const NzMesh* parent) :
NzSubMesh(parent)
{
}

NzSkeletalMesh::~NzSkeletalMesh()
{
	Destroy();
}

bool NzSkeletalMesh::Create(NzVertexBuffer* vertexBuffer, unsigned int weightCount)
{
	Destroy();

	#if NAZARA_UTILITY_SAFE
	if (!vertexBuffer)
	{
		NazaraError("Invalid vertex buffer");
		return false;
	}

	if (weightCount == 0)
	{
		NazaraError("Weight count must be over 0");
		return false;
	}
	#endif

	vertexBuffer->AddResourceReference();

	unsigned int vertexCount = vertexBuffer->GetVertexCount();

	m_impl = new NzSkeletalMeshImpl;
	m_impl->bindPoseBuffer = new nzUInt8[vertexCount*vertexBuffer->GetTypeSize()];
	m_impl->vertexBuffer = vertexBuffer;
	m_impl->vertexWeights.resize(vertexCount);
	m_impl->weights.resize(weightCount);

	return true;
}

void NzSkeletalMesh::Destroy()
{
	if (m_impl)
	{
		if (m_impl->indexBuffer)
			m_impl->indexBuffer->RemoveResourceReference();

		if (m_impl->vertexBuffer)
			m_impl->vertexBuffer->RemoveResourceReference();

		delete[] m_impl->bindPoseBuffer;
		delete m_impl;
		m_impl = nullptr;
	}
}

void NzSkeletalMesh::Finish()
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	Skin();
}

const NzAxisAlignedBox& NzSkeletalMesh::GetAABB() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return NzAxisAlignedBox::Null;
	}
	#endif

	return m_impl->aabb;
}

nzAnimationType NzSkeletalMesh::GetAnimationType() const
{
	return nzAnimationType_Skeletal;
}

void* NzSkeletalMesh::GetBindPoseBuffer()
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->bindPoseBuffer;
}

const void* NzSkeletalMesh::GetBindPoseBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->bindPoseBuffer;
}

const NzIndexBuffer* NzSkeletalMesh::GetIndexBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->indexBuffer;
}

const NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return m_impl->vertexBuffer;
}

NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex)
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->vertexWeights[vertexIndex];
}

const NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->vertexWeights[vertexIndex];
}

NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex)
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->weights[weightIndex];
}

const NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return nullptr;
	}
	#endif

	return &m_impl->weights[weightIndex];
}

unsigned int NzSkeletalMesh::GetWeightCount() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return 0;
	}
	#endif

	return m_impl->weights.size();
}

bool NzSkeletalMesh::IsAnimated() const
{
	return true;
}

bool NzSkeletalMesh::IsValid() const
{
	return m_impl != nullptr;
}

void NzSkeletalMesh::Skin() const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	Skin(m_parent->GetSkeleton());
}

void NzSkeletalMesh::Skin(const NzSkeleton* skeleton) const
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Skeletal mesh not created");
		return;
	}
	#endif

	void* outputBuffer = m_impl->vertexBuffer->Map(nzBufferAccess_DiscardAndWrite);
	if (!outputBuffer)
	{
		NazaraError("Failed to map vertex buffer");
		return;
	}

	NzMeshVertex* inputVertex = reinterpret_cast<NzMeshVertex*>(m_impl->bindPoseBuffer);
	NzMeshVertex* outputVertex = reinterpret_cast<NzMeshVertex*>(outputBuffer);

	const NzJoint* joints = skeleton->GetJoints();
	unsigned int vertexCount = m_impl->vertexBuffer->GetVertexCount();
	for (unsigned int i = 0; i < vertexCount; ++i)
	{
		NzVector3f finalPosition(NzVector3f::Zero());
		NzVector3f finalNormal(NzVector3f::Zero());
		NzVector3f finalTangent(NzVector3f::Zero());

		unsigned int weightCount = m_impl->vertexWeights[i].weights.size();
		for (unsigned int j = 0; j < weightCount; ++j)
		{
			const NzWeight& weight = m_impl->weights[m_impl->vertexWeights[i].weights[j]];

			NzMatrix4f mat(joints[weight.jointIndex].GetInverseBindMatrix());
			mat.ConcatenateAffine(joints[weight.jointIndex].GetTransformMatrix());
			mat *= weight.weight;

			finalPosition += mat.Transform(inputVertex->position);
			finalNormal += mat.Transform(inputVertex->normal, 0.f);
			finalTangent += mat.Transform(inputVertex->tangent, 0.f);
		}

		finalNormal.Normalize();
		finalTangent.Normalize();

		outputVertex->normal = finalNormal;
		outputVertex->position = finalPosition;
		outputVertex->tangent = finalTangent;
		outputVertex->uv = inputVertex->uv;

		inputVertex++;
		outputVertex++;
	}

	if (!m_impl->vertexBuffer->Unmap())
		NazaraWarning("Failed to unmap vertex buffer");

	m_impl->aabb = skeleton->GetAABB();
}

void NzSkeletalMesh::SetIndexBuffer(const NzIndexBuffer* indexBuffer)
{
	if (m_impl->indexBuffer)
		m_impl->indexBuffer->RemoveResourceReference();

	if (indexBuffer)
		indexBuffer->AddResourceReference();

	m_impl->indexBuffer = indexBuffer;
}
<|endoftext|>


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

#include "FilePrinter.hpp"

#include <srchilite/sourcehighlight.h>
#include <srchilite/langmap.h>
#include <srchilite/lineranges.h>

#include <algorithm>
#include <deque>
#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>

#include "FileComparator.hpp"
#include "printing.hpp"

namespace {

int
countWidth(int n)
{
    int width = 1;
    while (n > 0) {
        n /= 10;
        ++width;
    }
    return width;
}

class CoverageColumn
{
    struct Blank
    {
        const CoverageColumn &cc;
    };
    struct LineAt
    {
        const CoverageColumn &cc;
        const int lineNo;
        const bool active;
    };

    friend std::ostream & operator<<(std::ostream &os, const Blank &blank)
    {
        blank.cc.printBlank(os);
        return os;
    }

    friend std::ostream & operator<<(std::ostream &os, const LineAt &lineAt)
    {
        lineAt.cc.printAt(os, lineAt.lineNo, lineAt.active);
        return os;
    }

public:
    explicit CoverageColumn(const std::vector<int> &coverage)
        : coverage(coverage)
    {
        const int MinHitsNumWidth = 5;
        // XXX: this could in principle be stored in database.
        int maxHits = 0;
        if (!coverage.empty()) {
            maxHits = *std::max_element(coverage.cbegin(), coverage.cend());
        }
        hitsNumWidth = std::max(MinHitsNumWidth, countWidth(maxHits));
    }

public:
    Blank blank() const
    {
        return { *this };
    }

    LineAt active(int lineNo) const
    {
        return { *this, lineNo, true };
    }

    LineAt inactive(int lineNo) const
    {
        return { *this, lineNo, false };
    }

private:
    void printBlank(std::ostream &os) const
    {
        os << std::setw(hitsNumWidth) << "" << ' ';
    }

    void printAt(std::ostream &os, std::size_t lineNo, bool active) const
    {
        if (lineNo >= coverage.size()) {
            os << std::setw(hitsNumWidth) << ErrorMsg{"ERROR "};
            return;
        }

        os << std::setw(hitsNumWidth);
        if (active) {
            os << HitsCount{coverage[lineNo]};
        } else {
            os << SilentHitsCount{coverage[lineNo]};
        }
    }

private:
    const std::vector<int> &coverage;
    int hitsNumWidth;
};

}

FilePrinter::FilePrinter(const FilePrinterSettings &settings, bool allowColors)
    : colorizeOutput(allowColors && settings.isColorOutputAllowed()),
      highlighter(settings.isHtmlOutput() ? DATADIR "/srchilight/html.outlang"
                                          : "esc256.outlang"),
      langMap("lang.map")
{
    highlighter.setStyleFile(settings.isHtmlOutput() ? "default.style"
                                                     : "esc256.style");
    highlighter.setTabSpaces(settings.getTabSize());
}

void
FilePrinter::print(std::ostream &os, const std::string &path,
                   const std::string &contents,
                   const std::vector<int> &coverage, bool leaveMissedOnly)
{
    const int MinLineNoWidth = 5;

    const int nLines = coverage.size();
    const int lineNoWidth = std::max(MinLineNoWidth, countWidth(nLines));

    std::vector<int> lines;
    unsigned int uninterestingLines = 0U;
    auto foldUninteresting = [&lines, &uninterestingLines](bool last) {
        if (uninterestingLines > 4) {
            int startContext = (uninterestingLines == lines.size() ? 0 : 1);
            int endContext = last ? 0 : 1;
            int context = startContext + endContext;

            lines.erase(lines.cend() - (uninterestingLines - startContext),
                        lines.cend() - endContext);
            lines.insert(lines.cend() - endContext,
                         -(uninterestingLines - context));
        }
        uninterestingLines = 0U;
    };

    for (unsigned int i = 0U; i < coverage.size(); ++i) {
        if (leaveMissedOnly) {
            if (coverage[i] == 0) {
                foldUninteresting(false);
            } else {
                ++uninterestingLines;
            }
        }
        lines.push_back(i);
    }
    foldUninteresting(true);

    srchilite::LineRanges ranges;
    if (leaveMissedOnly) {
        std::size_t lineNo = 0U;
        for (int line : lines) {
            if (line < 0) {
                lineNo += -line;
            } else {
                ranges.addRange(std::to_string(line + 1));
                ++lineNo;
            }
        }
        ranges.addRange(std::to_string(lineNo + 1) + '-');
    }

    std::istringstream iss(contents);
    std::stringstream ss;
    highlight(ss, iss, getLang(path), leaveMissedOnly ? &ranges : nullptr);

    CoverageColumn covCol(coverage);
    std::size_t lineNo = 0U;

    for (int line : lines) {
        if (line < 0) {
            os << " <<< " << -line << " lines folded >>>\n";
            lineNo += -line;
        } else {
            std::string fileLine;
            if (!std::getline(ss, fileLine)) {
                // Not enough lines in the file.
                break;
            }

            os << std::setw(lineNoWidth) << LineNo{lineNo + 1U}
               << covCol.active(lineNo) << ": " << fileLine << '\n';
            ++lineNo;
        }
    }

    // Print extra file lines (with unknown coverage).
    for (std::string fileLine; std::getline(ss, fileLine); ++lineNo) {
        os << std::setw(lineNoWidth) << LineNo{lineNo + 1U}
           << covCol.active(lineNo) << ": " << fileLine << '\n';
    }

    if (lineNo < coverage.size()) {
        os << ErrorMsg{"ERROR"} << ": not enough lines in the file.\n";
    } else if (lineNo > coverage.size()) {
        os << ErrorMsg{"ERROR"} << ": too many lines in the file.\n";
    }
}

void
FilePrinter::printDiff(std::ostream &os, const std::string &path,
                       std::istream &oText, const std::vector<int> &oCov,
                       std::istream &nText, const std::vector<int> &nCov,
                       const FileComparator &comparator)
{
    const std::deque<DiffLine> &diff = comparator.getDiffSequence();

    srchilite::LineRanges fLines, sLines;
    for (const DiffLine &line : diff) {
        switch (line.type) {
            case DiffLineType::Added:
                sLines.addRange(std::to_string(line.newLine + 1));
                break;
            case DiffLineType::Removed:
            case DiffLineType::Common:
            case DiffLineType::Identical:
                fLines.addRange(std::to_string(line.oldLine + 1));
                break;
            case DiffLineType::Note:
                // Do nothing.
                break;
        }
    }

    const std::string &lang = getLang(path);
    std::stringstream fss, sss;
    highlight(fss, oText, lang, &fLines);
    highlight(sss, nText, lang, &sLines);

    auto getLine = [](std::stringstream &ss) {
        std::string line;
        std::getline(ss, line);
        return line;
    };

    CoverageColumn oldCovCol(oCov), newCovCol(nCov);
    for (const DiffLine &line : diff) {
        switch (line.type) {
            case DiffLineType::Added:
                os << oldCovCol.blank() << ':'
                   << newCovCol.active(line.newLine) << ':'
                   << LineAdded{getLine(sss)};
                break;
            case DiffLineType::Removed:
                os << oldCovCol.active(line.oldLine) << ':'
                   << newCovCol.blank() << ':'
                   << LineRemoved{getLine(fss)};
                break;
            case DiffLineType::Note:
                os << " <<< " + line.text + " >>>";
                break;
            case DiffLineType::Common:
                os << oldCovCol.active(line.oldLine) << ':'
                   << newCovCol.active(line.newLine) << ": "
                   << getLine(fss);
                break;
            case DiffLineType::Identical:
                os << oldCovCol.inactive(line.oldLine) << ':'
                   << newCovCol.inactive(line.newLine) << ": "
                   << getLine(fss);
                break;
        }
        os << '\n';
    }
}

std::string
FilePrinter::getLang(const std::string &path)
{
    std::string lang = langMap.getMappedFileNameFromFileName(path);
    if (lang.empty()) {
        lang = "cpp.lang";
    }
    return lang;
}

void
FilePrinter::highlight(std::stringstream &ss, std::istream &text,
                       const std::string &lang, srchilite::LineRanges *ranges)
{
    if (colorizeOutput) {
        highlighter.setLineRanges(ranges);
        highlighter.highlight(text, ss, lang);
    } else {
        ss << text.rdbuf();
    }
}
<commit_msg>Document anonymous entities of FilePrinter.cpp<commit_after>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// uncov is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with uncov.  If not, see <http://www.gnu.org/licenses/>.

#include "FilePrinter.hpp"

#include <srchilite/sourcehighlight.h>
#include <srchilite/langmap.h>
#include <srchilite/lineranges.h>

#include <algorithm>
#include <deque>
#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>

#include "FileComparator.hpp"
#include "printing.hpp"

namespace {

/**
 * @brief Counts width of a number in digit places.
 *
 * @param n Number to measure.
 *
 * @returns The width plus one (for convenience) for non-zero and zero for zero.
 */
int
countWidth(int n)
{
    int width = 1;
    while (n > 0) {
        n /= 10;
        ++width;
    }
    return width;
}

/**
 * @brief Auxiliary class that draws coverage column.
 */
class CoverageColumn
{
    //! Marker to print blank entry.
    struct Blank
    {
        const CoverageColumn &cc; //!< Reference to parent object.
    };
    //! Marker to print information of a specific line.
    struct LineAt
    {
        const CoverageColumn &cc; //!< Reference to parent object.
        const int lineNo;         //!< Line number.
        const bool active;        //!< Whether the entry should standout.
    };

    /**
     * @brief Redirects printing to an instance of CoverageColumn.
     *
     * @param os    Output stream.
     * @param blank Marker.
     *
     * @returns @p os.
     */
    friend std::ostream & operator<<(std::ostream &os, const Blank &blank)
    {
        blank.cc.printBlank(os);
        return os;
    }

    /**
     * @brief Redirects printing to an instance of CoverageColumn.
     *
     * @param os     Output stream.
     * @param lineAt Marker.
     *
     * @returns @p os.
     */
    friend std::ostream & operator<<(std::ostream &os, const LineAt &lineAt)
    {
        lineAt.cc.printAt(os, lineAt.lineNo, lineAt.active);
        return os;
    }

public:
    /**
     * @brief Constructs from coverage information.
     *
     * @param coverage Coverage information.
     */
    explicit CoverageColumn(const std::vector<int> &coverage)
        : coverage(coverage)
    {
        const int MinHitsNumWidth = 5;
        // XXX: this could in principle be stored in database.
        int maxHits = 0;
        if (!coverage.empty()) {
            maxHits = *std::max_element(coverage.cbegin(), coverage.cend());
        }
        hitsNumWidth = std::max(MinHitsNumWidth, countWidth(maxHits));
    }

public:
    /**
     * @brief Requests marker to print blank entry.
     *
     * @returns Marker to be output to @c std::ostream.
     */
    Blank blank() const
    {
        return { *this };
    }

    /**
     * @brief Requests marker to print active number of hits.
     *
     * @param lineNo
     *
     * @returns Marker to be output to @c std::ostream.
     */
    LineAt active(int lineNo) const
    {
        return { *this, lineNo, true };
    }

    /**
     * @brief Requests marker to print inactive number of hits.
     *
     * @param lineNo
     *
     * @returns Marker to be output to @c std::ostream.
     */
    LineAt inactive(int lineNo) const
    {
        return { *this, lineNo, false };
    }

private:
    /**
     * @brief Prints blank entry.
     *
     * @param os Output stream.
     */
    void printBlank(std::ostream &os) const
    {
        os << std::setw(hitsNumWidth) << "" << ' ';
    }

    /**
     * @brief Prints entry with number of hits.
     *
     * @param os     Output stream.
     * @param lineNo Line number.
     * @param active Whether number of hits should standout.
     */
    void printAt(std::ostream &os, std::size_t lineNo, bool active) const
    {
        if (lineNo >= coverage.size()) {
            os << std::setw(hitsNumWidth) << ErrorMsg{"ERROR "};
            return;
        }

        os << std::setw(hitsNumWidth);
        if (active) {
            os << HitsCount{coverage[lineNo]};
        } else {
            os << SilentHitsCount{coverage[lineNo]};
        }
    }

private:
    const std::vector<int> &coverage; //!< Coverage information.
    int hitsNumWidth;                 //!< Maximum width of number of hits.
};

}

FilePrinter::FilePrinter(const FilePrinterSettings &settings, bool allowColors)
    : colorizeOutput(allowColors && settings.isColorOutputAllowed()),
      highlighter(settings.isHtmlOutput() ? DATADIR "/srchilight/html.outlang"
                                          : "esc256.outlang"),
      langMap("lang.map")
{
    highlighter.setStyleFile(settings.isHtmlOutput() ? "default.style"
                                                     : "esc256.style");
    highlighter.setTabSpaces(settings.getTabSize());
}

void
FilePrinter::print(std::ostream &os, const std::string &path,
                   const std::string &contents,
                   const std::vector<int> &coverage, bool leaveMissedOnly)
{
    const int MinLineNoWidth = 5;

    const int nLines = coverage.size();
    const int lineNoWidth = std::max(MinLineNoWidth, countWidth(nLines));

    std::vector<int> lines;
    unsigned int uninterestingLines = 0U;
    auto foldUninteresting = [&lines, &uninterestingLines](bool last) {
        if (uninterestingLines > 4) {
            int startContext = (uninterestingLines == lines.size() ? 0 : 1);
            int endContext = last ? 0 : 1;
            int context = startContext + endContext;

            lines.erase(lines.cend() - (uninterestingLines - startContext),
                        lines.cend() - endContext);
            lines.insert(lines.cend() - endContext,
                         -(uninterestingLines - context));
        }
        uninterestingLines = 0U;
    };

    for (unsigned int i = 0U; i < coverage.size(); ++i) {
        if (leaveMissedOnly) {
            if (coverage[i] == 0) {
                foldUninteresting(false);
            } else {
                ++uninterestingLines;
            }
        }
        lines.push_back(i);
    }
    foldUninteresting(true);

    srchilite::LineRanges ranges;
    if (leaveMissedOnly) {
        std::size_t lineNo = 0U;
        for (int line : lines) {
            if (line < 0) {
                lineNo += -line;
            } else {
                ranges.addRange(std::to_string(line + 1));
                ++lineNo;
            }
        }
        ranges.addRange(std::to_string(lineNo + 1) + '-');
    }

    std::istringstream iss(contents);
    std::stringstream ss;
    highlight(ss, iss, getLang(path), leaveMissedOnly ? &ranges : nullptr);

    CoverageColumn covCol(coverage);
    std::size_t lineNo = 0U;

    for (int line : lines) {
        if (line < 0) {
            os << " <<< " << -line << " lines folded >>>\n";
            lineNo += -line;
        } else {
            std::string fileLine;
            if (!std::getline(ss, fileLine)) {
                // Not enough lines in the file.
                break;
            }

            os << std::setw(lineNoWidth) << LineNo{lineNo + 1U}
               << covCol.active(lineNo) << ": " << fileLine << '\n';
            ++lineNo;
        }
    }

    // Print extra file lines (with unknown coverage).
    for (std::string fileLine; std::getline(ss, fileLine); ++lineNo) {
        os << std::setw(lineNoWidth) << LineNo{lineNo + 1U}
           << covCol.active(lineNo) << ": " << fileLine << '\n';
    }

    if (lineNo < coverage.size()) {
        os << ErrorMsg{"ERROR"} << ": not enough lines in the file.\n";
    } else if (lineNo > coverage.size()) {
        os << ErrorMsg{"ERROR"} << ": too many lines in the file.\n";
    }
}

void
FilePrinter::printDiff(std::ostream &os, const std::string &path,
                       std::istream &oText, const std::vector<int> &oCov,
                       std::istream &nText, const std::vector<int> &nCov,
                       const FileComparator &comparator)
{
    const std::deque<DiffLine> &diff = comparator.getDiffSequence();

    srchilite::LineRanges fLines, sLines;
    for (const DiffLine &line : diff) {
        switch (line.type) {
            case DiffLineType::Added:
                sLines.addRange(std::to_string(line.newLine + 1));
                break;
            case DiffLineType::Removed:
            case DiffLineType::Common:
            case DiffLineType::Identical:
                fLines.addRange(std::to_string(line.oldLine + 1));
                break;
            case DiffLineType::Note:
                // Do nothing.
                break;
        }
    }

    const std::string &lang = getLang(path);
    std::stringstream fss, sss;
    highlight(fss, oText, lang, &fLines);
    highlight(sss, nText, lang, &sLines);

    auto getLine = [](std::stringstream &ss) {
        std::string line;
        std::getline(ss, line);
        return line;
    };

    CoverageColumn oldCovCol(oCov), newCovCol(nCov);
    for (const DiffLine &line : diff) {
        switch (line.type) {
            case DiffLineType::Added:
                os << oldCovCol.blank() << ':'
                   << newCovCol.active(line.newLine) << ':'
                   << LineAdded{getLine(sss)};
                break;
            case DiffLineType::Removed:
                os << oldCovCol.active(line.oldLine) << ':'
                   << newCovCol.blank() << ':'
                   << LineRemoved{getLine(fss)};
                break;
            case DiffLineType::Note:
                os << " <<< " + line.text + " >>>";
                break;
            case DiffLineType::Common:
                os << oldCovCol.active(line.oldLine) << ':'
                   << newCovCol.active(line.newLine) << ": "
                   << getLine(fss);
                break;
            case DiffLineType::Identical:
                os << oldCovCol.inactive(line.oldLine) << ':'
                   << newCovCol.inactive(line.newLine) << ": "
                   << getLine(fss);
                break;
        }
        os << '\n';
    }
}

std::string
FilePrinter::getLang(const std::string &path)
{
    std::string lang = langMap.getMappedFileNameFromFileName(path);
    if (lang.empty()) {
        lang = "cpp.lang";
    }
    return lang;
}

void
FilePrinter::highlight(std::stringstream &ss, std::istream &text,
                       const std::string &lang, srchilite::LineRanges *ranges)
{
    if (colorizeOutput) {
        highlighter.setLineRanges(ranges);
        highlighter.highlight(text, ss, lang);
    } else {
        ss << text.rdbuf();
    }
}
<|endoftext|>


<commit_before>#include "FrameBuffer.h"

#include "Texture2D.h"

FrameBuffer::FrameBuffer()
{
	glGenFramebuffers(1, &frameBufferHandle);
}

FrameBuffer::~FrameBuffer()
{
	glDeleteFramebuffers(1, &frameBufferHandle);
}

void FrameBuffer::Attach(const Texture2D *texture, GLenum attachment)
{
	// Check sizes to see if they match. It's not a requirement, but I have a feeling this will save me from some bugs...
	if (attachments.size() != 0)
	{
		const auto first = attachments.begin()->second;
		assert(first->GetWidth() == texture->GetWidth());
		assert(first->GetHeight() == texture->GetHeight());
	}

	BindAsDrawFrameBuffer(false, false);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture->GetTextureHandle(), 0);

	// Store attachment textures and draw buffers
	attachments[attachment] = texture;
	if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT31)
	{
		drawBuffers.emplace_back(attachment);
	}
}

bool FrameBuffer::IsComplete(GLenum *statusIfNotComplete) const
{
	BindAsDrawFrameBuffer(false, false);

	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if (status != GL_FRAMEBUFFER_COMPLETE)
	{
		if (statusIfNotComplete != nullptr)
		{
			*statusIfNotComplete = status;
		}

		return false;
	}

	return true;
}

void FrameBuffer::BindAsDrawFrameBuffer(bool setViewport, bool setDrawBuffers) const
{
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferHandle);

	if (setViewport)
	{
		const auto first = attachments.begin()->second;
		glViewport(0, 0, first->GetWidth(), first->GetHeight());
	}

	if (setDrawBuffers && drawBuffers.size() > 0)
	{
		glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);
	}
}

void FrameBuffer::BindAsReadFrameBuffer() const
{
	glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferHandle);
}

const Texture2D *FrameBuffer::GetAttached(GLenum attachment) const
{
	auto result = attachments.find(attachment);
	if (result != attachments.end())
	{
		return result->second;
	}

	return nullptr;
}
<commit_msg>Add redundancy checks to FrameBuffer binding<commit_after>#include "FrameBuffer.h"

#include "Texture2D.h"

FrameBuffer::FrameBuffer()
{
	glGenFramebuffers(1, &frameBufferHandle);
}

FrameBuffer::~FrameBuffer()
{
	glDeleteFramebuffers(1, &frameBufferHandle);
}

void FrameBuffer::Attach(const Texture2D *texture, GLenum attachment)
{
	// Check sizes to see if they match. It's not a requirement, but I have a feeling this will save me from some bugs...
	if (attachments.size() != 0)
	{
		const auto first = attachments.begin()->second;
		assert(first->GetWidth() == texture->GetWidth());
		assert(first->GetHeight() == texture->GetHeight());
	}

	BindAsDrawFrameBuffer(false, false);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture->GetTextureHandle(), 0);

	// Store attachment textures and draw buffers
	attachments[attachment] = texture;
	if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT31)
	{
		drawBuffers.emplace_back(attachment);
	}
}

const Texture2D *FrameBuffer::GetAttached(GLenum attachment) const
{
	auto result = attachments.find(attachment);
	if (result != attachments.end())
	{
		return result->second;
	}

	return nullptr;
}

bool FrameBuffer::IsComplete(GLenum *statusIfNotComplete) const
{
	BindAsDrawFrameBuffer(false, false);

	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if (status != GL_FRAMEBUFFER_COMPLETE)
	{
		if (statusIfNotComplete != nullptr)
		{
			*statusIfNotComplete = status;
		}

		return false;
	}

	return true;
}

void FrameBuffer::BindAsDrawFrameBuffer(bool setViewport, bool setDrawBuffers) const
{
	static GLuint lastBound = 0;
	if (frameBufferHandle != lastBound)
	{
		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferHandle);
		lastBound = frameBufferHandle;
	}

	// Always set viewport and draw buffers if requested, even if the correct frame buffer is bound.
	// We assume that GL_DRAW_FRAMEBUFFER is only bound from this function, but glDrawBuffers and
	// glViewport are not *directly* connected to the function of a frame buffer, so they might be
	// manipulated/called outside of our scope.

	if (setViewport)
	{
		const auto first = attachments.begin()->second;
		glViewport(0, 0, first->GetWidth(), first->GetHeight());
	}

	if (setDrawBuffers && drawBuffers.size() > 0)
	{
		glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);
	}
}

void FrameBuffer::BindAsReadFrameBuffer() const
{
	static GLuint lastBound = 0;
	if (frameBufferHandle != lastBound)
	{
		glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferHandle);
		lastBound = frameBufferHandle;
	}
}
<|endoftext|>