<commit_before>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "launchersockethandler.h"

#include "launcherlogging.h"

#include <QtCore/qcoreapplication.h>
#include <QtCore/qprocess.h>
#include <QtCore/qtimer.h>
#include <QtNetwork/qlocalsocket.h>

namespace qbs {
namespace Internal {

class Process : public QProcess
{
    Q_OBJECT
public:
    Process(quintptr token, QObject *parent = nullptr) :
        QProcess(parent), m_token(token), m_stopTimer(new QTimer(this))
    {
        m_stopTimer->setSingleShot(true);
        connect(m_stopTimer, &QTimer::timeout, this, &Process::cancel);
    }

    void cancel()
    {
        switch (m_stopState) {
        case StopState::Inactive:
            m_stopState = StopState::Terminating;
            m_stopTimer->start(3000);
            terminate();
            break;
        case StopState::Terminating:
            m_stopState = StopState::Killing;
            m_stopTimer->start(3000);
            kill();
            break;
        case StopState::Killing:
            m_stopState = StopState::Inactive;
            emit failedToStop();
            break;
        }
    }

    void stopStopProcedure()
    {
        m_stopState = StopState::Inactive;
        m_stopTimer->stop();
    }

    quintptr token() const { return m_token; }

signals:
    void failedToStop();

private:
    const quintptr m_token;
    QTimer * const m_stopTimer;
    enum class StopState { Inactive, Terminating, Killing } m_stopState = StopState::Inactive;
};

LauncherSocketHandler::LauncherSocketHandler(const QString &serverPath, QObject *parent)
    : QObject(parent),
      m_serverPath(serverPath),
      m_socket(new QLocalSocket(this))
{
    m_packetParser.setDevice(m_socket);
}

LauncherSocketHandler::~LauncherSocketHandler()
{
    m_socket->disconnect();
    if (m_socket->state() != QLocalSocket::UnconnectedState) {
        logWarn("socket handler destroyed while connection was active");
        m_socket->close();
    }
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it)
        it.value()->disconnect();
}

void LauncherSocketHandler::start()
{
    connect(m_socket, &QLocalSocket::disconnected,
            this, &LauncherSocketHandler::handleSocketClosed);
    connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData);
    connect(m_socket,
            static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
            this, &LauncherSocketHandler::handleSocketError);
    m_socket->connectToServer(m_serverPath);
}

void LauncherSocketHandler::handleSocketData()
{
    try {
        if (!m_packetParser.parse())
            return;
    } catch (const PacketParser::InvalidPacketSizeException &e) {
        logWarn(QString::fromLatin1("Internal protocol error: invalid packet size %1.")
                .arg(e.size));
        return;
    }
    switch (m_packetParser.type()) {
    case LauncherPacketType::StartProcess:
        handleStartPacket();
        break;
    case LauncherPacketType::StopProcess:
        handleStopPacket();
        break;
    case LauncherPacketType::Shutdown:
        handleShutdownPacket();
        return;
    default:
        logWarn(QString::fromLatin1("Internal protocol error: invalid packet type %1.")
                .arg(static_cast<int>(m_packetParser.type())));
        return;
    }
    handleSocketData();
}

void LauncherSocketHandler::handleSocketError()
{
    if (m_socket->error() != QLocalSocket::PeerClosedError) {
        logError(QString::fromLatin1("socket error: %1").arg(m_socket->errorString()));
        m_socket->disconnect();
        qApp->quit();
    }
}

void LauncherSocketHandler::handleSocketClosed()
{
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
        if (it.value()->state() != QProcess::NotRunning) {
            logWarn("client closed connection while process still running");
            break;
        }
    }
    m_socket->disconnect();
}

void LauncherSocketHandler::handleProcessError()
{
    Process * proc = senderProcess();
    if (proc->error() != QProcess::FailedToStart)
        return;
    proc->stopStopProcedure();
    ProcessErrorPacket packet(proc->token());
    packet.error = proc->error();
    packet.errorString = proc->errorString();
    sendPacket(packet);
}

void LauncherSocketHandler::handleProcessFinished()
{
    Process * proc = senderProcess();
    proc->stopStopProcedure();
    ProcessFinishedPacket packet(proc->token());
    packet.error = proc->error();
    packet.errorString = proc->errorString();
    packet.exitCode = proc->exitCode();
    packet.exitStatus = proc->exitStatus();
    packet.stdErr = proc->readAllStandardError();
    packet.stdOut = proc->readAllStandardOutput();
    sendPacket(packet);
}

void LauncherSocketHandler::handleStopFailure()
{
    // Process did not react to a kill signal. Rare, but not unheard of.
    // Forget about the associated Process object and report process exit to the client.
    Process * proc = senderProcess();
    proc->disconnect();
    m_processes.remove(proc->token());
    ProcessFinishedPacket packet(proc->token());
    packet.error = QProcess::Crashed;
    packet.exitCode = -1;
    packet.exitStatus = QProcess::CrashExit;
    packet.stdErr = proc->readAllStandardError();
    packet.stdOut = proc->readAllStandardOutput();
    sendPacket(packet);
}

void LauncherSocketHandler::handleStartPacket()
{
    Process *& process = m_processes[m_packetParser.token()];
    if (!process)
        process = setupProcess(m_packetParser.token());
    if (process->state() != QProcess::NotRunning) {
        logWarn("got start request while process was running");
        return;
    }
    const auto packet = LauncherPacket::extractPacket<StartProcessPacket>(
                m_packetParser.token(),
                m_packetParser.packetData());
    process->setEnvironment(packet.env);
    process->setWorkingDirectory(packet.workingDir);
    process->start(packet.command, packet.arguments);
}

void LauncherSocketHandler::handleStopPacket()
{
    Process * const process = m_processes.value(m_packetParser.token());
    if (!process) {
        logWarn("got stop request for unknown process");
        return;
    }
    if (process->state() == QProcess::NotRunning) {
        // This can happen if the process finishes on its own at about the same time the client
        // sends the request.
        logDebug("got stop request when process was not running");
        return;
    }
    process->cancel();
}

void LauncherSocketHandler::handleShutdownPacket()
{
    logDebug("got shutdown request, closing down");
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
        it.value()->disconnect();
        if (it.value()->state() != QProcess::NotRunning) {
            logWarn("got shutdown request while process was running");
            it.value()->terminate();
        }
    }
    m_socket->disconnect();
    qApp->quit();
}

void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
{
    m_socket->write(packet.serialize());
}

Process *LauncherSocketHandler::setupProcess(quintptr token)
{
    Process * const p = new Process(token, this);
    connect(p, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
            this, &LauncherSocketHandler::handleProcessError);
    connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
            this, &LauncherSocketHandler::handleProcessFinished);
    connect(p, &Process::failedToStop, this, &LauncherSocketHandler::handleStopFailure);
    return p;
}

Process *LauncherSocketHandler::senderProcess() const
{
    return static_cast<Process *>(sender());
}

} // namespace Internal
} // namespace qbs

#include <launchersockethandler.moc>
<commit_msg>Close process launcher on socket disconnect<commit_after>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "launchersockethandler.h"

#include "launcherlogging.h"

#include <QtCore/qcoreapplication.h>
#include <QtCore/qprocess.h>
#include <QtCore/qtimer.h>
#include <QtNetwork/qlocalsocket.h>

namespace qbs {
namespace Internal {

class Process : public QProcess
{
    Q_OBJECT
public:
    Process(quintptr token, QObject *parent = nullptr) :
        QProcess(parent), m_token(token), m_stopTimer(new QTimer(this))
    {
        m_stopTimer->setSingleShot(true);
        connect(m_stopTimer, &QTimer::timeout, this, &Process::cancel);
    }

    void cancel()
    {
        switch (m_stopState) {
        case StopState::Inactive:
            m_stopState = StopState::Terminating;
            m_stopTimer->start(3000);
            terminate();
            break;
        case StopState::Terminating:
            m_stopState = StopState::Killing;
            m_stopTimer->start(3000);
            kill();
            break;
        case StopState::Killing:
            m_stopState = StopState::Inactive;
            emit failedToStop();
            break;
        }
    }

    void stopStopProcedure()
    {
        m_stopState = StopState::Inactive;
        m_stopTimer->stop();
    }

    quintptr token() const { return m_token; }

signals:
    void failedToStop();

private:
    const quintptr m_token;
    QTimer * const m_stopTimer;
    enum class StopState { Inactive, Terminating, Killing } m_stopState = StopState::Inactive;
};

LauncherSocketHandler::LauncherSocketHandler(const QString &serverPath, QObject *parent)
    : QObject(parent),
      m_serverPath(serverPath),
      m_socket(new QLocalSocket(this))
{
    m_packetParser.setDevice(m_socket);
}

LauncherSocketHandler::~LauncherSocketHandler()
{
    m_socket->disconnect();
    if (m_socket->state() != QLocalSocket::UnconnectedState) {
        logWarn("socket handler destroyed while connection was active");
        m_socket->close();
    }
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it)
        it.value()->disconnect();
}

void LauncherSocketHandler::start()
{
    connect(m_socket, &QLocalSocket::disconnected,
            this, &LauncherSocketHandler::handleSocketClosed);
    connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData);
    connect(m_socket,
            static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
            this, &LauncherSocketHandler::handleSocketError);
    m_socket->connectToServer(m_serverPath);
}

void LauncherSocketHandler::handleSocketData()
{
    try {
        if (!m_packetParser.parse())
            return;
    } catch (const PacketParser::InvalidPacketSizeException &e) {
        logWarn(QString::fromLatin1("Internal protocol error: invalid packet size %1.")
                .arg(e.size));
        return;
    }
    switch (m_packetParser.type()) {
    case LauncherPacketType::StartProcess:
        handleStartPacket();
        break;
    case LauncherPacketType::StopProcess:
        handleStopPacket();
        break;
    case LauncherPacketType::Shutdown:
        handleShutdownPacket();
        return;
    default:
        logWarn(QString::fromLatin1("Internal protocol error: invalid packet type %1.")
                .arg(static_cast<int>(m_packetParser.type())));
        return;
    }
    handleSocketData();
}

void LauncherSocketHandler::handleSocketError()
{
    if (m_socket->error() != QLocalSocket::PeerClosedError) {
        logError(QString::fromLatin1("socket error: %1").arg(m_socket->errorString()));
        m_socket->disconnect();
        qApp->quit();
    }
}

void LauncherSocketHandler::handleSocketClosed()
{
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
        if (it.value()->state() != QProcess::NotRunning) {
            logWarn("client closed connection while process still running");
            break;
        }
    }
    m_socket->disconnect();
    qApp->quit();
}

void LauncherSocketHandler::handleProcessError()
{
    Process * proc = senderProcess();
    if (proc->error() != QProcess::FailedToStart)
        return;
    proc->stopStopProcedure();
    ProcessErrorPacket packet(proc->token());
    packet.error = proc->error();
    packet.errorString = proc->errorString();
    sendPacket(packet);
}

void LauncherSocketHandler::handleProcessFinished()
{
    Process * proc = senderProcess();
    proc->stopStopProcedure();
    ProcessFinishedPacket packet(proc->token());
    packet.error = proc->error();
    packet.errorString = proc->errorString();
    packet.exitCode = proc->exitCode();
    packet.exitStatus = proc->exitStatus();
    packet.stdErr = proc->readAllStandardError();
    packet.stdOut = proc->readAllStandardOutput();
    sendPacket(packet);
}

void LauncherSocketHandler::handleStopFailure()
{
    // Process did not react to a kill signal. Rare, but not unheard of.
    // Forget about the associated Process object and report process exit to the client.
    Process * proc = senderProcess();
    proc->disconnect();
    m_processes.remove(proc->token());
    ProcessFinishedPacket packet(proc->token());
    packet.error = QProcess::Crashed;
    packet.exitCode = -1;
    packet.exitStatus = QProcess::CrashExit;
    packet.stdErr = proc->readAllStandardError();
    packet.stdOut = proc->readAllStandardOutput();
    sendPacket(packet);
}

void LauncherSocketHandler::handleStartPacket()
{
    Process *& process = m_processes[m_packetParser.token()];
    if (!process)
        process = setupProcess(m_packetParser.token());
    if (process->state() != QProcess::NotRunning) {
        logWarn("got start request while process was running");
        return;
    }
    const auto packet = LauncherPacket::extractPacket<StartProcessPacket>(
                m_packetParser.token(),
                m_packetParser.packetData());
    process->setEnvironment(packet.env);
    process->setWorkingDirectory(packet.workingDir);
    process->start(packet.command, packet.arguments);
}

void LauncherSocketHandler::handleStopPacket()
{
    Process * const process = m_processes.value(m_packetParser.token());
    if (!process) {
        logWarn("got stop request for unknown process");
        return;
    }
    if (process->state() == QProcess::NotRunning) {
        // This can happen if the process finishes on its own at about the same time the client
        // sends the request.
        logDebug("got stop request when process was not running");
        return;
    }
    process->cancel();
}

void LauncherSocketHandler::handleShutdownPacket()
{
    logDebug("got shutdown request, closing down");
    for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
        it.value()->disconnect();
        if (it.value()->state() != QProcess::NotRunning) {
            logWarn("got shutdown request while process was running");
            it.value()->terminate();
        }
    }
    m_socket->disconnect();
    qApp->quit();
}

void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
{
    m_socket->write(packet.serialize());
}

Process *LauncherSocketHandler::setupProcess(quintptr token)
{
    Process * const p = new Process(token, this);
    connect(p, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
            this, &LauncherSocketHandler::handleProcessError);
    connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
            this, &LauncherSocketHandler::handleProcessFinished);
    connect(p, &Process::failedToStop, this, &LauncherSocketHandler::handleStopFailure);
    return p;
}

Process *LauncherSocketHandler::senderProcess() const
{
    return static_cast<Process *>(sender());
}

} // namespace Internal
} // namespace qbs

#include <launchersockethandler.moc>
<|endoftext|>


<commit_before>#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "direct.hxx"
#include "RootPool.hxx"
#include "pool.hxx"
#include "istream/istream.hxx"
#include "istream/istream_catch.hxx"
#include "fb_pool.hxx"
#include "system/fd_util.h"
#include "event/Event.hxx"

#include <stdio.h>
#include <stdlib.h>

struct Instance final : HttpServerConnectionHandler {
    struct pool *pool;

    HttpServerConnection *connection = nullptr;

    explicit Instance(struct pool &_pool)
        :pool(pool_new_libc(&_pool, "catch")) {}

    ~Instance() {
        CheckCloseConnection();
    }

    void CloseConnection() {
        http_server_connection_close(connection);
        connection = nullptr;
    }

    void CheckCloseConnection() {
        if (connection != nullptr)
            CloseConnection();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) override {}

    void HttpConnectionError(GError *error) override;
    void HttpConnectionClosed() override;
};

static GError *
catch_callback(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
    return nullptr;
}

void
Instance::HandleHttpRequest(HttpServerRequest &request,
                            gcc_unused CancellablePointer &cancel_ptr)
{
    http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                         istream_catch_new(&request.pool, *request.body,
                                           catch_callback, nullptr));

    CloseConnection();
}

void
Instance::HttpConnectionError(GError *error)
{
    g_printerr("%s\n", error->message);
    g_error_free(error);
}

void
Instance::HttpConnectionClosed()
{
}

static void
test_catch(EventLoop &event_loop, struct pool *_pool)
{
    int fds[2];
    if (socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
        perror("socketpair()");
        abort();
    }

    static constexpr char request[] =
        "POST / HTTP/1.1\r\nContent-Length: 1024\r\n\r\nfoo";
    send(fds[1], request, sizeof(request) - 1, 0);

    Instance instance(*_pool);
    instance.connection =
        http_server_connection_new(instance.pool, event_loop,
                                   fds[0], FdType::FD_SOCKET,
                                   nullptr, nullptr,
                                   nullptr, nullptr,
                                   true, instance);
    pool_unref(instance.pool);

    event_loop.Dispatch();

    close(fds[1]);
}

int main(int argc, char **argv) {
    (void)argc;
    (void)argv;

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;
    EventLoop event_loop;

    test_catch(event_loop, RootPool());
}
<commit_msg>test/t_http_server: clear the "connection" attribute<commit_after>#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "direct.hxx"
#include "RootPool.hxx"
#include "pool.hxx"
#include "istream/istream.hxx"
#include "istream/istream_catch.hxx"
#include "fb_pool.hxx"
#include "system/fd_util.h"
#include "event/Event.hxx"

#include <stdio.h>
#include <stdlib.h>

struct Instance final : HttpServerConnectionHandler {
    struct pool *pool;

    HttpServerConnection *connection = nullptr;

    explicit Instance(struct pool &_pool)
        :pool(pool_new_libc(&_pool, "catch")) {}

    ~Instance() {
        CheckCloseConnection();
    }

    void CloseConnection() {
        http_server_connection_close(connection);
        connection = nullptr;
    }

    void CheckCloseConnection() {
        if (connection != nullptr)
            CloseConnection();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) override {}

    void HttpConnectionError(GError *error) override;
    void HttpConnectionClosed() override;
};

static GError *
catch_callback(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
    return nullptr;
}

void
Instance::HandleHttpRequest(HttpServerRequest &request,
                            gcc_unused CancellablePointer &cancel_ptr)
{
    http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                         istream_catch_new(&request.pool, *request.body,
                                           catch_callback, nullptr));

    CloseConnection();
}

void
Instance::HttpConnectionError(GError *error)
{
    connection = nullptr;

    g_printerr("%s\n", error->message);
    g_error_free(error);
}

void
Instance::HttpConnectionClosed()
{
    connection = nullptr;
}

static void
test_catch(EventLoop &event_loop, struct pool *_pool)
{
    int fds[2];
    if (socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
        perror("socketpair()");
        abort();
    }

    static constexpr char request[] =
        "POST / HTTP/1.1\r\nContent-Length: 1024\r\n\r\nfoo";
    send(fds[1], request, sizeof(request) - 1, 0);

    Instance instance(*_pool);
    instance.connection =
        http_server_connection_new(instance.pool, event_loop,
                                   fds[0], FdType::FD_SOCKET,
                                   nullptr, nullptr,
                                   nullptr, nullptr,
                                   true, instance);
    pool_unref(instance.pool);

    event_loop.Dispatch();

    close(fds[1]);
}

int main(int argc, char **argv) {
    (void)argc;
    (void)argv;

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;
    EventLoop event_loop;

    test_catch(event_loop, RootPool());
}
<|endoftext|>


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

#include <moor/archive_reader.hpp>
#include <moor/archive_writer.hpp>

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <system_error>
#include <vector>

using namespace moor;

static bool testDoesNotExist()
{
    try
    {
        ArchiveReader badReader("xxxx_does_not_exist");

        return true; // Failure, we expect an exception
    }
    catch (const std::system_error& ex)
    {
        std::cerr << "Expected exception reading non-existent file: " << ex.what() << '\n';

        if (ex.code() != std::errc::no_such_file_or_directory)
        {
            std::cerr << "Unexpected error code for non-existent file\n";
            return true;
        }

        return false;
    }
}

int main()
{
  if (testDoesNotExist())
  {
      return 1;
  }

  try
  {
    std::vector<unsigned char> lout;
    //ArchiveWriter compressor("test1.tar.gz", Format::PAX, Filter::Gzip);
    ArchiveWriter compressor(lout, Format::PAX, Filter::Gzip);
    compressor.addFile("test_data_dir/bar.txt");
    compressor.addDirectory("test_data_dir/foo_dir");
    char a[] = {64, 65, 66, 67, 68};
    std::vector<char> l(10, 'A');
    std::vector<char> v(10, 'B');

    compressor.addFile("a_array.txt", a, a+10);
    compressor.addFile("b_list.txt", l.begin(), l.end());
    compressor.addFile("vector_a.txt", v.begin(), v.end());

    compressor.close();
    std::ofstream of("test2.tar.gz", std::ios::binary);
    for (auto a = lout.begin(); a != lout.end(); a++)
        of << *a;
    of.close();
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing archive: " << ex.what() << '\n';
    return 1;
  }

  try
  {
    ArchiveReader reader1("test2.tar.gz");
    std::ifstream iff ("test2.tar.gz", std::ios::binary);
    iff.seekg(0, std::ios::end);
    auto size = iff.tellg();
    iff.seekg(0, std::ios::beg);
    std::vector<unsigned char> ff(size);

    while(iff.good())
      iff.read((char*)&*ff.begin(), size);
    ArchiveReader reader(std::move(ff));
    auto data = reader.extractNext();
    while (data.first.length() > 0)
    {
        std::cout << data.first << " : " << data.second.size()<< std::endl;
        data = reader.extractNext();
    }
    data = reader1.extractNext();
    while(data.first.length() > 0)
    {
      std::cout << data.first << " : " << data.second.size()<< std::endl;
      data = reader1.extractNext();
    }
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error reading archive: " << ex.what() << '\n';
    return 1;
  }

  return 0;
}
<commit_msg>Move tests to separate functions<commit_after>/*
 * Copyright (c) 2013 Mohammad Mehdi Saboorian
 *
 * This is part of moor, a wrapper for libarchive
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <moor/archive_reader.hpp>
#include <moor/archive_writer.hpp>

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <system_error>
#include <vector>

using namespace moor;

static bool testDoesNotExist()
{
    try
    {
        ArchiveReader badReader("xxxx_does_not_exist");

        return true; // Failure, we expect an exception
    }
    catch (const std::system_error& ex)
    {
        std::cerr << "Expected exception reading non-existent file: " << ex.what() << '\n';

        if (ex.code() != std::errc::no_such_file_or_directory)
        {
            std::cerr << "Unexpected error code for non-existent file\n";
            return true;
        }

        return false;
    }
}

static bool testArchiveWrite(const std::string& path)
{
  try
  {
    std::vector<unsigned char> lout;
    //ArchiveWriter compressor("test1.tar.gz", Format::PAX, Filter::Gzip);
    ArchiveWriter compressor(lout, Format::PAX, Filter::Gzip);
    compressor.addFile("test_data_dir/bar.txt");
    compressor.addDirectory("test_data_dir/foo_dir");
    char a[] = {64, 65, 66, 67, 68};
    std::vector<char> l(10, 'A');
    std::vector<char> v(10, 'B');

    compressor.addFile("a_array.txt", a, a+10);
    compressor.addFile("b_list.txt", l.begin(), l.end());
    compressor.addFile("vector_a.txt", v.begin(), v.end());

    compressor.close();
    std::ofstream of(path, std::ios::binary);
    for (auto a = lout.begin(); a != lout.end(); a++)
        of << *a;
    of.close();
    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing archive: " << ex.what() << '\n';
    return true;
  }
}

static bool testArchiveRead(const std::string& path)
{
  try
  {
    ArchiveReader reader1(path);
    std::ifstream iff (path, std::ios::binary);
    iff.seekg(0, std::ios::end);
    auto size = iff.tellg();
    iff.seekg(0, std::ios::beg);
    std::vector<unsigned char> ff(size);

    while(iff.good())
      iff.read((char*)&*ff.begin(), size);
    ArchiveReader reader(std::move(ff));

    auto data = reader.extractNext();
    while (data.first.length() > 0)
    {
        std::cout << data.first << " : " << data.second.size()<< std::endl;
        data = reader.extractNext();
    }

    data = reader1.extractNext();
    while (data.first.length() > 0)
    {
      std::cout << data.first << " : " << data.second.size()<< std::endl;
      data = reader1.extractNext();
    }

    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error reading archive: " << ex.what() << '\n';
    return true;
  }
}

int main()
{
  if (testDoesNotExist())
  {
    return 1;
  }

  if (testArchiveWrite("test2.tar.gz"))
  {
    return 1;
  }

  if (testArchiveRead("test2.tar.gz"))
  {
      return 1;
  }

  return 0;
}
<|endoftext|>


<commit_before>#ifndef DISRUPTORPLUS_SINGLE_THREADED_CLAIM_STRATEGY_HPP_INCLUDED
#define DISRUPTORPLUS_SINGLE_THREADED_CLAIM_STRATEGY_HPP_INCLUDED

#include <disruptorplus/sequence_range.hpp>
#include <disruptorplus/sequence_barrier.hpp>
#include <disruptorplus/sequence_barrier_group.hpp>

#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cassert>

namespace disruptorplus
{
    /// \brief
    /// A claim strategy for use where only a single thread will be publishing
    /// items to a ring buffer.
    ///
    /// This strategy avoids the overhead required to synchronise multiple
    /// threads trying to claim slots in the ring buffer.
    ///
    /// A writer thread starts by first claiming one or more slots in the ring buffer
    /// by calling one of the 'claim' methods. The writer then writes to the slots
    /// and indicates to readers it has finished writing to the slots by calling
    /// publish().
    ///
    /// Reader threads can wait until a writer thread has published an element
    /// to the ring buffer by calling one of the wait_until_published() methods.
    ///
    /// Readers indicate they are finished reading from a slot in the ring buffer
    /// by publishing the sequence number of the latest item they are finished
    /// with in the 'claim barrier'. This frees the slot up for writers to
    /// subsequently claim it for future writes.
    ///
    /// \tparam WaitStrategy
    /// The strategy to use to block threads waiting for sequence numbers to be published.
    template<typename WaitStrategy>
    class single_threaded_claim_strategy
    {
    public:

        /// \brief
        /// Initialise the claim strategy.
        ///
        /// \param bufferSize
        /// The size of the ring buffer in which elements are allocated.
        /// Must be a power-of-two.
        ///
        /// \param waitStrategy
        /// The wait strategy object to use for blocking threads waiting for
        /// a particular sequence number to be published.
        /// This must be the same object that sequence barriers used by readers
        /// are constructed with.
        single_threaded_claim_strategy(
            size_t bufferSize,
            WaitStrategy& waitStrategy)
        : m_bufferSize(bufferSize)
        , m_nextSequenceToClaim(0)
        , m_claimBarrier(waitStrategy)
        , m_readBarrier(waitStrategy)
        {
            assert(bufferSize > 0 && (bufferSize & (bufferSize - 1)) == 0);
        }

        /// \brief
        /// The number of elements in the ring buffer.
        ///
        /// This will always be a power-of-two
        size_t buffer_size() const
        {
            return m_bufferSize;
        }

        /// \brief
        /// Add a sequence barrier for claiming slots in the ring buffer.
        ///
        /// Add a sequence barrier that prevents elements of the ring buffer from
        /// being claimed by writers until the reader has indicated the slot is
        /// available by publishing the sequence number they have finished reading.
        /// Claimed slots will never advance more than buffer_size() ahead
        /// of any of the registered claim barriers.
        ///
        /// \param barrier
        /// The sequence barrier to add.
        /// A reference to the sequence barrier is held by the claim strategy
        /// so the caller must ensure the lifetime of the sequence barrier
        /// exceeds that of the cliam strategy.
        /// This barrier must have been constructed with the same wait strategy object
        /// as the claim strategy was constructed with.
        /// \note
        /// This operation is not thread-safe and the caller must ensure that no other
        /// threads are accessing the claim strategy concurrently with this call.
        void add_claim_barrier(sequence_barrier<WaitStrategy>& barrier)
        {
            m_claimBarrier.add(barrier);
        }

        /// \brief
        /// Add a sequence barrier group for claiming slots in the ring buffer.
        ///
        /// Adds sequence barriers that prevent elements of the ring buffer from
        /// being claimed by writers until the readers have indicated the slot is
        /// available by publishing the sequence number they have finished reading.
        /// Claimed slots will never advance more than buffer_size() ahead
        /// of any of the registered claim barriers.
        ///
        /// \param barrier
        /// The sequence barriers to add.
        /// A reference to each sequence barrier in the group is held by the claim
        /// strategy, so the caller must ensure the lifetime of the sequence barriers
        /// exceed that of the cliam strategy.
        /// This barrier must have been constructed with the same wait strategy object
        /// as the claim strategy was constructed with.
        ///
        /// \note
        /// This operation is not thread-safe and the caller must ensure that no other
        /// threads are accessing the claim strategy concurrently with this call.
        void add_claim_barrier(sequence_barrier_group<WaitStrategy>& barrier)
        {
            m_claimBarrier.add(barrier);
        }

        /// \brief
        /// Claim a single slot in the ring buffer for writing to.
        ///
        /// Blocks the caller until a slot is available.
        ///
        /// The caller must call \ref publish(sequence_t) once it has
        /// finished writing to the slot to publish the item to readers.
        ///
        /// \return
        /// The sequence number of the slot claimed.
        sequence_t claim_one()
        {
            m_claimBarrier.wait_until_published(
                static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize));
            return m_nextSequenceToClaim++;
        }

        /// Claim up to \p count consecutive slots in the ring buffer.
        ///
        /// Blocks the caller until at least one slot is available to claim.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \return
        /// A sequence range indicating the sequence numbers that were claimed.
        /// This sequence range may contain less items than requested if fewer
        /// were available but will contain at least one slot if \p count is non-zero.
        sequence_range claim(size_t count)
        {
            sequence_t claimable = static_cast<sequence_t>(
                m_claimBarrier.wait_until_published(
                    static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize)) +
                m_bufferSize);

            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            assert(diff >= 0);

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            sequence_range result(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return result;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer without blocking.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// If any slots were claimed then this variable is populated with the
        /// range of sequence numbers that were claimed. If no slots were claimed
        /// then the value is left unchanged.
        ///
        /// \return
        /// Returns \c true if any elements were claimed in which case the range of
        /// slots claimed is written to the \p range out-parameter. May claim less
        /// slots than requested if fewer slots were available.
        /// Returns \c false if no elements were claimed. The \p range out-parameter is
        /// not modified in this case.
        bool try_claim(size_t count, sequence_range& range)
        {
            sequence_t seq = static_cast<sequence_t>(m_claimBarrier.last_published() + m_bufferSize);
            sequence_diff_t diff = difference(seq, m_nextSequenceToClaim);
            if (diff < 0)
            {
                return false;
            }
            assert(diff >= 0);
            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;
            return true;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer.
        ///
        /// Blocks the caller until either at least one slot was claimed or until
        /// the specified timeout period has elapsed, whichever comes first.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// Receives the range of sequence numbers claimed if the operation succeeds.
        /// Value is left unchanged if no slots were claimed.
        ///
        /// \param timeout
        /// The maximum time to wait for claiming any slots.
        ///
        /// \return
        /// Returns \c true if any slots were claimed in which case the range
        /// of slots claimed is written to the \p range out-parameter.
        /// Returns \c false if the timeout duration was exceeded without
        /// claiming any slots.
        ///
        /// \throw std::exception
        /// May throw any exception thrown by WaitStrategy::wait_until_published().
        template<class Rep, class Period>
        bool try_claim_for(
            size_t count,
            sequence_range& range,
            const std::chrono::duration<Rep, Period>& timeout)
        {
            if (try_claim(count, range))
            {
                return true;
            }

            sequence_t claimable =
                static_cast<sequence_t>(
                    m_claimBarrier.wait_until_published(
                        static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize),
                        timeout) + m_bufferSize);
            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            if (diff < 0)
            {
                // Timeout
                return false;
            }

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return true;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer.
        ///
        /// Blocks the caller until either at least one slot was claimed or until
        /// the specified timeout period has elapsed, whichever comes first.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// Receives the range of sequence numbers claimed if the operation succeeds.
        /// Value is left unchanged if no slots were claimed.
        ///
        /// \param timeoutTime
        /// The time after which the call should stop waiting for available slots.
        ///
        /// \return
        /// Returns \c true if any slots were claimed in which case the range
        /// of slots claimed is written to the \p range out-parameter.
        /// Returns \c false if the timeout duration was exceeded without
        /// claiming any slots.
        ///
        /// \throw std::exception
        /// May throw any exception thrown by \c Clock::now() or
        /// \c WaitStrategy::wait_until_published().
        template<class Clock, class Duration>
        bool try_claim_until(
            size_t count,
            sequence_range& range,
            const std::chrono::time_point<Clock, Duration>& timeoutTime)
        {
            if (try_claim(count, range))
            {
                return true;
            }

            sequence_t claimable =
                static_cast<sequence_t>(
                    m_claimBarrier.wait_until_published(
                        static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize),
                        timeoutTime) + m_bufferSize);
            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            if (diff < 0)
            {
                // Timeout
                return false;
            }

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return true;
        }

        /// \brief
        /// Publishes all sequences up to and including \p sequence.
        ///
        /// Notifies reader threads that these items are now available
        /// to be read.
        ///
        /// This operation has 'release' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to publish to readers.
        void publish(sequence_t sequence)
        {
            m_readBarrier.publish(sequence);
        }

        /// \brief
        /// Query the last sequence that was published.
        ///
        /// All sequences up to and including returned sequence value
        /// have been published and are available for readers to access.
        ///
        /// This operation has 'acquire' memory semantics.
        sequence_t last_published() const
        {
            return m_readBarrier.last_published();
        }

        /// \brief
        /// Block the caller until the specified \p sequence has been
        /// published by the writer thread.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be in
        /// advance of the requested sequence.
        sequence_t wait_until_published(sequence_t sequence) const
        {
            return m_readBarrier.wait_until_published(sequence);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// has elapsed.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param timeout
        /// The maximum amount of time to wait for the sequence number to
        /// be published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Rep, typename Period>
        sequence_t wait_until_published(
            sequence_t sequence,
            const std::chrono::duration<Rep, Period>& timeout) const
        {
            return m_readBarrier.wait_until_published(sequence, timeout);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// time has passed.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param timeoutTime
        /// The time after which the call should return in failure if
        /// the specified \p sequence has not yet been published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Clock, typename Duration>
        sequence_t wait_until_published(
            sequence_t sequence,
            const std::chrono::time_point<Clock, Duration>& timeoutTime) const
        {
            return m_readBarrier.wait_until_published(sequence, timeoutTime);
        }

    private:

        const size_t m_bufferSize;

        // The next sequence to be claimed (may not yet be available).
        sequence_t m_nextSequenceToClaim;

        // A cache of the last-known available sequence value
        // queried using m_claimBarrier.
        sequence_t m_lastKnownClaimableSequence;

        sequence_barrier_group<WaitStrategy> m_claimBarrier;

        // Barrier used to publish items to the
        sequence_barrier<WaitStrategy> m_readBarrier;

    };

}

#endif
<commit_msg>Add overloads of wait_until_published() to single_threaded_claim_strategy.<commit_after>#ifndef DISRUPTORPLUS_SINGLE_THREADED_CLAIM_STRATEGY_HPP_INCLUDED
#define DISRUPTORPLUS_SINGLE_THREADED_CLAIM_STRATEGY_HPP_INCLUDED

#include <disruptorplus/sequence_range.hpp>
#include <disruptorplus/sequence_barrier.hpp>
#include <disruptorplus/sequence_barrier_group.hpp>

#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cassert>

namespace disruptorplus
{
    /// \brief
    /// A claim strategy for use where only a single thread will be publishing
    /// items to a ring buffer.
    ///
    /// This strategy avoids the overhead required to synchronise multiple
    /// threads trying to claim slots in the ring buffer.
    ///
    /// A writer thread starts by first claiming one or more slots in the ring buffer
    /// by calling one of the 'claim' methods. The writer then writes to the slots
    /// and indicates to readers it has finished writing to the slots by calling
    /// publish().
    ///
    /// Reader threads can wait until a writer thread has published an element
    /// to the ring buffer by calling one of the wait_until_published() methods.
    ///
    /// Readers indicate they are finished reading from a slot in the ring buffer
    /// by publishing the sequence number of the latest item they are finished
    /// with in the 'claim barrier'. This frees the slot up for writers to
    /// subsequently claim it for future writes.
    ///
    /// \tparam WaitStrategy
    /// The strategy to use to block threads waiting for sequence numbers to be published.
    template<typename WaitStrategy>
    class single_threaded_claim_strategy
    {
    public:

        /// \brief
        /// Initialise the claim strategy.
        ///
        /// \param bufferSize
        /// The size of the ring buffer in which elements are allocated.
        /// Must be a power-of-two.
        ///
        /// \param waitStrategy
        /// The wait strategy object to use for blocking threads waiting for
        /// a particular sequence number to be published.
        /// This must be the same object that sequence barriers used by readers
        /// are constructed with.
        single_threaded_claim_strategy(
            size_t bufferSize,
            WaitStrategy& waitStrategy)
        : m_bufferSize(bufferSize)
        , m_nextSequenceToClaim(0)
        , m_claimBarrier(waitStrategy)
        , m_readBarrier(waitStrategy)
        {
            assert(bufferSize > 0 && (bufferSize & (bufferSize - 1)) == 0);
        }

        /// \brief
        /// The number of elements in the ring buffer.
        ///
        /// This will always be a power-of-two
        size_t buffer_size() const
        {
            return m_bufferSize;
        }

        /// \brief
        /// Add a sequence barrier for claiming slots in the ring buffer.
        ///
        /// Add a sequence barrier that prevents elements of the ring buffer from
        /// being claimed by writers until the reader has indicated the slot is
        /// available by publishing the sequence number they have finished reading.
        /// Claimed slots will never advance more than buffer_size() ahead
        /// of any of the registered claim barriers.
        ///
        /// \param barrier
        /// The sequence barrier to add.
        /// A reference to the sequence barrier is held by the claim strategy
        /// so the caller must ensure the lifetime of the sequence barrier
        /// exceeds that of the cliam strategy.
        /// This barrier must have been constructed with the same wait strategy object
        /// as the claim strategy was constructed with.
        /// \note
        /// This operation is not thread-safe and the caller must ensure that no other
        /// threads are accessing the claim strategy concurrently with this call.
        void add_claim_barrier(sequence_barrier<WaitStrategy>& barrier)
        {
            m_claimBarrier.add(barrier);
        }

        /// \brief
        /// Add a sequence barrier group for claiming slots in the ring buffer.
        ///
        /// Adds sequence barriers that prevent elements of the ring buffer from
        /// being claimed by writers until the readers have indicated the slot is
        /// available by publishing the sequence number they have finished reading.
        /// Claimed slots will never advance more than buffer_size() ahead
        /// of any of the registered claim barriers.
        ///
        /// \param barrier
        /// The sequence barriers to add.
        /// A reference to each sequence barrier in the group is held by the claim
        /// strategy, so the caller must ensure the lifetime of the sequence barriers
        /// exceed that of the cliam strategy.
        /// This barrier must have been constructed with the same wait strategy object
        /// as the claim strategy was constructed with.
        ///
        /// \note
        /// This operation is not thread-safe and the caller must ensure that no other
        /// threads are accessing the claim strategy concurrently with this call.
        void add_claim_barrier(sequence_barrier_group<WaitStrategy>& barrier)
        {
            m_claimBarrier.add(barrier);
        }

        /// \brief
        /// Claim a single slot in the ring buffer for writing to.
        ///
        /// Blocks the caller until a slot is available.
        ///
        /// The caller must call \ref publish(sequence_t) once it has
        /// finished writing to the slot to publish the item to readers.
        ///
        /// \return
        /// The sequence number of the slot claimed.
        sequence_t claim_one()
        {
            m_claimBarrier.wait_until_published(
                static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize));
            return m_nextSequenceToClaim++;
        }

        /// Claim up to \p count consecutive slots in the ring buffer.
        ///
        /// Blocks the caller until at least one slot is available to claim.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \return
        /// A sequence range indicating the sequence numbers that were claimed.
        /// This sequence range may contain less items than requested if fewer
        /// were available but will contain at least one slot if \p count is non-zero.
        sequence_range claim(size_t count)
        {
            sequence_t claimable = static_cast<sequence_t>(
                m_claimBarrier.wait_until_published(
                    static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize)) +
                m_bufferSize);

            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            assert(diff >= 0);

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            sequence_range result(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return result;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer without blocking.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// If any slots were claimed then this variable is populated with the
        /// range of sequence numbers that were claimed. If no slots were claimed
        /// then the value is left unchanged.
        ///
        /// \return
        /// Returns \c true if any elements were claimed in which case the range of
        /// slots claimed is written to the \p range out-parameter. May claim less
        /// slots than requested if fewer slots were available.
        /// Returns \c false if no elements were claimed. The \p range out-parameter is
        /// not modified in this case.
        bool try_claim(size_t count, sequence_range& range)
        {
            sequence_t seq = static_cast<sequence_t>(m_claimBarrier.last_published() + m_bufferSize);
            sequence_diff_t diff = difference(seq, m_nextSequenceToClaim);
            if (diff < 0)
            {
                return false;
            }
            assert(diff >= 0);
            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;
            return true;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer.
        ///
        /// Blocks the caller until either at least one slot was claimed or until
        /// the specified timeout period has elapsed, whichever comes first.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// Receives the range of sequence numbers claimed if the operation succeeds.
        /// Value is left unchanged if no slots were claimed.
        ///
        /// \param timeout
        /// The maximum time to wait for claiming any slots.
        ///
        /// \return
        /// Returns \c true if any slots were claimed in which case the range
        /// of slots claimed is written to the \p range out-parameter.
        /// Returns \c false if the timeout duration was exceeded without
        /// claiming any slots.
        ///
        /// \throw std::exception
        /// May throw any exception thrown by WaitStrategy::wait_until_published().
        template<class Rep, class Period>
        bool try_claim_for(
            size_t count,
            sequence_range& range,
            const std::chrono::duration<Rep, Period>& timeout)
        {
            if (try_claim(count, range))
            {
                return true;
            }

            sequence_t claimable =
                static_cast<sequence_t>(
                    m_claimBarrier.wait_until_published(
                        static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize),
                        timeout) + m_bufferSize);
            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            if (diff < 0)
            {
                // Timeout
                return false;
            }

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return true;
        }

        /// \brief
        /// Attempt to claim up to \p count slots in the ring buffer.
        ///
        /// Blocks the caller until either at least one slot was claimed or until
        /// the specified timeout period has elapsed, whichever comes first.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param count
        /// The maximum number of slots to claim.
        ///
        /// \param range [out]
        /// Receives the range of sequence numbers claimed if the operation succeeds.
        /// Value is left unchanged if no slots were claimed.
        ///
        /// \param timeoutTime
        /// The time after which the call should stop waiting for available slots.
        ///
        /// \return
        /// Returns \c true if any slots were claimed in which case the range
        /// of slots claimed is written to the \p range out-parameter.
        /// Returns \c false if the timeout duration was exceeded without
        /// claiming any slots.
        ///
        /// \throw std::exception
        /// May throw any exception thrown by \c Clock::now() or
        /// \c WaitStrategy::wait_until_published().
        template<class Clock, class Duration>
        bool try_claim_until(
            size_t count,
            sequence_range& range,
            const std::chrono::time_point<Clock, Duration>& timeoutTime)
        {
            if (try_claim(count, range))
            {
                return true;
            }

            sequence_t claimable =
                static_cast<sequence_t>(
                    m_claimBarrier.wait_until_published(
                        static_cast<sequence_t>(m_nextSequenceToClaim - m_bufferSize),
                        timeoutTime) + m_bufferSize);
            sequence_diff_t diff = difference(claimable, m_nextSequenceToClaim);
            if (diff < 0)
            {
                // Timeout
                return false;
            }

            size_t available = static_cast<size_t>(diff + 1);
            count = std::min(count, available);
            range = sequence_range(m_nextSequenceToClaim, count);
            m_nextSequenceToClaim += count;

            return true;
        }

        /// \brief
        /// Publishes all sequences up to and including \p sequence.
        ///
        /// Notifies reader threads that these items are now available
        /// to be read.
        ///
        /// This operation has 'release' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to publish to readers.
        void publish(sequence_t sequence)
        {
            m_readBarrier.publish(sequence);
        }

        /// \brief
        /// Query the last sequence that was published.
        ///
        /// All sequences up to and including returned sequence value
        /// have been published and are available for readers to access.
        ///
        /// This operation has 'acquire' memory semantics.
        sequence_t last_published() const
        {
            return m_readBarrier.last_published();
        }

        /// \brief
        /// Block the caller until the specified \p sequence has been
        /// published by the writer thread.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be in
        /// advance of the requested sequence.
        sequence_t wait_until_published(sequence_t sequence) const
        {
            return m_readBarrier.wait_until_published(sequence);
        }

        /// \brief
        /// Block the caller until the specified \p sequence has been
        /// published by the writer thread.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param lastKnownPublished
        /// This sequence number is assumed to have already been published.
        /// The initial value passed in here on first call should be sequence_t(-1).
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be in
        /// advance of the requested sequence.
        sequence_t wait_until_published(sequence_t sequence, sequence_t lastKnownPublished) const
        {
            return wait_until_published(sequence);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// has elapsed.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param timeout
        /// The maximum amount of time to wait for the sequence number to
        /// be published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Rep, typename Period>
        sequence_t wait_until_published(
            sequence_t sequence,
            const std::chrono::duration<Rep, Period>& timeout) const
        {
            return m_readBarrier.wait_until_published(sequence, timeout);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// has elapsed.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param lastKnownPublished
        /// This sequence number is assumed to have already been published.
        /// The initial value passed in here on first call should be sequence_t(-1).
        ///
        /// \param timeout
        /// The maximum amount of time to wait for the sequence number to
        /// be published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Rep, typename Period>
        sequence_t wait_until_published(
            sequence_t sequence,
            sequence_t lastKnownPublished,
            const std::chrono::duration<Rep, Period>& timeout) const
        {
            return wait_until_published(sequence, timeout);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// time has passed.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param timeoutTime
        /// The time after which the call should return in failure if
        /// the specified \p sequence has not yet been published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Clock, typename Duration>
        sequence_t wait_until_published(
            sequence_t sequence,
            const std::chrono::time_point<Clock, Duration>& timeoutTime) const
        {
            return m_readBarrier.wait_until_published(sequence, timeoutTime);
        }

        /// \brief
        /// Block the caller until either the specified sequence has been
        /// published by the writer thread or until the specified timeout
        /// has elapsed.
        ///
        /// This operation has 'acquire' memory semantics.
        ///
        /// \param sequence
        /// The sequence number to wait for.
        ///
        /// \param lastKnownPublished
        /// This sequence number is assumed to have already been published.
        /// The initial value passed in here on first call should be sequence_t(-1).
        ///
        /// \param timeoutTime
        /// The time after which the call should return in failure if
        /// the specified \p sequence has not yet been published.
        ///
        /// \return
        /// Returns the value of \ref last_published() which may be prior to
        /// the specified sequence in the case of a timeout or equal to
        /// or after the specified sequence in the case of that item being
        /// published.
        template<typename Clock, typename Duration>
        sequence_t wait_until_published(
            sequence_t sequence,
            sequence_t lastKnownPublished,
            const std::chrono::time_point<Clock, Duration>& timeoutTime) const
        {
            return wait_until_published(sequence, timeoutTime);
        }

    private:

        const size_t m_bufferSize;

        // The next sequence to be claimed (may not yet be available).
        sequence_t m_nextSequenceToClaim;

        // A cache of the last-known available sequence value
        // queried using m_claimBarrier.
        sequence_t m_lastKnownClaimableSequence;

        sequence_barrier_group<WaitStrategy> m_claimBarrier;

        // Barrier used to publish items to the
        sequence_barrier<WaitStrategy> m_readBarrier;

    };

}

#endif
<|endoftext|>


<commit_before>//
//  catmullrom.cpp
//  curves
//
//  Created by Aaron Taylor on 10/12/14.
//  Copyright (c) 2014 Aaron Taylor. All rights reserved.
//

#include "catmullrom.h"

void CatmullRom::recalculateTangents()
{
    if (controlPointVectorSize() < 6) {
        return;
    }
    // iterate over intermediate points and recompute them
    for (int k = 3; k < controlPointVectorSize()-2; k += 2) {

        float2 pkminus = controlPointVectorElement(k - 3);
        float2 pkplus = controlPointVectorElement(k + 2);

        float tkplus = (float)(k+2);
        float tkminus = (float)(k-3);

        controlPoints.at(k) = (pkplus - pkminus) * (1/(tkplus - tkminus));
    }
}

void CatmullRom::addControlPoint(float2 newPoint)
{
    controlPoints.push_back(newPoint);
    controlPoints.push_back(float2(0.0,0.0));
    recalculateTangents();
}

void CatmullRom::moveControlPoint(int i, float2 pos)
{
    HermiteInterp::moveControlPoint(i, pos);
    recalculateTangents();
}

void CatmullRom::drawControlPoints()
{
    HermiteInterp::drawControlPoints();

    // skip the hermite's drawing of control points
//    for (int i = 0; i < controlPointVectorSize(); i += 2) {
//        Freeform::drawSingleControlPoint(i);
//    }
}

int CatmullRom::currentControlPoint(float2 test)
{
    for (int i = 0; i < controlPoints.size(); i+=2) {
        float2 cur = controlPoints.at(i);
        if (pointMatch(cur, test)) {
            return i;
        }
    }
    return -1;
}<commit_msg>working catmull rom!<commit_after>//
//  catmullrom.cpp
//  curves
//
//  Created by Aaron Taylor on 10/12/14.
//  Copyright (c) 2014 Aaron Taylor. All rights reserved.
//

#include "catmullrom.h"

void CatmullRom::recalculateTangents()
{
    if (controlPointVectorSize() < 6) {
        return;
    }
    // iterate over intermediate points and recompute them
    for (int k = 2; k < controlPointVectorSize()-2; k += 2) {

        float2 pkminus = controlPointVectorElement(k - 2);
        float2 pkplus = controlPointVectorElement(k + 2);

        float tkplus = (float)(k/2+2);
        float tkminus = (float)(k/2-3);

        controlPoints.at(k+1) = (pkplus - pkminus) * (1/(tkplus - tkminus));
    }
}

void CatmullRom::addControlPoint(float2 newPoint)
{
    controlPoints.push_back(newPoint);
    controlPoints.push_back(float2(0.0,0.0));
    recalculateTangents();
}

void CatmullRom::moveControlPoint(int i, float2 pos)
{
    HermiteInterp::moveControlPoint(i, pos);
    recalculateTangents();
}

void CatmullRom::drawControlPoints()
{
    // skip the hermite's drawing of control points
    for (int i = 0; i < controlPointVectorSize(); i += 2) {
        Freeform::drawSingleControlPoint(i);
    }
}

int CatmullRom::currentControlPoint(float2 test)
{
    for (int i = 0; i < controlPoints.size(); i+=2) {
        float2 cur = controlPoints.at(i);
        if (pointMatch(cur, test)) {
            return i;
        }
    }
    return -1;
}<|endoftext|>


<commit_before>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/operation/overlayng/EdgeNodingBuilder.h>
#include <geos/operation/overlayng/EdgeMerger.h>

using geos::operation::valid::RepeatedPointRemover;

namespace geos {      // geos
namespace operation { // geos.operation
namespace overlayng { // geos.operation.overlayng

/*private*/
Noder*
EdgeNodingBuilder::getNoder()
{
    if (customNoder != nullptr) {
        return customNoder;
    }

    if (OverlayUtil::isFloating(pm)) {
        internalNoder = createFloatingPrecisionNoder(IS_NODING_VALIDATED);
    }
    else {
        internalNoder = createFixedPrecisionNoder(pm);
    }
    return internalNoder.get();
}

/*private*/
std::unique_ptr<Noder>
EdgeNodingBuilder::createFixedPrecisionNoder(const PrecisionModel* p_pm)
{
    std::unique_ptr<Noder> srNoder(new SnapRoundingNoder(p_pm));
    return srNoder;
}

/*private*/
std::unique_ptr<Noder>
EdgeNodingBuilder::createFloatingPrecisionNoder(bool doValidation)
{
    std::unique_ptr<MCIndexNoder> mcNoder(new MCIndexNoder());
    mcNoder->setSegmentIntersector(&intAdder);

    if (doValidation) {
        spareInternalNoder = std::move(mcNoder);
        std::unique_ptr<Noder> validNoder(new ValidatingNoder(*spareInternalNoder));
        return validNoder;
    }

    return std::unique_ptr<Noder>(mcNoder.release());
}

/*public*/
void
EdgeNodingBuilder::setClipEnvelope(const Envelope* p_clipEnv)
{
    clipEnv = p_clipEnv;
    clipper.reset(new RingClipper(p_clipEnv));
    limiter.reset(new LineLimiter(p_clipEnv));
}

/*public*/
std::vector<Edge*>
EdgeNodingBuilder::build(const Geometry* geom0, const Geometry* geom1)
{
    add(geom0, 0);
    add(geom1, 1);
    std::vector<Edge*> nodedEdges = node(inputEdges.get());

    /**
     * Merge the noded edges to eliminate duplicates.
     * Labels are combined.
     */
    return EdgeMerger::merge(nodedEdges);
}

/*private*/
std::vector<Edge*>
EdgeNodingBuilder::node(std::vector<SegmentString*>* segStrings)
{
    std::vector<Edge*> nodedEdges;

    Noder* noder = getNoder();
    noder->computeNodes(segStrings);

    std::unique_ptr<std::vector<SegmentString*>> nodedSS(noder->getNodedSubstrings());

    nodedEdges = createEdges(nodedSS.get());

    // Clean up now that all the info is transferred to Edges
    for (SegmentString* ss : *nodedSS) {
        delete ss;
    }

    return nodedEdges;
}

/*private*/
std::vector<Edge*>
EdgeNodingBuilder::createEdges(std::vector<SegmentString*>* segStrings)
{
    std::vector<Edge*> createdEdges;

    for (SegmentString* ss : *segStrings) {
        const CoordinateSequence* pts = ss->getCoordinates();

        // don't create edges from collapsed lines
        if (Edge::isCollapsed(pts)) continue;

        // This EdgeSourceInfo is already managed locally in a std::deque
        const EdgeSourceInfo* info = static_cast<const EdgeSourceInfo*>(ss->getData());
        // Record that a non-collapsed edge exists for the parent geometry
        hasEdges[info->getIndex()] = true;
        // Allocate the new Edge locally in a std::deque
        std::unique_ptr<CoordinateSequence> ssPts = ss->getCoordinates()->clone();
        edgeQue.emplace_back(ssPts.release(), info);
        Edge* newEdge = &(edgeQue.back());
        createdEdges.push_back(newEdge);
    }
    return createdEdges;
}

/*public*/
bool
EdgeNodingBuilder::hasEdgesFor(int geomIndex) const
{
    assert(geomIndex >= 0 && geomIndex < 2);
    return hasEdges[geomIndex];
}

/*private*/
void
EdgeNodingBuilder::add(const Geometry* g, int geomIndex)
{
    if (g == nullptr || g->isEmpty())
        return;

    if (isClippedCompletely(g->getEnvelopeInternal()))
        return;

    switch (g->getGeometryTypeId())
    {
        case GEOS_POLYGON:
            return addPolygon(static_cast<const Polygon*>(g), geomIndex);
        case GEOS_LINESTRING:
            return addLine(static_cast<const LineString*>(g), geomIndex);
        case GEOS_MULTILINESTRING:
        case GEOS_MULTIPOLYGON:
        case GEOS_GEOMETRYCOLLECTION:
            return addCollection(static_cast<const GeometryCollection*>(g), geomIndex);
        case GEOS_POINT:
        case GEOS_LINEARRING:
        case GEOS_MULTIPOINT:
            return; // do nothing
        default:
            return; // do nothing
    }
}

/*private*/
void
EdgeNodingBuilder::addCollection(const GeometryCollection* gc, int geomIndex)
{
    for (std::size_t i = 0; i < gc->getNumGeometries(); i++) {
        const Geometry* g = gc->getGeometryN(i);
        add(g, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addPolygon(const Polygon* poly, int geomIndex)
{
    const LinearRing* shell = poly->getExteriorRing();
    addPolygonRing(shell, false, geomIndex);

    for (std::size_t i = 0; i < poly->getNumInteriorRing(); i++) {
        const LinearRing* hole = poly->getInteriorRingN(i);

        // Holes are topologically labelled opposite to the shell, since
        // the interior of the polygon lies on their opposite side
        // (on the left, if the hole is oriented CW)
        addPolygonRing(hole, true, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addPolygonRing(const LinearRing* ring, bool isHole, int index)
  {
    // don't add empty rings
    if (ring->isEmpty()) return;

    if (isClippedCompletely(ring->getEnvelopeInternal()))
      return;

    std::unique_ptr<geom::CoordinateArraySequence> pts = clip(ring);

    /**
    * Don't add edges that collapse to a point
    */
    if (pts->size() < 2) {
        return;
    }

    int depthDelta = computeDepthDelta(ring, isHole);
    addEdge(pts, createEdgeSourceInfo(index, depthDelta, isHole));
}

/*private*/
const EdgeSourceInfo*
EdgeNodingBuilder::createEdgeSourceInfo(int index)
{
    // Concentrate small memory allocations via std::deque and
    // retain ownership of the EdgeSourceInfo* in the EdgeNodingBuilder
    edgeSourceInfoQue.emplace_back(index);
    return &(edgeSourceInfoQue.back());
}

/*private*/
const EdgeSourceInfo*
EdgeNodingBuilder::createEdgeSourceInfo(int index, int depthDelta, bool isHole)
{
    // Concentrate small memory allocations via std::deque and
    // retain ownership of the EdgeSourceInfo* in the EdgeNodingBuilder
    edgeSourceInfoQue.emplace_back(index, depthDelta, isHole);
    return &(edgeSourceInfoQue.back());
}

/*private*/
void
EdgeNodingBuilder::addEdge(std::unique_ptr<CoordinateArraySequence>& cas, const EdgeSourceInfo* info)
{
    // TODO: manage these internally to EdgeNodingBuilder in a std::deque,
    // since they do not have a life span longer than the EdgeNodingBuilder
    // in OverlayNG::buildGraph()
    NodedSegmentString* ss = new NodedSegmentString(cas.release(), reinterpret_cast<const void*>(info));
    inputEdges->push_back(ss);
}

/*private*/
void
EdgeNodingBuilder::addEdge(std::unique_ptr<std::vector<Coordinate>> pts, const EdgeSourceInfo* info)
{
    CoordinateArraySequence* cas = new CoordinateArraySequence(pts.release());
    NodedSegmentString* ss = new NodedSegmentString(cas, reinterpret_cast<const void*>(info));
    inputEdges->push_back(ss);
}

/*private*/
bool
EdgeNodingBuilder::isClippedCompletely(const Envelope* env)
{
    if (clipEnv == nullptr) return false;
    return clipEnv->disjoint(env);
}

/* private */
std::unique_ptr<geom::CoordinateArraySequence>
EdgeNodingBuilder::clip(const LinearRing* ring)
{
    const Envelope* env = ring->getEnvelopeInternal();

    /**
     * If no clipper or ring is completely contained then no need to clip.
     * But repeated points must be removed to ensure correct noding.
     */
    if (clipper == nullptr || clipEnv->covers(env)) {
        return removeRepeatedPoints(ring);
    }

    return clipper->clip(ring->getCoordinatesRO());
}

/*private*/
std::unique_ptr<CoordinateArraySequence>
EdgeNodingBuilder::removeRepeatedPoints(const LineString* line)
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    return RepeatedPointRemover::removeRepeatedPoints(pts);
}

/*private*/
int
EdgeNodingBuilder::computeDepthDelta(const LinearRing* ring, bool isHole)
{
    /**
     * Compute the orientation of the ring, to
     * allow assigning side interior/exterior labels correctly.
     * JTS canonical orientation is that shells are CW, holes are CCW.
     *
     * It is important to compute orientation on the original ring,
     * since topology collapse can make the orientation computation give the wrong answer.
     */
    bool isCCW = algorithm::Orientation::isCCW(ring->getCoordinatesRO());

    /**
     * Compute whether ring is in canonical orientation or not.
     * Canonical orientation for the overlay process is
     * Shells : CW, Holes: CCW
     */
    bool isOriented = true;
    if (!isHole) {
        isOriented = !isCCW;
    }
    else {
        isOriented = isCCW;
    }
    /**
     * Depth delta can now be computed.
     * Canonical depth delta is 1 (Exterior on L, Interior on R).
     * It is flipped to -1 if the ring is oppositely oriented.
     */
    int depthDelta = isOriented ? 1 : -1;
    return depthDelta;
}

/*private*/
void
EdgeNodingBuilder::addLine(const LineString* line, int geomIndex)
{
    // don't add empty lines
    if (line->isEmpty()) return;

    if (isClippedCompletely(line->getEnvelopeInternal()))
        return;

    if (isToBeLimited(line)) {
        std::vector<std::unique_ptr<CoordinateArraySequence>>& sections = limit(line);
        for (auto& pts : sections) {
            addLine(pts, geomIndex);
        }
    }
    else {
        std::unique_ptr<CoordinateArraySequence> ptsNoRepeat = removeRepeatedPoints(line);
        addLine(ptsNoRepeat, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addLine(std::unique_ptr<CoordinateArraySequence>& pts, int geomIndex)
{
    /**
     * Don't add edges that collapse to a point
     */
    if (pts->size() < 2) {
        return;
    }

    addEdge(pts, createEdgeSourceInfo(geomIndex));
}

/*private*/
bool
EdgeNodingBuilder::isToBeLimited(const LineString* line) const
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    if (limiter == nullptr || pts->size() <= MIN_LIMIT_PTS) {
        return false;
    }
    const Envelope* env = line->getEnvelopeInternal();
    /**
     * If line is completely contained then no need to limit
     */
    if (clipEnv->covers(env)) {
        return false;
    }
    return true;
}

/*private*/
std::vector<std::unique_ptr<CoordinateArraySequence>>&
EdgeNodingBuilder::limit(const LineString* line)
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    return limiter->limit(pts);
}

} // namespace geos.operation.overlayng
} // namespace geos.operation
} // namespace geos
<commit_msg>[OverlayNG] Fix handling of LinearRing<commit_after>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/operation/overlayng/EdgeNodingBuilder.h>
#include <geos/operation/overlayng/EdgeMerger.h>

using geos::operation::valid::RepeatedPointRemover;

namespace geos {      // geos
namespace operation { // geos.operation
namespace overlayng { // geos.operation.overlayng

/*private*/
Noder*
EdgeNodingBuilder::getNoder()
{
    if (customNoder != nullptr) {
        return customNoder;
    }

    if (OverlayUtil::isFloating(pm)) {
        internalNoder = createFloatingPrecisionNoder(IS_NODING_VALIDATED);
    }
    else {
        internalNoder = createFixedPrecisionNoder(pm);
    }
    return internalNoder.get();
}

/*private*/
std::unique_ptr<Noder>
EdgeNodingBuilder::createFixedPrecisionNoder(const PrecisionModel* p_pm)
{
    std::unique_ptr<Noder> srNoder(new SnapRoundingNoder(p_pm));
    return srNoder;
}

/*private*/
std::unique_ptr<Noder>
EdgeNodingBuilder::createFloatingPrecisionNoder(bool doValidation)
{
    std::unique_ptr<MCIndexNoder> mcNoder(new MCIndexNoder());
    mcNoder->setSegmentIntersector(&intAdder);

    if (doValidation) {
        spareInternalNoder = std::move(mcNoder);
        std::unique_ptr<Noder> validNoder(new ValidatingNoder(*spareInternalNoder));
        return validNoder;
    }

    return std::unique_ptr<Noder>(mcNoder.release());
}

/*public*/
void
EdgeNodingBuilder::setClipEnvelope(const Envelope* p_clipEnv)
{
    clipEnv = p_clipEnv;
    clipper.reset(new RingClipper(p_clipEnv));
    limiter.reset(new LineLimiter(p_clipEnv));
}

/*public*/
std::vector<Edge*>
EdgeNodingBuilder::build(const Geometry* geom0, const Geometry* geom1)
{
    add(geom0, 0);
    add(geom1, 1);
    std::vector<Edge*> nodedEdges = node(inputEdges.get());

    /**
     * Merge the noded edges to eliminate duplicates.
     * Labels are combined.
     */
    return EdgeMerger::merge(nodedEdges);
}

/*private*/
std::vector<Edge*>
EdgeNodingBuilder::node(std::vector<SegmentString*>* segStrings)
{
    std::vector<Edge*> nodedEdges;

    Noder* noder = getNoder();
    noder->computeNodes(segStrings);

    std::unique_ptr<std::vector<SegmentString*>> nodedSS(noder->getNodedSubstrings());

    nodedEdges = createEdges(nodedSS.get());

    // Clean up now that all the info is transferred to Edges
    for (SegmentString* ss : *nodedSS) {
        delete ss;
    }

    return nodedEdges;
}

/*private*/
std::vector<Edge*>
EdgeNodingBuilder::createEdges(std::vector<SegmentString*>* segStrings)
{
    std::vector<Edge*> createdEdges;

    for (SegmentString* ss : *segStrings) {
        const CoordinateSequence* pts = ss->getCoordinates();

        // don't create edges from collapsed lines
        if (Edge::isCollapsed(pts)) continue;

        // This EdgeSourceInfo is already managed locally in a std::deque
        const EdgeSourceInfo* info = static_cast<const EdgeSourceInfo*>(ss->getData());
        // Record that a non-collapsed edge exists for the parent geometry
        hasEdges[info->getIndex()] = true;
        // Allocate the new Edge locally in a std::deque
        std::unique_ptr<CoordinateSequence> ssPts = ss->getCoordinates()->clone();
        edgeQue.emplace_back(ssPts.release(), info);
        Edge* newEdge = &(edgeQue.back());
        createdEdges.push_back(newEdge);
    }
    return createdEdges;
}

/*public*/
bool
EdgeNodingBuilder::hasEdgesFor(int geomIndex) const
{
    assert(geomIndex >= 0 && geomIndex < 2);
    return hasEdges[geomIndex];
}

/*private*/
void
EdgeNodingBuilder::add(const Geometry* g, int geomIndex)
{
    if (g == nullptr || g->isEmpty())
        return;

    if (isClippedCompletely(g->getEnvelopeInternal()))
        return;

    switch (g->getGeometryTypeId())
    {
        case GEOS_POLYGON:
            return addPolygon(static_cast<const Polygon*>(g), geomIndex);
        case GEOS_LINESTRING:
        case GEOS_LINEARRING:
            return addLine(static_cast<const LineString*>(g), geomIndex);
        case GEOS_MULTILINESTRING:
        case GEOS_MULTIPOLYGON:
        case GEOS_GEOMETRYCOLLECTION:
            return addCollection(static_cast<const GeometryCollection*>(g), geomIndex);
        case GEOS_POINT:
        case GEOS_MULTIPOINT:
            return; // do nothing
        default:
            return; // do nothing
    }
}

/*private*/
void
EdgeNodingBuilder::addCollection(const GeometryCollection* gc, int geomIndex)
{
    for (std::size_t i = 0; i < gc->getNumGeometries(); i++) {
        const Geometry* g = gc->getGeometryN(i);
        add(g, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addPolygon(const Polygon* poly, int geomIndex)
{
    const LinearRing* shell = poly->getExteriorRing();
    addPolygonRing(shell, false, geomIndex);

    for (std::size_t i = 0; i < poly->getNumInteriorRing(); i++) {
        const LinearRing* hole = poly->getInteriorRingN(i);

        // Holes are topologically labelled opposite to the shell, since
        // the interior of the polygon lies on their opposite side
        // (on the left, if the hole is oriented CW)
        addPolygonRing(hole, true, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addPolygonRing(const LinearRing* ring, bool isHole, int index)
  {
    // don't add empty rings
    if (ring->isEmpty()) return;

    if (isClippedCompletely(ring->getEnvelopeInternal()))
      return;

    std::unique_ptr<geom::CoordinateArraySequence> pts = clip(ring);

    /**
    * Don't add edges that collapse to a point
    */
    if (pts->size() < 2) {
        return;
    }

    int depthDelta = computeDepthDelta(ring, isHole);
    addEdge(pts, createEdgeSourceInfo(index, depthDelta, isHole));
}

/*private*/
const EdgeSourceInfo*
EdgeNodingBuilder::createEdgeSourceInfo(int index)
{
    // Concentrate small memory allocations via std::deque and
    // retain ownership of the EdgeSourceInfo* in the EdgeNodingBuilder
    edgeSourceInfoQue.emplace_back(index);
    return &(edgeSourceInfoQue.back());
}

/*private*/
const EdgeSourceInfo*
EdgeNodingBuilder::createEdgeSourceInfo(int index, int depthDelta, bool isHole)
{
    // Concentrate small memory allocations via std::deque and
    // retain ownership of the EdgeSourceInfo* in the EdgeNodingBuilder
    edgeSourceInfoQue.emplace_back(index, depthDelta, isHole);
    return &(edgeSourceInfoQue.back());
}

/*private*/
void
EdgeNodingBuilder::addEdge(std::unique_ptr<CoordinateArraySequence>& cas, const EdgeSourceInfo* info)
{
    // TODO: manage these internally to EdgeNodingBuilder in a std::deque,
    // since they do not have a life span longer than the EdgeNodingBuilder
    // in OverlayNG::buildGraph()
    NodedSegmentString* ss = new NodedSegmentString(cas.release(), reinterpret_cast<const void*>(info));
    inputEdges->push_back(ss);
}

/*private*/
void
EdgeNodingBuilder::addEdge(std::unique_ptr<std::vector<Coordinate>> pts, const EdgeSourceInfo* info)
{
    CoordinateArraySequence* cas = new CoordinateArraySequence(pts.release());
    NodedSegmentString* ss = new NodedSegmentString(cas, reinterpret_cast<const void*>(info));
    inputEdges->push_back(ss);
}

/*private*/
bool
EdgeNodingBuilder::isClippedCompletely(const Envelope* env)
{
    if (clipEnv == nullptr) return false;
    return clipEnv->disjoint(env);
}

/* private */
std::unique_ptr<geom::CoordinateArraySequence>
EdgeNodingBuilder::clip(const LinearRing* ring)
{
    const Envelope* env = ring->getEnvelopeInternal();

    /**
     * If no clipper or ring is completely contained then no need to clip.
     * But repeated points must be removed to ensure correct noding.
     */
    if (clipper == nullptr || clipEnv->covers(env)) {
        return removeRepeatedPoints(ring);
    }

    return clipper->clip(ring->getCoordinatesRO());
}

/*private*/
std::unique_ptr<CoordinateArraySequence>
EdgeNodingBuilder::removeRepeatedPoints(const LineString* line)
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    return RepeatedPointRemover::removeRepeatedPoints(pts);
}

/*private*/
int
EdgeNodingBuilder::computeDepthDelta(const LinearRing* ring, bool isHole)
{
    /**
     * Compute the orientation of the ring, to
     * allow assigning side interior/exterior labels correctly.
     * JTS canonical orientation is that shells are CW, holes are CCW.
     *
     * It is important to compute orientation on the original ring,
     * since topology collapse can make the orientation computation give the wrong answer.
     */
    bool isCCW = algorithm::Orientation::isCCW(ring->getCoordinatesRO());

    /**
     * Compute whether ring is in canonical orientation or not.
     * Canonical orientation for the overlay process is
     * Shells : CW, Holes: CCW
     */
    bool isOriented = true;
    if (!isHole) {
        isOriented = !isCCW;
    }
    else {
        isOriented = isCCW;
    }
    /**
     * Depth delta can now be computed.
     * Canonical depth delta is 1 (Exterior on L, Interior on R).
     * It is flipped to -1 if the ring is oppositely oriented.
     */
    int depthDelta = isOriented ? 1 : -1;
    return depthDelta;
}

/*private*/
void
EdgeNodingBuilder::addLine(const LineString* line, int geomIndex)
{
    // don't add empty lines
    if (line->isEmpty()) return;

    if (isClippedCompletely(line->getEnvelopeInternal()))
        return;

    if (isToBeLimited(line)) {
        std::vector<std::unique_ptr<CoordinateArraySequence>>& sections = limit(line);
        for (auto& pts : sections) {
            addLine(pts, geomIndex);
        }
    }
    else {
        std::unique_ptr<CoordinateArraySequence> ptsNoRepeat = removeRepeatedPoints(line);
        addLine(ptsNoRepeat, geomIndex);
    }
}

/*private*/
void
EdgeNodingBuilder::addLine(std::unique_ptr<CoordinateArraySequence>& pts, int geomIndex)
{
    /**
     * Don't add edges that collapse to a point
     */
    if (pts->size() < 2) {
        return;
    }

    addEdge(pts, createEdgeSourceInfo(geomIndex));
}

/*private*/
bool
EdgeNodingBuilder::isToBeLimited(const LineString* line) const
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    if (limiter == nullptr || pts->size() <= MIN_LIMIT_PTS) {
        return false;
    }
    const Envelope* env = line->getEnvelopeInternal();
    /**
     * If line is completely contained then no need to limit
     */
    if (clipEnv->covers(env)) {
        return false;
    }
    return true;
}

/*private*/
std::vector<std::unique_ptr<CoordinateArraySequence>>&
EdgeNodingBuilder::limit(const LineString* line)
{
    const CoordinateSequence* pts = line->getCoordinatesRO();
    return limiter->limit(pts);
}

} // namespace geos.operation.overlayng
} // namespace geos.operation
} // namespace geos
<|endoftext|>


<commit_before>/*
 * This file is part ServerManager of package
 *
 * (c) Ondřej Záruba <zarubaondra@gmail.com>
 *
 * For the full copyright and license information, please view the license.md
 * file that was distributed with this source code.
 */

#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "./ArgumentParser.h"

using namespace ServerManager;
using namespace std;

ArgumentParser::ArgumentParser(int argc, char** argv)
{
	int i;
	if (argc == 2) {
		this->action = argv[1];
	} else if (argc >= 3) {
		this->action = argv[1];
		if ((argc - 3) % 2 != 0) {
			throw "Missing option value";
		}
		this->param = argv[2];
		for (i = 3; i < argc; i++) {
			this->argv.push_back(argv[i]);
		}
	} else {
		throw "Invalid arguments";
	}
}

string ArgumentParser::getAction()
{
	return this->action;
}

string ArgumentParser::getParam()
{
	return this->param;
}

string ArgumentParser::getOption(string key)
{
	int i;
	for(i = 0; i < this->argv.size(); i++) {
		if (this->argv[i].compare(key) == 0) {
			return this->argv[i + 1];
		}
	}

	return "";
}
<commit_msg>Fixed variable type<commit_after>/*
 * This file is part ServerManager of package
 *
 * (c) Ondřej Záruba <zarubaondra@gmail.com>
 *
 * For the full copyright and license information, please view the license.md
 * file that was distributed with this source code.
 */

#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "./ArgumentParser.h"

using namespace ServerManager;
using namespace std;

ArgumentParser::ArgumentParser(int argc, char** argv)
{
	int i;
	if (argc == 2) {
		this->action = argv[1];
	} else if (argc >= 3) {
		this->action = argv[1];
		if ((argc - 3) % 2 != 0) {
			throw "Missing option value";
		}
		this->param = argv[2];
		for (i = 3; i < argc; i++) {
			this->argv.push_back(argv[i]);
		}
	} else {
		throw "Invalid arguments";
	}
}

string ArgumentParser::getAction()
{
	return this->action;
}

string ArgumentParser::getParam()
{
	return this->param;
}

string ArgumentParser::getOption(string key)
{
	unsigned int i;
	for(i = 0; i < this->argv.size(); i++) {
		if (this->argv[i].compare(key) == 0) {
			return this->argv[i + 1];
		}
	}

	return "";
}
<|endoftext|>