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

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

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

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

#include "mainwindow.h"
#include "settingspage.h"
#include "systemmonitor.h"
#include "pluginloader.h"
#include "textdocument.h"
#include "connectpage.h"
#include "bufferview.h"
#include "helppopup.h"
#include "chatpage.h"
#include "dock.h"
#include <IrcCommandQueue>
#include <IrcBufferModel>
#include <IrcConnection>
#include <QApplication>
#include <QCloseEvent>
#include <QPushButton>
#include <IrcBuffer>
#include <QShortcut>
#include <QSettings>
#include <QMenuBar>
#include <QTimer>
#include <QUuid>
#include <QMenu>
#include <QDir>

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
    d.view = 0;
    d.save = false;

    // TODO
    QDir::addSearchPath("black", ":/images/black");
    QDir::addSearchPath("gray", ":/images/gray");
    QDir::addSearchPath("white", ":/images/white");

#ifndef Q_OS_MAC
    QIcon icon;
    icon.addFile(":/communi-16.png");
    icon.addFile(":/communi-24.png");
    icon.addFile(":/communi-32.png");
    icon.addFile(":/communi-48.png");
    icon.addFile(":/communi-64.png");
    icon.addFile(":/communi-128.png");
    icon.addFile(":/communi-256.png");
    icon.addFile(":/communi-512.png");
    setWindowIcon(icon);
    qApp->setWindowIcon(icon);
#endif // Q_OS_MAC

    d.stack = new QStackedWidget(this);
    connect(d.stack, SIGNAL(currentChanged(int)), this, SLOT(updateTitle()));

    setCentralWidget(d.stack);

    d.chatPage = new ChatPage(this);
    setCurrentView(d.chatPage->currentView());
    connect(d.chatPage, SIGNAL(currentBufferChanged(IrcBuffer*)), this, SLOT(updateTitle()));
    connect(d.chatPage, SIGNAL(currentViewChanged(BufferView*)), this, SLOT(setCurrentView(BufferView*)));
    connect(this, SIGNAL(connectionAdded(IrcConnection*)), d.chatPage, SLOT(addConnection(IrcConnection*)));
    connect(this, SIGNAL(connectionRemoved(IrcConnection*)), d.chatPage, SLOT(removeConnection(IrcConnection*)));
    connect(this, SIGNAL(connectionRemoved(IrcConnection*)), this, SLOT(removeConnection(IrcConnection*)));

    d.stack->addWidget(d.chatPage);

    d.settingsPage = new SettingsPage(0);
    connect(d.settingsPage, SIGNAL(accepted()), this, SLOT(onSettingsAccepted()));
    connect(d.settingsPage, SIGNAL(rejected()), this, SLOT(pop()));

    QShortcut* shortcut = new QShortcut(QKeySequence::Quit, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(close()));

    shortcut = new QShortcut(QKeySequence::New, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(doConnect()));

    shortcut = new QShortcut(QKeySequence::Preferences, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(showSettings()));

    shortcut = new QShortcut(QKeySequence::Close, d.chatPage);
    connect(shortcut, SIGNAL(activated()), d.chatPage, SLOT(closeBuffer()));

    d.dock = new Dock(this);
    connect(d.chatPage, SIGNAL(alert(IrcMessage*)), d.dock, SLOT(alert(IrcMessage*)));

    d.monitor = new SystemMonitor(this);
    connect(d.monitor, SIGNAL(screenLocked()), d.dock, SLOT(activateAlert()));
    connect(d.monitor, SIGNAL(screenUnlocked()), d.dock, SLOT(deactivateAlert()));
    connect(d.monitor, SIGNAL(screenSaverStarted()), d.dock, SLOT(activateAlert()));
    connect(d.monitor, SIGNAL(screenSaverStopped()), d.dock, SLOT(deactivateAlert()));

    PluginLoader::instance()->windowCreated(this);
    PluginLoader::instance()->setConnectionsList(&(d.connections));

    restoreState();
    d.save = true;

    if (d.connections.isEmpty())
        doConnect();

}

MainWindow::~MainWindow()
{
    PluginLoader::instance()->windowDestroyed(this);
    delete d.settingsPage;
}

void MainWindow::saveState()
{
    if (!d.save)
        return;

    QSettings settings;
    settings.setValue("geometry", saveGeometry());
    settings.setValue("settings", d.chatPage->saveSettings());
    settings.setValue("state", d.chatPage->saveState());

    QVariantList states;
    foreach (IrcConnection* connection, d.connections) {
        QVariantMap state;
        state.insert("connection", connection->saveState());
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        if (model)
            state.insert("model", model->saveState());
        states += state;
    }
    settings.setValue("connections", states);
}

void MainWindow::restoreState()
{
    QSettings settings;
    if (settings.contains("geometry"))
        restoreGeometry(settings.value("geometry").toByteArray());

    d.chatPage->restoreSettings(settings.value("settings").toByteArray());

    foreach (const QVariant& v, settings.value("connections").toList()) {
        QVariantMap state = v.toMap();
        IrcConnection* connection = new IrcConnection(d.chatPage);
        connection->restoreState(state.value("connection").toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        if (model)
            model->restoreState(state.value("model").toByteArray());
    }

    d.chatPage->restoreState(settings.value("state").toByteArray());

    if (!settings.value("loggingLocation").isValid()) {
        settings.setValue("loggingLocation", QDir::homePath() + "/communi-irc-logs");
    }

    d.settingsPage->setTheme(d.chatPage->theme());
    d.settingsPage->setLoggingEnabled(settings.value("loggingEnabled", false).toBool());
    d.settingsPage->setLoggingLocation(settings.value("loggingLocation").toString());

    PluginLoader::instance()->settingsChanged();

    if (d.settingsPage->loggingEnabled()) {
        PluginLoader::instance()->enablePlugin("libloggerplugin");
    }
    else {
        PluginLoader::instance()->disablePlugin("libloggerplugin");
    }
}

BufferView* MainWindow::currentView() const
{
    return d.view;
}

void MainWindow::setCurrentView(BufferView* view)
{
    if (d.view != view) {
        d.view = view;
        emit currentViewChanged(view);
    }
}

QList<IrcConnection*> MainWindow::connections() const
{
    return d.connections;
}

void MainWindow::addConnection(IrcConnection* connection)
{
    QVariantMap ud = connection->userData();
    if (!ud.contains("uuid")) {
        ud.insert("uuid", QUuid::createUuid());
        connection->setUserData(ud);
    }

    connection->setReconnectDelay(15);
    connection->network()->setRequestedCapabilities(Irc::supportedCapabilities());

    IrcCommandQueue* queue = new IrcCommandQueue(connection);
    queue->setConnection(connection);

    // backwards compatibility
    if (connection->nickNames().isEmpty())
        connection->setNickNames(QStringList() << connection->nickName());
    if (connection->servers().isEmpty())
        connection->setServers(QStringList() << QString("%1 %2%3").arg(connection->host())
                                                                  .arg(connection->isSecure() ? "+" : "")
                                                                  .arg(connection->port()));

    connect(d.monitor, SIGNAL(wake()), connection, SLOT(open()));
    connect(d.monitor, SIGNAL(online()), connection, SLOT(open()));

    connect(d.monitor, SIGNAL(sleep()), connection, SLOT(quit()));
    connect(d.monitor, SIGNAL(sleep()), connection, SLOT(close()));

    connect(d.monitor, SIGNAL(offline()), connection, SLOT(close()));

    d.connections += connection;
    connect(connection, SIGNAL(enabledChanged(bool)), this, SLOT(saveState()));
    connect(connection, SIGNAL(destroyed(IrcConnection*)), this, SLOT(removeConnection(IrcConnection*)));
    emit connectionAdded(connection);

    saveState();
}

void MainWindow::removeConnection(IrcConnection* connection)
{
    if (d.connections.removeOne(connection))
        emit connectionRemoved(connection);
    if (d.connections.isEmpty())
        doConnect();
    else
        saveState();
}

void MainWindow::push(QWidget* page)
{
    QWidget* prev = d.stack->currentWidget();

    if (prev == page)
        return;

    if (prev) {
        prev->setProperty("__focus_widget__", QVariant::fromValue(prev->focusWidget()));
        prev->setEnabled(false);
    }
    d.stack->addWidget(page);
    d.stack->setCurrentWidget(page);
    page->setFocus();
}

void MainWindow::pop()
{
    QWidget* page = d.stack->currentWidget();
    if (!qobject_cast<ChatPage*>(page)) {
        d.stack->removeWidget(page);
        d.stack->setCurrentIndex(d.stack->count() - 1);
        QWidget* current = d.stack->currentWidget();
        if (current) {
            current->setEnabled(true);
            QWidget* fw = current->property("__focus_widget__").value<QWidget*>();
            if (fw)
                fw->setFocus();
        }

        // Don't delete the settings page, it has a single instance
        if (page != d.settingsPage)
            page->deleteLater();
    }
}

QSize MainWindow::sizeHint() const
{
    return QSize(800, 600);
}

bool MainWindow::event(QEvent* event)
{
    if (event->type() == QEvent::ActivationChange) {
        if (isActiveWindow())
            emit activated();
    }
    return QMainWindow::event(event);
}

void MainWindow::closeEvent(QCloseEvent* event)
{
    if (isVisible()) {
        saveState();
        d.save = false;

        foreach (IrcConnection* connection, d.connections) {
            connection->quit(qApp->property("description").toString());
            connection->close();
        }

        // let the sessions close in the background
        hide();
        event->ignore();
        QTimer::singleShot(1000, qApp, SLOT(quit()));
    }
}

void MainWindow::showEvent(QShowEvent* event)
{
    PluginLoader::instance()->windowShowEvent(this, event);
}

void MainWindow::doConnect()
{
    for (int i = 0; i < d.stack->count(); ++i) {
        ConnectPage* page = qobject_cast<ConnectPage*>(d.stack->widget(i));
        if (page) {
            d.stack->setCurrentWidget(page);
            return;
        }
    }

    ConnectPage* page = new ConnectPage(this);
    page->buttonBox()->button(QDialogButtonBox::Cancel)->setEnabled(!d.connections.isEmpty());
    connect(page, SIGNAL(accepted()), this, SLOT(onConnectAccepted()));
    connect(page, SIGNAL(rejected()), this, SLOT(pop()));
    push(page);
}

void MainWindow::onEditAccepted()
{
    ConnectPage* page = qobject_cast<ConnectPage*>(sender());
    if (page) {
        IrcConnection* connection = page->connection();
        connection->setServers(page->servers());
        connection->setNickNames(page->nickNames());
        connection->setRealName(page->realName());
        connection->setUserName(page->userName());
        connection->setDisplayName(page->displayName());
        connection->setPassword(page->password());
        connection->setSaslMechanism(page->saslMechanism());
        saveState();
        pop();
    }
}

void MainWindow::onConnectAccepted()
{
    ConnectPage* page = qobject_cast<ConnectPage*>(sender());
    if (page) {
        IrcConnection* connection = new IrcConnection(d.chatPage);
        connection->setServers(page->servers());
        connection->setNickNames(page->nickNames());
        connection->setRealName(page->realName());
        connection->setUserName(page->userName());
        connection->setDisplayName(page->displayName());
        connection->setPassword(page->password());
        connection->setSaslMechanism(page->saslMechanism());
        addConnection(connection);
        pop();
    }
}

void MainWindow::onSettingsAccepted()
{
    SettingsPage* page = qobject_cast<SettingsPage*>(sender());
    if (page) {
        d.chatPage->setTheme(page->theme());
        saveState();
        pop();

        QSettings settings;
        settings.setValue("loggingEnabled", page->loggingEnabled());
        settings.setValue("loggingLocation", page->loggingLocation());

        PluginLoader::instance()->settingsChanged();

        if (page->loggingEnabled()) {
            PluginLoader::instance()->enablePlugin("libloggerplugin");
        }
        else {
            PluginLoader::instance()->disablePlugin("libloggerplugin");
        }
    }
}

void MainWindow::updateTitle()
{
    IrcBuffer* buffer = d.chatPage->currentBuffer();
    if (!buffer || d.stack->currentWidget() != d.chatPage)
        setWindowTitle(QCoreApplication::applicationName());
    else
        setWindowTitle(tr("%1 - %2").arg(buffer->title(), QCoreApplication::applicationName()));
}

void MainWindow::showSettings()
{
    push(d.settingsPage);
}

void MainWindow::showHelp()
{
    HelpPopup* help = new HelpPopup(this);
    help->popup();
}

void MainWindow::editConnection(IrcConnection* connection)
{
    ConnectPage* page = new ConnectPage(connection, this);
    connect(page, SIGNAL(accepted()), this, SLOT(onEditAccepted()));
    connect(page, SIGNAL(rejected()), this, SLOT(pop()));
    page->setServers(connection->servers());
    page->setNickNames(connection->nickNames());
    page->setRealName(connection->realName());
    page->setUserName(connection->userName());
    page->setDisplayName(connection->displayName());
    page->setPassword(connection->password());
    page->setSaslMechanism(connection->saslMechanism());
    push(page);
}

void MainWindow::toggleFullScreen()
{
    if (!isFullScreen()) {
        showFullScreen();
    } else {
        showNormal();
    }
}
<commit_msg>Use QStandardPaths::writableLocation(AppDataLocation) for logs<commit_after>/*
  Copyright (C) 2008-2016 The Communi Project

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

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

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

#include "mainwindow.h"
#include "settingspage.h"
#include "systemmonitor.h"
#include "pluginloader.h"
#include "textdocument.h"
#include "connectpage.h"
#include "bufferview.h"
#include "helppopup.h"
#include "chatpage.h"
#include "dock.h"
#include <IrcCommandQueue>
#include <IrcBufferModel>
#include <QStandardPaths>
#include <IrcConnection>
#include <QApplication>
#include <QCloseEvent>
#include <QPushButton>
#include <IrcBuffer>
#include <QShortcut>
#include <QSettings>
#include <QMenuBar>
#include <QTimer>
#include <QUuid>
#include <QMenu>
#include <QDir>

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
    d.view = 0;
    d.save = false;

    // TODO
    QDir::addSearchPath("black", ":/images/black");
    QDir::addSearchPath("gray", ":/images/gray");
    QDir::addSearchPath("white", ":/images/white");

#ifndef Q_OS_MAC
    QIcon icon;
    icon.addFile(":/communi-16.png");
    icon.addFile(":/communi-24.png");
    icon.addFile(":/communi-32.png");
    icon.addFile(":/communi-48.png");
    icon.addFile(":/communi-64.png");
    icon.addFile(":/communi-128.png");
    icon.addFile(":/communi-256.png");
    icon.addFile(":/communi-512.png");
    setWindowIcon(icon);
    qApp->setWindowIcon(icon);
#endif // Q_OS_MAC

    d.stack = new QStackedWidget(this);
    connect(d.stack, SIGNAL(currentChanged(int)), this, SLOT(updateTitle()));

    setCentralWidget(d.stack);

    d.chatPage = new ChatPage(this);
    setCurrentView(d.chatPage->currentView());
    connect(d.chatPage, SIGNAL(currentBufferChanged(IrcBuffer*)), this, SLOT(updateTitle()));
    connect(d.chatPage, SIGNAL(currentViewChanged(BufferView*)), this, SLOT(setCurrentView(BufferView*)));
    connect(this, SIGNAL(connectionAdded(IrcConnection*)), d.chatPage, SLOT(addConnection(IrcConnection*)));
    connect(this, SIGNAL(connectionRemoved(IrcConnection*)), d.chatPage, SLOT(removeConnection(IrcConnection*)));
    connect(this, SIGNAL(connectionRemoved(IrcConnection*)), this, SLOT(removeConnection(IrcConnection*)));

    d.stack->addWidget(d.chatPage);

    d.settingsPage = new SettingsPage(0);
    connect(d.settingsPage, SIGNAL(accepted()), this, SLOT(onSettingsAccepted()));
    connect(d.settingsPage, SIGNAL(rejected()), this, SLOT(pop()));

    QShortcut* shortcut = new QShortcut(QKeySequence::Quit, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(close()));

    shortcut = new QShortcut(QKeySequence::New, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(doConnect()));

    shortcut = new QShortcut(QKeySequence::Preferences, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(showSettings()));

    shortcut = new QShortcut(QKeySequence::Close, d.chatPage);
    connect(shortcut, SIGNAL(activated()), d.chatPage, SLOT(closeBuffer()));

    d.dock = new Dock(this);
    connect(d.chatPage, SIGNAL(alert(IrcMessage*)), d.dock, SLOT(alert(IrcMessage*)));

    d.monitor = new SystemMonitor(this);
    connect(d.monitor, SIGNAL(screenLocked()), d.dock, SLOT(activateAlert()));
    connect(d.monitor, SIGNAL(screenUnlocked()), d.dock, SLOT(deactivateAlert()));
    connect(d.monitor, SIGNAL(screenSaverStarted()), d.dock, SLOT(activateAlert()));
    connect(d.monitor, SIGNAL(screenSaverStopped()), d.dock, SLOT(deactivateAlert()));

    PluginLoader::instance()->windowCreated(this);
    PluginLoader::instance()->setConnectionsList(&(d.connections));

    restoreState();
    d.save = true;

    if (d.connections.isEmpty())
        doConnect();

}

MainWindow::~MainWindow()
{
    PluginLoader::instance()->windowDestroyed(this);
    delete d.settingsPage;
}

void MainWindow::saveState()
{
    if (!d.save)
        return;

    QSettings settings;
    settings.setValue("geometry", saveGeometry());
    settings.setValue("settings", d.chatPage->saveSettings());
    settings.setValue("state", d.chatPage->saveState());

    QVariantList states;
    foreach (IrcConnection* connection, d.connections) {
        QVariantMap state;
        state.insert("connection", connection->saveState());
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        if (model)
            state.insert("model", model->saveState());
        states += state;
    }
    settings.setValue("connections", states);
}

void MainWindow::restoreState()
{
    QSettings settings;
    if (settings.contains("geometry"))
        restoreGeometry(settings.value("geometry").toByteArray());

    d.chatPage->restoreSettings(settings.value("settings").toByteArray());

    foreach (const QVariant& v, settings.value("connections").toList()) {
        QVariantMap state = v.toMap();
        IrcConnection* connection = new IrcConnection(d.chatPage);
        connection->restoreState(state.value("connection").toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        if (model)
            model->restoreState(state.value("model").toByteArray());
    }

    d.chatPage->restoreState(settings.value("state").toByteArray());

    if (!settings.value("loggingLocation").isValid()) {
#if QT_VERSION >= 0x050400
        settings.setValue("loggingLocation", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/logs");
#else
        settings.setValue("loggingLocation", QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/logs");
#endif
    }

    d.settingsPage->setTheme(d.chatPage->theme());
    d.settingsPage->setLoggingEnabled(settings.value("loggingEnabled", false).toBool());
    d.settingsPage->setLoggingLocation(settings.value("loggingLocation").toString());

    PluginLoader::instance()->settingsChanged();

    if (d.settingsPage->loggingEnabled()) {
        PluginLoader::instance()->enablePlugin("libloggerplugin");
    }
    else {
        PluginLoader::instance()->disablePlugin("libloggerplugin");
    }
}

BufferView* MainWindow::currentView() const
{
    return d.view;
}

void MainWindow::setCurrentView(BufferView* view)
{
    if (d.view != view) {
        d.view = view;
        emit currentViewChanged(view);
    }
}

QList<IrcConnection*> MainWindow::connections() const
{
    return d.connections;
}

void MainWindow::addConnection(IrcConnection* connection)
{
    QVariantMap ud = connection->userData();
    if (!ud.contains("uuid")) {
        ud.insert("uuid", QUuid::createUuid());
        connection->setUserData(ud);
    }

    connection->setReconnectDelay(15);
    connection->network()->setRequestedCapabilities(Irc::supportedCapabilities());

    IrcCommandQueue* queue = new IrcCommandQueue(connection);
    queue->setConnection(connection);

    // backwards compatibility
    if (connection->nickNames().isEmpty())
        connection->setNickNames(QStringList() << connection->nickName());
    if (connection->servers().isEmpty())
        connection->setServers(QStringList() << QString("%1 %2%3").arg(connection->host())
                                                                  .arg(connection->isSecure() ? "+" : "")
                                                                  .arg(connection->port()));

    connect(d.monitor, SIGNAL(wake()), connection, SLOT(open()));
    connect(d.monitor, SIGNAL(online()), connection, SLOT(open()));

    connect(d.monitor, SIGNAL(sleep()), connection, SLOT(quit()));
    connect(d.monitor, SIGNAL(sleep()), connection, SLOT(close()));

    connect(d.monitor, SIGNAL(offline()), connection, SLOT(close()));

    d.connections += connection;
    connect(connection, SIGNAL(enabledChanged(bool)), this, SLOT(saveState()));
    connect(connection, SIGNAL(destroyed(IrcConnection*)), this, SLOT(removeConnection(IrcConnection*)));
    emit connectionAdded(connection);

    saveState();
}

void MainWindow::removeConnection(IrcConnection* connection)
{
    if (d.connections.removeOne(connection))
        emit connectionRemoved(connection);
    if (d.connections.isEmpty())
        doConnect();
    else
        saveState();
}

void MainWindow::push(QWidget* page)
{
    QWidget* prev = d.stack->currentWidget();

    if (prev == page)
        return;

    if (prev) {
        prev->setProperty("__focus_widget__", QVariant::fromValue(prev->focusWidget()));
        prev->setEnabled(false);
    }
    d.stack->addWidget(page);
    d.stack->setCurrentWidget(page);
    page->setFocus();
}

void MainWindow::pop()
{
    QWidget* page = d.stack->currentWidget();
    if (!qobject_cast<ChatPage*>(page)) {
        d.stack->removeWidget(page);
        d.stack->setCurrentIndex(d.stack->count() - 1);
        QWidget* current = d.stack->currentWidget();
        if (current) {
            current->setEnabled(true);
            QWidget* fw = current->property("__focus_widget__").value<QWidget*>();
            if (fw)
                fw->setFocus();
        }

        // Don't delete the settings page, it has a single instance
        if (page != d.settingsPage)
            page->deleteLater();
    }
}

QSize MainWindow::sizeHint() const
{
    return QSize(800, 600);
}

bool MainWindow::event(QEvent* event)
{
    if (event->type() == QEvent::ActivationChange) {
        if (isActiveWindow())
            emit activated();
    }
    return QMainWindow::event(event);
}

void MainWindow::closeEvent(QCloseEvent* event)
{
    if (isVisible()) {
        saveState();
        d.save = false;

        foreach (IrcConnection* connection, d.connections) {
            connection->quit(qApp->property("description").toString());
            connection->close();
        }

        // let the sessions close in the background
        hide();
        event->ignore();
        QTimer::singleShot(1000, qApp, SLOT(quit()));
    }
}

void MainWindow::showEvent(QShowEvent* event)
{
    PluginLoader::instance()->windowShowEvent(this, event);
}

void MainWindow::doConnect()
{
    for (int i = 0; i < d.stack->count(); ++i) {
        ConnectPage* page = qobject_cast<ConnectPage*>(d.stack->widget(i));
        if (page) {
            d.stack->setCurrentWidget(page);
            return;
        }
    }

    ConnectPage* page = new ConnectPage(this);
    page->buttonBox()->button(QDialogButtonBox::Cancel)->setEnabled(!d.connections.isEmpty());
    connect(page, SIGNAL(accepted()), this, SLOT(onConnectAccepted()));
    connect(page, SIGNAL(rejected()), this, SLOT(pop()));
    push(page);
}

void MainWindow::onEditAccepted()
{
    ConnectPage* page = qobject_cast<ConnectPage*>(sender());
    if (page) {
        IrcConnection* connection = page->connection();
        connection->setServers(page->servers());
        connection->setNickNames(page->nickNames());
        connection->setRealName(page->realName());
        connection->setUserName(page->userName());
        connection->setDisplayName(page->displayName());
        connection->setPassword(page->password());
        connection->setSaslMechanism(page->saslMechanism());
        saveState();
        pop();
    }
}

void MainWindow::onConnectAccepted()
{
    ConnectPage* page = qobject_cast<ConnectPage*>(sender());
    if (page) {
        IrcConnection* connection = new IrcConnection(d.chatPage);
        connection->setServers(page->servers());
        connection->setNickNames(page->nickNames());
        connection->setRealName(page->realName());
        connection->setUserName(page->userName());
        connection->setDisplayName(page->displayName());
        connection->setPassword(page->password());
        connection->setSaslMechanism(page->saslMechanism());
        addConnection(connection);
        pop();
    }
}

void MainWindow::onSettingsAccepted()
{
    SettingsPage* page = qobject_cast<SettingsPage*>(sender());
    if (page) {
        d.chatPage->setTheme(page->theme());
        saveState();
        pop();

        QSettings settings;
        settings.setValue("loggingEnabled", page->loggingEnabled());
        settings.setValue("loggingLocation", page->loggingLocation());

        PluginLoader::instance()->settingsChanged();

        if (page->loggingEnabled()) {
            PluginLoader::instance()->enablePlugin("libloggerplugin");
        }
        else {
            PluginLoader::instance()->disablePlugin("libloggerplugin");
        }
    }
}

void MainWindow::updateTitle()
{
    IrcBuffer* buffer = d.chatPage->currentBuffer();
    if (!buffer || d.stack->currentWidget() != d.chatPage)
        setWindowTitle(QCoreApplication::applicationName());
    else
        setWindowTitle(tr("%1 - %2").arg(buffer->title(), QCoreApplication::applicationName()));
}

void MainWindow::showSettings()
{
    push(d.settingsPage);
}

void MainWindow::showHelp()
{
    HelpPopup* help = new HelpPopup(this);
    help->popup();
}

void MainWindow::editConnection(IrcConnection* connection)
{
    ConnectPage* page = new ConnectPage(connection, this);
    connect(page, SIGNAL(accepted()), this, SLOT(onEditAccepted()));
    connect(page, SIGNAL(rejected()), this, SLOT(pop()));
    page->setServers(connection->servers());
    page->setNickNames(connection->nickNames());
    page->setRealName(connection->realName());
    page->setUserName(connection->userName());
    page->setDisplayName(connection->displayName());
    page->setPassword(connection->password());
    page->setSaslMechanism(connection->saslMechanism());
    push(page);
}

void MainWindow::toggleFullScreen()
{
    if (!isFullScreen()) {
        showFullScreen();
    } else {
        showNormal();
    }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: refernce.cxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: hr $ $Date: 2000-09-18 15:18:17 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <limits.h>
#include <vos/diagnose.hxx>

#include <vos/refernce.hxx>

#ifdef _USE_NAMESPACE
using namespace vos;
#endif

ORefCount::~ORefCount()
{

}

OReference::OReference()
{
}

OReference::~OReference()
{
    VOS_ASSERT(m_RefCount.referenced() == 0);
}

IReference::RefCount OReference::acquire()
{
    return (m_RefCount.acquire());
}

IReference::RefCount OReference::release()
{
    RefCount Count = m_RefCount.release();

    if (Count == 0)
        delete this;

    return (Count);
}

<commit_msg>INTEGRATION: CWS ooo19126 (1.1.1.1.150); FILE MERGED 2005/09/05 17:40:17 rt 1.1.1.1.150.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: refernce.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 09:09:03 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <limits.h>
#include <vos/diagnose.hxx>

#include <vos/refernce.hxx>

#ifdef _USE_NAMESPACE
using namespace vos;
#endif

ORefCount::~ORefCount()
{

}

OReference::OReference()
{
}

OReference::~OReference()
{
    VOS_ASSERT(m_RefCount.referenced() == 0);
}

IReference::RefCount OReference::acquire()
{
    return (m_RefCount.acquire());
}

IReference::RefCount OReference::release()
{
    RefCount Count = m_RefCount.release();

    if (Count == 0)
        delete this;

    return (Count);
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010-2011 OTClient <https://github.com/edubart/otclient>
 *
 * 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 "spritemanager.h"
#include <framework/core/resourcemanager.h>
#include <framework/graphics/graphics.h>

SpriteManager g_sprites;

SpriteManager::SpriteManager()
{
    m_spritesCount = 0;
    m_signature = 0;
}

bool SpriteManager::load(const std::string& file)
{
    try {
        g_resources.loadFile(file, m_fin);
        m_signature = Fw::getU32(m_fin);
        m_spritesCount = Fw::getU16(m_fin);
        m_sprites.resize(m_spritesCount);
        return true;
    } catch(std::exception& e) {
        logError("faile to load sprites from '", file, "': ", e.what());
        return false;
    }
}

void SpriteManager::unload()
{
    m_sprites.clear();
    m_spritesCount = 0;
    m_signature = 0;
}

TexturePtr SpriteManager::loadSpriteTexture(int id)
{
    m_fin.seekg(((id-1) * 4) + 6, std::ios_base::beg);

    uint32 spriteAddress = Fw::getU32(m_fin);

    // no sprite? return an empty texture
    if(spriteAddress == 0)
        return g_graphics.getEmptyTexture();

    m_fin.seekg(spriteAddress, std::ios_base::beg);
    assert(m_fin.good());

    // skip color key
    Fw::getU8(m_fin);
    Fw::getU8(m_fin);
    Fw::getU8(m_fin);

    uint16 pixelDataSize = Fw::getU16(m_fin);

    uchar pixels[4096];
    int writePos = 0;
    int read = 0;

    // decompress pixels
    while(read < pixelDataSize) {
        uint16 transparentPixels, coloredPixels;
        m_fin.read((char*)&transparentPixels, 2);
        m_fin.read((char*)&coloredPixels, 2);

        for(int i = 0; i < transparentPixels; i++) {
            pixels[writePos + 0] = 0x00;
            pixels[writePos + 1] = 0x00;
            pixels[writePos + 2] = 0x00;
            pixels[writePos + 3] = 0x00;
            writePos += 4;
        }

        for(int i = 0; i < coloredPixels; i++) {
            pixels[writePos + 0] = Fw::getU8(m_fin);
            pixels[writePos + 1] = Fw::getU8(m_fin);
            pixels[writePos + 2] = Fw::getU8(m_fin);
            pixels[writePos + 3] = 0xFF;

            writePos += 4;
        }

        read += 4 + (3 * coloredPixels);
    }

    // fill remaining pixels with alpha
    while(writePos < 4096) {
        pixels[writePos + 0] = 0x00;
        pixels[writePos + 1] = 0x00;
        pixels[writePos + 2] = 0x00;
        pixels[writePos + 3] = 0x00;
        writePos += 4;
    }

    return TexturePtr(new Texture(32, 32, 4, pixels));
}

TexturePtr SpriteManager::loadSpriteMask(TexturePtr spriteTex, Otc::SpriteMask mask)
{
    auto pixels = spriteTex->getPixels();

    static uint32 maskColors[4] = { Fw::red, Fw::green, Fw::blue, Fw::yellow };
    uint32 maskColor = maskColors[mask];
    uint32 whiteColor = Fw::white;
    uint32 alphaColor = Fw::alpha;

    // convert pixels
    // masked color -> white color
    // any other color -> alpha color
    for(int i=0;i<4096;i+=4) {
        uint32& currentColor = *(uint32*)&pixels[i];
        if(currentColor == maskColor)
            currentColor = whiteColor;
        else
            currentColor = alphaColor;
    }

    return TexturePtr(new Texture(32, 32, 4, &pixels[0]));
}

TexturePtr SpriteManager::getSpriteTexture(int id, Otc::SpriteMask mask)
{
    if(id == 0)
        return g_graphics.getEmptyTexture();

    assert(id > 0 && id <= m_spritesCount);

    // load sprites on demand
    Sprite& sprite = m_sprites[id-1];
    if(!sprite.texture)
        sprite.texture = loadSpriteTexture(id);

    //TODO: release unused sprites textures after X seconds
    // to avoid massive texture allocations

    if(mask != Otc::SpriteNoMask) {
        if(!sprite.masks[mask])
            sprite.masks[mask] = loadSpriteMask(sprite.texture, mask);
        return sprite.masks[mask];
    } else
        return sprite.texture;
}
<commit_msg>small fix to sprite loading<commit_after>/*
 * Copyright (c) 2010-2011 OTClient <https://github.com/edubart/otclient>
 *
 * 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 "spritemanager.h"
#include <framework/core/resourcemanager.h>
#include <framework/graphics/graphics.h>

SpriteManager g_sprites;

SpriteManager::SpriteManager()
{
    m_spritesCount = 0;
    m_signature = 0;
}

bool SpriteManager::load(const std::string& file)
{
    try {
        g_resources.loadFile(file, m_fin);
        m_signature = Fw::getU32(m_fin);
        m_spritesCount = Fw::getU16(m_fin);
        m_sprites.resize(m_spritesCount);
        return true;
    } catch(std::exception& e) {
        logError("faile to load sprites from '", file, "': ", e.what());
        return false;
    }
}

void SpriteManager::unload()
{
    m_sprites.clear();
    m_spritesCount = 0;
    m_signature = 0;
}

TexturePtr SpriteManager::loadSpriteTexture(int id)
{
    m_fin.seekg(((id-1) * 4) + 6, std::ios_base::beg);

    uint32 spriteAddress = Fw::getU32(m_fin);

    // no sprite? return an empty texture
    if(spriteAddress == 0)
        return g_graphics.getEmptyTexture();

    m_fin.seekg(spriteAddress, std::ios_base::beg);
    assert(m_fin.good());

    // skip color key
    Fw::getU8(m_fin);
    Fw::getU8(m_fin);
    Fw::getU8(m_fin);

    uint16 pixelDataSize = Fw::getU16(m_fin);

    uchar pixels[4096];
    int writePos = 0;
    int read = 0;

    // decompress pixels
    while(read < pixelDataSize) {
        uint16 transparentPixels, coloredPixels;
        m_fin.read((char*)&transparentPixels, 2);
        m_fin.read((char*)&coloredPixels, 2);

        if(writePos + transparentPixels*4 + coloredPixels*3 >= 4096)
            return g_graphics.getEmptyTexture();

        for(int i = 0; i < transparentPixels; i++) {
            pixels[writePos + 0] = 0x00;
            pixels[writePos + 1] = 0x00;
            pixels[writePos + 2] = 0x00;
            pixels[writePos + 3] = 0x00;
            writePos += 4;
        }

        for(int i = 0; i < coloredPixels; i++) {
            pixels[writePos + 0] = Fw::getU8(m_fin);
            pixels[writePos + 1] = Fw::getU8(m_fin);
            pixels[writePos + 2] = Fw::getU8(m_fin);
            pixels[writePos + 3] = 0xFF;

            writePos += 4;
        }

        read += 4 + (3 * coloredPixels);
    }

    // fill remaining pixels with alpha
    while(writePos < 4096) {
        pixels[writePos + 0] = 0x00;
        pixels[writePos + 1] = 0x00;
        pixels[writePos + 2] = 0x00;
        pixels[writePos + 3] = 0x00;
        writePos += 4;
    }

    return TexturePtr(new Texture(32, 32, 4, pixels));
}

TexturePtr SpriteManager::loadSpriteMask(TexturePtr spriteTex, Otc::SpriteMask mask)
{
    auto pixels = spriteTex->getPixels();

    static uint32 maskColors[4] = { Fw::red, Fw::green, Fw::blue, Fw::yellow };
    uint32 maskColor = maskColors[mask];
    uint32 whiteColor = Fw::white;
    uint32 alphaColor = Fw::alpha;

    // convert pixels
    // masked color -> white color
    // any other color -> alpha color
    for(int i=0;i<4096;i+=4) {
        uint32& currentColor = *(uint32*)&pixels[i];
        if(currentColor == maskColor)
            currentColor = whiteColor;
        else
            currentColor = alphaColor;
    }

    return TexturePtr(new Texture(32, 32, 4, &pixels[0]));
}

TexturePtr SpriteManager::getSpriteTexture(int id, Otc::SpriteMask mask)
{
    if(id == 0)
        return g_graphics.getEmptyTexture();

    assert(id > 0 && id <= m_spritesCount);

    // load sprites on demand
    Sprite& sprite = m_sprites[id-1];
    if(!sprite.texture)
        sprite.texture = loadSpriteTexture(id);

    //TODO: release unused sprites textures after X seconds
    // to avoid massive texture allocations

    if(mask != Otc::SpriteNoMask) {
        if(!sprite.masks[mask])
            sprite.masks[mask] = loadSpriteMask(sprite.texture, mask);
        return sprite.masks[mask];
    } else
        return sprite.texture;
}
<|endoftext|>


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

#include "repo_property_tree.h"
#include "../core//model/repo_node_utils.h"

using namespace repo::lib;

static const std::string XML_ATTR_TAG = "<xmlattr>";

PropertyTree::PropertyTree() :
	hackStrings(true)
{
}

PropertyTree::PropertyTree(const bool &enableJSONWorkAround) :
	hackStrings(false)
{}

PropertyTree::~PropertyTree()
{
}

//see http://stackoverflow.com/questions/2855741/why-boost-property-tree-write-json-saves-everything-as-string-is-it-possible-to
struct stringTranslator
{
	typedef std::string internal_type;
	typedef std::string external_type;

	boost::optional<std::string> get_value(const std::string &v) { return  v.substr(1, v.size() - 2); }
	boost::optional<std::string> put_value(const std::string &v) { return '"' + v + '"'; }
};

template <>
void PropertyTree::addFieldAttribute(
	const std::string &label,
	const std::string &attribute,
	const std::string &value
	)
{
	std::string actualLabel = XML_ATTR_TAG;

	if (!label.empty())
		actualLabel = label + "." + actualLabel;

	addToTree(actualLabel + "." + attribute, value);
}

template <>
void PropertyTree::addFieldAttribute(
	const std::string  &label,
	const std::string  &attribute,
	const repo_vector_t &value
	)
{

	std::stringstream ss;
	ss << value.x << "," << value.x << "," << value.z ;

	addFieldAttribute(label, attribute, ss.str());
}

template <>
void PropertyTree::addToTree<std::string>(
	const std::string           &label,
	const std::string           &value)
{
	if (hackStrings)
	{
		if (label.empty())
			tree.put(label, value, stringTranslator());
		else
			tree.add(label, value, stringTranslator());
	}
	else
	{
		if (label.empty())
			tree.put(label, value);
		else
			tree.add(label, value);
	}

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

#include "repo_property_tree.h"
#include "../core//model/repo_node_utils.h"

using namespace repo::lib;

static const std::string XML_ATTR_TAG = "<xmlattr>";

PropertyTree::PropertyTree() :
	hackStrings(true)
{
}

PropertyTree::PropertyTree(const bool &enableJSONWorkAround) :
	hackStrings(false)
{}

PropertyTree::~PropertyTree()
{
}

//see http://stackoverflow.com/questions/2855741/why-boost-property-tree-write-json-saves-everything-as-string-is-it-possible-to
struct stringTranslator
{
	typedef std::string internal_type;
	typedef std::string external_type;

	boost::optional<std::string> get_value(const std::string &v) { return  v.substr(1, v.size() - 2); }
	boost::optional<std::string> put_value(const std::string &v) { return '"' + v + '"'; }
};

template <>
void PropertyTree::addFieldAttribute(
	const std::string &label,
	const std::string &attribute,
	const std::string &value
	)
{
	std::string actualLabel = XML_ATTR_TAG;

	if (!label.empty())
		actualLabel = label + "." + actualLabel;

	addToTree(actualLabel + "." + attribute, value);
}

template <>
void PropertyTree::addFieldAttribute(
	const std::string  &label,
	const std::string  &attribute,
	const repo_vector_t &value
	)
{

	std::stringstream ss;
	ss << value.x << "," << value.y << "," << value.z ;

	addFieldAttribute(label, attribute, ss.str());
}

template <>
void PropertyTree::addToTree<std::string>(
	const std::string           &label,
	const std::string           &value)
{
	if (hackStrings)
	{
		if (label.empty())
			tree.put(label, value, stringTranslator());
		else
			tree.add(label, value, stringTranslator());
	}
	else
	{
		if (label.empty())
			tree.put(label, value);
		else
			tree.add(label, value);
	}

}
<|endoftext|>


<commit_before>#include "DepthSensor.h"
#include "librealsense/rs.hpp"

#include "cinder/app/app.h"
#include "cinder/Log.h"

using namespace ci;
using namespace ci::app;
using namespace std;
using namespace Kinect;

struct DeviceRealSense : public Device
{
    rs::context ctx;
    rs::device* dev = nullptr;
    float depthScale;

    const int32_t kWidth = 640;
    const int32_t kHeight = 480;

    static uint32_t getDeviceCount()
    {
        // TODO:
        return 1;
    }

    virtual float getDepthToMmScale()
    {
        return depthScale;
    }

    ~DeviceRealSense()
    {
    }

    // TODO:
    ivec2 getDepthSize() const
    {
        return{ kWidth, kHeight };
    }

    bool isValid() const
    {
        return dev != nullptr;;
    }

    DeviceRealSense(Option option)
    {
        this->option = option;

        if (ctx.get_device_count() == 0)
        {
            CI_LOG_E("Failed to connect to RealSense devices.");
            return;
        }

        dev = ctx.get_device(option.deviceId);
        printf("\nUsing device %s, an %s\n", option.deviceId, dev->get_name());
        printf("    Serial number: %s\n", dev->get_serial());
        printf("    Firmware version: %s\n", dev->get_firmware_version());

        if (option.enableDepth)
        {
            dev->enable_stream(rs::stream::depth, kWidth, kHeight, rs::format::z16, 60);
            depthScale = dev->get_depth_scale() * 1000;
        }

        if (option.enableColor)
        {
            dev->enable_stream(rs::stream::color, kWidth, kHeight, rs::format::rgb8, 60);
        }

        if (option.enableInfrared)
        {
            dev->enable_stream(rs::stream::infrared, kWidth, kHeight, rs::format::y8, 60);
        }

        dev->start();

        App::get()->getSignalUpdate().connect(std::bind(&DeviceRealSense::update, this));
    }

    void update()
    {
        if (option.enableDepth)
        {
            dev->wait_for_frames();

            if (option.enableDepth)
            {
                uint16_t* data = (uint16_t*)dev->get_frame_data(rs::stream::depth);
                depthChannel = Channel16u(kWidth, kHeight, sizeof(uint16_t) * kWidth, 1, data);
                signalDepthDirty.emit();
            }

            if (option.enableInfrared)
            {
                uint8_t* data = (uint8_t*)dev->get_frame_data(rs::stream::infrared);
                infraredChannel.is16bit = false;
                infraredChannel.u8 = Channel8u(kWidth, kHeight, sizeof(uint16_t) * kWidth, 1, data);
            }

            if (option.enableColor)
            {
                uint8_t* data = (uint8_t*)dev->get_frame_data(rs::stream::color);
                colorSurface = Surface8u(data, kWidth, kHeight, sizeof(uint8_t) * 3 * kWidth, SurfaceChannelOrder::RGB);
            }
        }
    }
};

uint32_t getRealSenseCount()
{
    return DeviceRealSense::getDeviceCount();
}

DeviceRef createRealSense(Device::Option option)
{
    return DeviceRef(new DeviceRealSense(option));
}
<commit_msg>Fix infrared stream for real sense.<commit_after>#include "DepthSensor.h"
#include "librealsense/rs.hpp"

#include "cinder/app/app.h"
#include "cinder/Log.h"

using namespace ci;
using namespace ci::app;
using namespace std;
using namespace Kinect;

struct DeviceRealSense : public Device
{
    rs::context ctx;
    rs::device* dev = nullptr;
    float depthScale;

    const int32_t kWidth = 640;
    const int32_t kHeight = 480;

    static uint32_t getDeviceCount()
    {
        // TODO:
        return 1;
    }

    virtual float getDepthToMmScale()
    {
        return depthScale;
    }

    ~DeviceRealSense()
    {
    }

    // TODO:
    ivec2 getDepthSize() const
    {
        return{ kWidth, kHeight };
    }

    bool isValid() const
    {
        return dev != nullptr;;
    }

    DeviceRealSense(Option option)
    {
        this->option = option;

        if (ctx.get_device_count() == 0)
        {
            CI_LOG_E("Failed to connect to RealSense devices.");
            return;
        }

        dev = ctx.get_device(option.deviceId);
        printf("\nUsing device %s, an %s\n", option.deviceId, dev->get_name());
        printf("    Serial number: %s\n", dev->get_serial());
        printf("    Firmware version: %s\n", dev->get_firmware_version());

        if (option.enableDepth)
        {
            dev->enable_stream(rs::stream::depth, kWidth, kHeight, rs::format::z16, 60);
            depthScale = dev->get_depth_scale() * 1000;
        }

        if (option.enableColor)
        {
            dev->enable_stream(rs::stream::color, kWidth, kHeight, rs::format::rgb8, 60);
        }

        if (option.enableInfrared)
        {
            dev->enable_stream(rs::stream::infrared, kWidth, kHeight, rs::format::y16, 60);
        }

        dev->start();

        App::get()->getSignalUpdate().connect(std::bind(&DeviceRealSense::update, this));
    }

    void update()
    {
        dev->wait_for_frames();

        if (option.enableDepth)
        {
            auto data = (uint16_t*)dev->get_frame_data(rs::stream::depth);
            depthChannel = Channel16u(kWidth, kHeight, sizeof(uint16_t) * kWidth, 1, data);
            signalDepthDirty.emit();
        }

        if (option.enableInfrared)
        {
            auto data = (uint16_t*)dev->get_frame_data(rs::stream::infrared);
            infraredChannel.is16bit = true;
            infraredChannel.u16 = Channel16u(kWidth, kHeight, sizeof(uint16_t) * kWidth, 1, data);
            signalInfraredDirty.emit();
        }

        if (option.enableColor)
        {
            uint8_t* data = (uint8_t*)dev->get_frame_data(rs::stream::color);
            colorSurface = Surface8u(data, kWidth, kHeight, sizeof(uint8_t) * 3 * kWidth, SurfaceChannelOrder::RGB);
            signalColorDirty.emit();
        }
    }
};

uint32_t getRealSenseCount()
{
    return DeviceRealSense::getDeviceCount();
}

DeviceRef createRealSense(Device::Option option)
{
    return DeviceRef(new DeviceRealSense(option));
}
<|endoftext|>


<commit_before>//===- OutputSections.cpp -------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "OutputSections.h"

#include "Config.h"
#include "InputFiles.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/LEB128.h"

#define DEBUG_TYPE "lld"

using namespace llvm;
using namespace llvm::wasm;
using namespace lld;
using namespace lld::wasm;

enum class RelocEncoding {
  Uleb128,
  Sleb128,
  I32,
};

static StringRef sectionTypeToString(uint32_t SectionType) {
  switch (SectionType) {
  case WASM_SEC_CUSTOM:
    return "CUSTOM";
  case WASM_SEC_TYPE:
    return "TYPE";
  case WASM_SEC_IMPORT:
    return "IMPORT";
  case WASM_SEC_FUNCTION:
    return "FUNCTION";
  case WASM_SEC_TABLE:
    return "TABLE";
  case WASM_SEC_MEMORY:
    return "MEMORY";
  case WASM_SEC_GLOBAL:
    return "GLOBAL";
  case WASM_SEC_EXPORT:
    return "EXPORT";
  case WASM_SEC_START:
    return "START";
  case WASM_SEC_ELEM:
    return "ELEM";
  case WASM_SEC_CODE:
    return "CODE";
  case WASM_SEC_DATA:
    return "DATA";
  default:
    fatal("invalid section type");
  }
}

std::string lld::toString(OutputSection *Section) {
  std::string rtn = sectionTypeToString(Section->Type);
  if (!Section->Name.empty())
    rtn += "(" + Section->Name + ")";
  return rtn;
}

static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
  DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
               << " index=" << Reloc.Reloc.Index << " value=" << Reloc.Value
               << " offset=" << Reloc.Reloc.Offset << "\n");
  Buf += Reloc.Reloc.Offset;
  int64_t ExistingValue;
  switch (Reloc.Reloc.Type) {
  case R_WEBASSEMBLY_TYPE_INDEX_LEB:
  case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
    ExistingValue = decodeULEB128(Buf);
    if (ExistingValue != Reloc.Reloc.Index) {
      DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
      assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
    }
    LLVM_FALLTHROUGH;
  case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
  case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
    encodeULEB128(Reloc.Value, Buf, 5);
    break;
  case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
    ExistingValue = decodeSLEB128(Buf);
    if (ExistingValue != Reloc.Reloc.Index) {
      DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
      assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
    }
    LLVM_FALLTHROUGH;
  case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
    encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
    break;
  case R_WEBASSEMBLY_TABLE_INDEX_I32:
  case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    support::endian::write32<support::little>(Buf, Reloc.Value);
    break;
  default:
    llvm_unreachable("unknown relocation type");
  }
}

static void applyRelocations(uint8_t *Buf,
                             ArrayRef<OutputRelocation> Relocs) {
  log("applyRelocations: count=" + Twine(Relocs.size()));
  for (const OutputRelocation &Reloc : Relocs) {
    applyRelocation(Buf, Reloc);
  }
}

// Relocations contain an index into the function, global or table index
// space of the input file.  This function takes a relocation and returns the
// relocated index (i.e. translates from the input index space to the output
// index space).
static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
  switch (Reloc.Type) {
  case R_WEBASSEMBLY_TYPE_INDEX_LEB:
    return File.relocateTypeIndex(Reloc.Index);
  case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
    return File.relocateFunctionIndex(Reloc.Index);
  case R_WEBASSEMBLY_TABLE_INDEX_I32:
  case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
    return File.relocateTableIndex(Reloc.Index);
  case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    return File.relocateGlobalIndex(Reloc.Index);
  default:
    llvm_unreachable("unknown relocation type");
  }
}

// Take a vector of relocations from an input file and create output
// relocations based on them. Calculates the updated index and offset for
// each relocation as well as the value to write out in the final binary.
static void calcRelocations(const ObjFile &File,
                            ArrayRef<WasmRelocation> Relocs,
                            std::vector<OutputRelocation> &OutputRelocs,
                            int32_t OutputOffset) {
  log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
  for (const WasmRelocation &Reloc : Relocs) {
    OutputRelocation NewReloc;
    NewReloc.Reloc = Reloc;
    NewReloc.Reloc.Offset += OutputOffset;
    DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
                 << " offset=" << Reloc.Offset
                 << " newOffset=" << NewReloc.Reloc.Offset << "\n");

    if (Config->EmitRelocs)
      NewReloc.NewIndex = calcNewIndex(File, Reloc);
    else
      NewReloc.NewIndex = UINT32_MAX;

    switch (Reloc.Type) {
    case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
      NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
      if (NewReloc.Value != UINT32_MAX)
        NewReloc.Value += Reloc.Addend;
      break;
    default:
      NewReloc.Value = calcNewIndex(File, Reloc);
      break;
    }

    OutputRelocs.emplace_back(NewReloc);
  }
}

void OutputSection::createHeader(size_t BodySize) {
  raw_string_ostream OS(Header);
  debugWrite(OS.tell(),
             "section type [" + Twine(sectionTypeToString(Type)) + "]");
  writeUleb128(OS, Type, nullptr);
  writeUleb128(OS, BodySize, "section size");
  OS.flush();
  log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
      " total=" + Twine(getSize()));
}

CodeSection::CodeSection(uint32_t NumFunctions, ArrayRef<ObjFile *> Objs)
    : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
  raw_string_ostream OS(CodeSectionHeader);
  writeUleb128(OS, NumFunctions, "function count");
  OS.flush();
  BodySize = CodeSectionHeader.size();

  for (ObjFile *File : InputObjects) {
    if (!File->CodeSection)
      continue;

    File->CodeOffset = BodySize;
    ArrayRef<uint8_t> Content = File->CodeSection->Content;
    unsigned HeaderSize = 0;
    decodeULEB128(Content.data(), &HeaderSize);

    calcRelocations(*File, File->CodeSection->Relocations,
                    File->CodeRelocations, BodySize - HeaderSize);

    size_t PayloadSize = Content.size() - HeaderSize;
    BodySize += PayloadSize;
  }

  createHeader(BodySize);
}

void CodeSection::writeTo(uint8_t *Buf) {
  log("writing " + toString(this));
  log(" size=" + Twine(getSize()));
  Buf += Offset;

  // Write section header
  memcpy(Buf, Header.data(), Header.size());
  Buf += Header.size();

  uint8_t *ContentsStart = Buf;

  // Write code section headers
  memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
  Buf += CodeSectionHeader.size();

  // Write code section bodies
  parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
    if (!File->CodeSection)
      return;

    ArrayRef<uint8_t> Content(File->CodeSection->Content);

    // Payload doesn't include the initial header (function count)
    unsigned HeaderSize = 0;
    decodeULEB128(Content.data(), &HeaderSize);

    size_t PayloadSize = Content.size() - HeaderSize;
    memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
           PayloadSize);

    log("applying relocations for: " + File->getName());
    if (File->CodeRelocations.size())
      applyRelocations(ContentsStart, File->CodeRelocations);
  });
}

uint32_t CodeSection::numRelocations() const {
  uint32_t Count = 0;
  for (ObjFile *File : InputObjects)
    Count += File->CodeRelocations.size();
  return Count;
}

void CodeSection::writeRelocations(raw_ostream &OS) const {
  for (ObjFile *File : InputObjects)
    for (const OutputRelocation &Reloc : File->CodeRelocations)
      writeReloc(OS, Reloc);
}

DataSection::DataSection(ArrayRef<OutputSegment *> Segments)
    : OutputSection(WASM_SEC_DATA), Segments(Segments) {
  raw_string_ostream OS(DataSectionHeader);

  writeUleb128(OS, Segments.size(), "data segment count");
  OS.flush();
  BodySize = DataSectionHeader.size();

  for (OutputSegment *Segment : Segments) {
    raw_string_ostream OS(Segment->Header);
    writeUleb128(OS, 0, "memory index");
    writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
    writeSleb128(OS, Segment->StartVA, "memory offset");
    writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
    writeUleb128(OS, Segment->Size, "segment size");
    OS.flush();
    Segment->setSectionOffset(BodySize);
    BodySize += Segment->Header.size();
    log("Data segment: size=" + Twine(Segment->Size));
    for (const InputSegment *InputSeg : Segment->InputSegments) {
      uint32_t InputOffset = InputSeg->getInputSectionOffset();
      uint32_t OutputOffset = Segment->getSectionOffset() +
                              Segment->Header.size() +
                              InputSeg->getOutputSegmentOffset();
      calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
                      OutputOffset - InputOffset);
    }
    BodySize += Segment->Size;
  }

  createHeader(BodySize);
}

void DataSection::writeTo(uint8_t *Buf) {
  log("writing " + toString(this) + " size=" + Twine(getSize()) +
      " body=" + Twine(BodySize));
  Buf += Offset;

  // Write section header
  memcpy(Buf, Header.data(), Header.size());
  Buf += Header.size();

  uint8_t *ContentsStart = Buf;

  // Write data section headers
  memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());

  parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
    // Write data segment header
    uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
    memcpy(SegStart, Segment->Header.data(), Segment->Header.size());

    // Write segment data payload
    for (const InputSegment *Input : Segment->InputSegments) {
      ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
      memcpy(SegStart + Segment->Header.size() +
                 Input->getOutputSegmentOffset(),
             Content.data(), Content.size());
    }
  });

  applyRelocations(ContentsStart, Relocations);
}

void DataSection::writeRelocations(raw_ostream &OS) const {
  for (const OutputRelocation &Reloc : Relocations)
    writeReloc(OS, Reloc);
}
<commit_msg>[WebAssebmly] Fix the single clang-format issue in `wasm` directory<commit_after>//===- OutputSections.cpp -------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "OutputSections.h"

#include "Config.h"
#include "InputFiles.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/LEB128.h"

#define DEBUG_TYPE "lld"

using namespace llvm;
using namespace llvm::wasm;
using namespace lld;
using namespace lld::wasm;

enum class RelocEncoding {
  Uleb128,
  Sleb128,
  I32,
};

static StringRef sectionTypeToString(uint32_t SectionType) {
  switch (SectionType) {
  case WASM_SEC_CUSTOM:
    return "CUSTOM";
  case WASM_SEC_TYPE:
    return "TYPE";
  case WASM_SEC_IMPORT:
    return "IMPORT";
  case WASM_SEC_FUNCTION:
    return "FUNCTION";
  case WASM_SEC_TABLE:
    return "TABLE";
  case WASM_SEC_MEMORY:
    return "MEMORY";
  case WASM_SEC_GLOBAL:
    return "GLOBAL";
  case WASM_SEC_EXPORT:
    return "EXPORT";
  case WASM_SEC_START:
    return "START";
  case WASM_SEC_ELEM:
    return "ELEM";
  case WASM_SEC_CODE:
    return "CODE";
  case WASM_SEC_DATA:
    return "DATA";
  default:
    fatal("invalid section type");
  }
}

std::string lld::toString(OutputSection *Section) {
  std::string rtn = sectionTypeToString(Section->Type);
  if (!Section->Name.empty())
    rtn += "(" + Section->Name + ")";
  return rtn;
}

static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
  DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
               << " index=" << Reloc.Reloc.Index << " value=" << Reloc.Value
               << " offset=" << Reloc.Reloc.Offset << "\n");
  Buf += Reloc.Reloc.Offset;
  int64_t ExistingValue;
  switch (Reloc.Reloc.Type) {
  case R_WEBASSEMBLY_TYPE_INDEX_LEB:
  case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
    ExistingValue = decodeULEB128(Buf);
    if (ExistingValue != Reloc.Reloc.Index) {
      DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
      assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
    }
    LLVM_FALLTHROUGH;
  case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
  case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
    encodeULEB128(Reloc.Value, Buf, 5);
    break;
  case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
    ExistingValue = decodeSLEB128(Buf);
    if (ExistingValue != Reloc.Reloc.Index) {
      DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
      assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
    }
    LLVM_FALLTHROUGH;
  case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
    encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
    break;
  case R_WEBASSEMBLY_TABLE_INDEX_I32:
  case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    support::endian::write32<support::little>(Buf, Reloc.Value);
    break;
  default:
    llvm_unreachable("unknown relocation type");
  }
}

static void applyRelocations(uint8_t *Buf, ArrayRef<OutputRelocation> Relocs) {
  log("applyRelocations: count=" + Twine(Relocs.size()));
  for (const OutputRelocation &Reloc : Relocs) {
    applyRelocation(Buf, Reloc);
  }
}

// Relocations contain an index into the function, global or table index
// space of the input file.  This function takes a relocation and returns the
// relocated index (i.e. translates from the input index space to the output
// index space).
static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
  switch (Reloc.Type) {
  case R_WEBASSEMBLY_TYPE_INDEX_LEB:
    return File.relocateTypeIndex(Reloc.Index);
  case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
    return File.relocateFunctionIndex(Reloc.Index);
  case R_WEBASSEMBLY_TABLE_INDEX_I32:
  case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
    return File.relocateTableIndex(Reloc.Index);
  case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
  case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    return File.relocateGlobalIndex(Reloc.Index);
  default:
    llvm_unreachable("unknown relocation type");
  }
}

// Take a vector of relocations from an input file and create output
// relocations based on them. Calculates the updated index and offset for
// each relocation as well as the value to write out in the final binary.
static void calcRelocations(const ObjFile &File,
                            ArrayRef<WasmRelocation> Relocs,
                            std::vector<OutputRelocation> &OutputRelocs,
                            int32_t OutputOffset) {
  log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
  for (const WasmRelocation &Reloc : Relocs) {
    OutputRelocation NewReloc;
    NewReloc.Reloc = Reloc;
    NewReloc.Reloc.Offset += OutputOffset;
    DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
                 << " offset=" << Reloc.Offset
                 << " newOffset=" << NewReloc.Reloc.Offset << "\n");

    if (Config->EmitRelocs)
      NewReloc.NewIndex = calcNewIndex(File, Reloc);
    else
      NewReloc.NewIndex = UINT32_MAX;

    switch (Reloc.Type) {
    case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_I32:
    case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
      NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
      if (NewReloc.Value != UINT32_MAX)
        NewReloc.Value += Reloc.Addend;
      break;
    default:
      NewReloc.Value = calcNewIndex(File, Reloc);
      break;
    }

    OutputRelocs.emplace_back(NewReloc);
  }
}

void OutputSection::createHeader(size_t BodySize) {
  raw_string_ostream OS(Header);
  debugWrite(OS.tell(),
             "section type [" + Twine(sectionTypeToString(Type)) + "]");
  writeUleb128(OS, Type, nullptr);
  writeUleb128(OS, BodySize, "section size");
  OS.flush();
  log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
      " total=" + Twine(getSize()));
}

CodeSection::CodeSection(uint32_t NumFunctions, ArrayRef<ObjFile *> Objs)
    : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
  raw_string_ostream OS(CodeSectionHeader);
  writeUleb128(OS, NumFunctions, "function count");
  OS.flush();
  BodySize = CodeSectionHeader.size();

  for (ObjFile *File : InputObjects) {
    if (!File->CodeSection)
      continue;

    File->CodeOffset = BodySize;
    ArrayRef<uint8_t> Content = File->CodeSection->Content;
    unsigned HeaderSize = 0;
    decodeULEB128(Content.data(), &HeaderSize);

    calcRelocations(*File, File->CodeSection->Relocations,
                    File->CodeRelocations, BodySize - HeaderSize);

    size_t PayloadSize = Content.size() - HeaderSize;
    BodySize += PayloadSize;
  }

  createHeader(BodySize);
}

void CodeSection::writeTo(uint8_t *Buf) {
  log("writing " + toString(this));
  log(" size=" + Twine(getSize()));
  Buf += Offset;

  // Write section header
  memcpy(Buf, Header.data(), Header.size());
  Buf += Header.size();

  uint8_t *ContentsStart = Buf;

  // Write code section headers
  memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
  Buf += CodeSectionHeader.size();

  // Write code section bodies
  parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
    if (!File->CodeSection)
      return;

    ArrayRef<uint8_t> Content(File->CodeSection->Content);

    // Payload doesn't include the initial header (function count)
    unsigned HeaderSize = 0;
    decodeULEB128(Content.data(), &HeaderSize);

    size_t PayloadSize = Content.size() - HeaderSize;
    memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
           PayloadSize);

    log("applying relocations for: " + File->getName());
    if (File->CodeRelocations.size())
      applyRelocations(ContentsStart, File->CodeRelocations);
  });
}

uint32_t CodeSection::numRelocations() const {
  uint32_t Count = 0;
  for (ObjFile *File : InputObjects)
    Count += File->CodeRelocations.size();
  return Count;
}

void CodeSection::writeRelocations(raw_ostream &OS) const {
  for (ObjFile *File : InputObjects)
    for (const OutputRelocation &Reloc : File->CodeRelocations)
      writeReloc(OS, Reloc);
}

DataSection::DataSection(ArrayRef<OutputSegment *> Segments)
    : OutputSection(WASM_SEC_DATA), Segments(Segments) {
  raw_string_ostream OS(DataSectionHeader);

  writeUleb128(OS, Segments.size(), "data segment count");
  OS.flush();
  BodySize = DataSectionHeader.size();

  for (OutputSegment *Segment : Segments) {
    raw_string_ostream OS(Segment->Header);
    writeUleb128(OS, 0, "memory index");
    writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
    writeSleb128(OS, Segment->StartVA, "memory offset");
    writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
    writeUleb128(OS, Segment->Size, "segment size");
    OS.flush();
    Segment->setSectionOffset(BodySize);
    BodySize += Segment->Header.size();
    log("Data segment: size=" + Twine(Segment->Size));
    for (const InputSegment *InputSeg : Segment->InputSegments) {
      uint32_t InputOffset = InputSeg->getInputSectionOffset();
      uint32_t OutputOffset = Segment->getSectionOffset() +
                              Segment->Header.size() +
                              InputSeg->getOutputSegmentOffset();
      calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
                      OutputOffset - InputOffset);
    }
    BodySize += Segment->Size;
  }

  createHeader(BodySize);
}

void DataSection::writeTo(uint8_t *Buf) {
  log("writing " + toString(this) + " size=" + Twine(getSize()) +
      " body=" + Twine(BodySize));
  Buf += Offset;

  // Write section header
  memcpy(Buf, Header.data(), Header.size());
  Buf += Header.size();

  uint8_t *ContentsStart = Buf;

  // Write data section headers
  memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());

  parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
    // Write data segment header
    uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
    memcpy(SegStart, Segment->Header.data(), Segment->Header.size());

    // Write segment data payload
    for (const InputSegment *Input : Segment->InputSegments) {
      ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
      memcpy(SegStart + Segment->Header.size() +
                 Input->getOutputSegmentOffset(),
             Content.data(), Content.size());
    }
  });

  applyRelocations(ContentsStart, Relocations);
}

void DataSection::writeRelocations(raw_ostream &OS) const {
  for (const OutputRelocation &Reloc : Relocations)
    writeReloc(OS, Reloc);
}
<|endoftext|>


<commit_before>#include "adapter.h"

static VALUE cSwiftAdapter;

static void adapter_free(dbi::Handle *handle) {
    if (handle) {
      handle->conn()->cleanup();
      delete handle;
    }
}

VALUE adapter_alloc(VALUE klass) {
    dbi::Handle *handle = 0;
    return Data_Wrap_Struct(klass, 0, adapter_free, handle);
}

dbi::Handle* adapter_handle(VALUE self) {
  dbi::Handle *handle;
  Data_Get_Struct(self, dbi::Handle, handle);
  if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");

  return handle;
}

static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);

  dbi::Handle *handle = adapter_handle(self);
  try {
    NIL_P(save_point) ? handle->begin() : handle->begin(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

static VALUE adapter_close(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try { handle->close(); } CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

// TODO:
static VALUE adapter_clone(VALUE self) {
  rb_raise(eSwiftRuntimeError, "clone is not allowed.");
}

static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);
  dbi::Handle *handle = adapter_handle(self);

  try {
    NIL_P(save_point) ? handle->commit() : handle->commit(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

// TODO:
static VALUE adapter_dup(VALUE self) {
  rb_raise(eSwiftRuntimeError, "dup is not allowed.");
}

// TODO: Attempt TO_S() before escaping?
static VALUE adapter_escape(VALUE self, VALUE value) {
  if (TYPE(value) != T_STRING) rb_raise(eSwiftArgumentError, "Cannot escape non-string value.");

  dbi::Handle *handle = adapter_handle(self);
  try {
    std::string safe = handle->escape(std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
    return rb_str_new(safe.data(), safe.length());
  }
  CATCH_DBI_EXCEPTIONS();
}

// TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array.
static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
  VALUE statement, bind_values, block, rows;

  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "1*&", &statement, &bind_values, &block);

  try {
    Query query;
    query.sql    = CSTRING(statement);
    query.handle = handle;

    if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values, handle->driver());
    if (dbi::_trace)                 dbi::logMessage(dbi::_trace_fd, dbi::formatParams(query.sql, query.bind));

    if ((rows = rb_thread_blocking_region(((VALUE (*)(void*))query_execute), &query, RUBY_UBF_IO, 0)) == Qfalse)
      rb_raise(eSwiftRuntimeError, "%s", query.error);

    if (rb_block_given_p()) {
      dbi::AbstractResult *result = handle->results();
      return result_wrap_handle(cSwiftResult, self, result, false);
    }
    else
      return rows;
  }
  CATCH_DBI_EXCEPTIONS();
}

static VALUE adapter_initialize(VALUE self, VALUE options) {
  VALUE db       = rb_hash_aref(options, ID2SYM(rb_intern("db")));
  VALUE driver   = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
  VALUE user     = rb_hash_aref(options, ID2SYM(rb_intern("user")));

  if (NIL_P(db))     rb_raise(eSwiftArgumentError, "Adapter#new called without :db");
  if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver");

  user = NIL_P(user) ? rb_str_new2(getlogin()) : user;

  try {
    DATA_PTR(self) = new dbi::Handle(
      CSTRING(driver),
      CSTRING(user),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
      CSTRING(db),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
    );
  }
  CATCH_DBI_EXCEPTIONS();

  rb_iv_set(self, "@options", options);
  return Qnil;
}

static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
  VALUE sql, scheme, prepared;
  dbi::AbstractStatement *statement;

  rb_scan_args(argc, argv, "11", &scheme, &sql);
  if (TYPE(scheme) != T_CLASS) {
    sql    = scheme;
    scheme = Qnil;
  }

  dbi::Handle *handle = adapter_handle(self);
  try {
    // TODO: Move to statement_* constructor.
    statement = handle->conn()->prepare(CSTRING(sql));
    prepared  = statement_wrap_handle(cSwiftStatement, self, statement);
    rb_iv_set(prepared, "@scheme",  scheme);
    return prepared;
  }
  CATCH_DBI_EXCEPTIONS();
}

static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "01", &save_point);

  try {
    NIL_P(save_point) ? handle->rollback() : handle->rollback(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
  int status;
  VALUE sp, block;

  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "01&", &sp, &block);

  if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block.");
  std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp);

  try {
    handle->begin(save_point);
    rb_protect(rb_yield, self, &status);
    if (!status && handle->transactions().size() > 0) {
      handle->commit(save_point);
    }
    else if (status && handle->transactions().size() > 0) {
      handle->rollback(save_point);
      rb_jump_tag(status);
    }
  }
  CATCH_DBI_EXCEPTIONS();

  return Qtrue;
}

static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
  uint64_t rows = 0;
  VALUE stream, table, fields;
  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "30", &table, &fields, &stream);
  if (TYPE(stream) != T_STRING && !rb_respond_to(stream, rb_intern("read")))
    rb_raise(eSwiftArgumentError, "Stream must be a String or IO object.");
  if (TYPE(fields) != T_ARRAY)
    rb_raise(eSwiftArgumentError, "Fields must be an Array.");

  try {
    dbi::FieldSet write_fields;
    for (int i = 0; i < RARRAY_LEN(fields); i++) {
      VALUE field = TO_S(rb_ary_entry(fields, i));
      write_fields << std::string(RSTRING_PTR(field), RSTRING_LEN(field));
    }

    /*
      TODO: Adapter specific code is balls.
      This is just for the friggin mysql support - mysql does not like a statement close command being send on a
      handle when the writing has started.
    */
    rb_gc();

    if (TYPE(stream) == T_STRING) {
      dbi::IOStream io(RSTRING_PTR(stream), RSTRING_LEN(stream));
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    else {
      IOStream io(stream);
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    return SIZET2NUM(rows);
  }
  CATCH_DBI_EXCEPTIONS();
}

VALUE adapter_results(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try {
    dbi::AbstractResult *result = handle->results();
    return result_wrap_handle(cSwiftResult, self, result, false);
  }
  CATCH_DBI_EXCEPTIONS();
}

void init_swift_adapter() {
  VALUE mSwift   = rb_define_module("Swift");
  cSwiftAdapter = rb_define_class_under(mSwift, "Adapter", rb_cObject);

  rb_define_method(cSwiftAdapter, "begin",       RUBY_METHOD_FUNC(adapter_begin),       -1);
  rb_define_method(cSwiftAdapter, "clone",       RUBY_METHOD_FUNC(adapter_clone),       0);
  rb_define_method(cSwiftAdapter, "close",       RUBY_METHOD_FUNC(adapter_close),       0);
  rb_define_method(cSwiftAdapter, "commit",      RUBY_METHOD_FUNC(adapter_commit),      -1);
  rb_define_method(cSwiftAdapter, "dup",         RUBY_METHOD_FUNC(adapter_dup),         0);
  rb_define_method(cSwiftAdapter, "escape",      RUBY_METHOD_FUNC(adapter_escape),      1);
  rb_define_method(cSwiftAdapter, "execute",     RUBY_METHOD_FUNC(adapter_execute),     -1);
  rb_define_method(cSwiftAdapter, "initialize",  RUBY_METHOD_FUNC(adapter_initialize),  1);
  rb_define_method(cSwiftAdapter, "prepare",     RUBY_METHOD_FUNC(adapter_prepare),     -1);
  rb_define_method(cSwiftAdapter, "rollback",    RUBY_METHOD_FUNC(adapter_rollback),    -1);
  rb_define_method(cSwiftAdapter, "transaction", RUBY_METHOD_FUNC(adapter_transaction), -1);
  rb_define_method(cSwiftAdapter, "write",       RUBY_METHOD_FUNC(adapter_write),       -1);

  rb_define_alloc_func(cSwiftAdapter, adapter_alloc);

  // TODO Figure out how to avoid race conditions.
  rb_define_method(cSwiftAdapter, "results",     RUBY_METHOD_FUNC(adapter_results),     0);
}

<commit_msg>fix: return result_each from adapter#execute<commit_after>#include "adapter.h"

static VALUE cSwiftAdapter;

static void adapter_free(dbi::Handle *handle) {
    if (handle) {
      handle->conn()->cleanup();
      delete handle;
    }
}

VALUE adapter_alloc(VALUE klass) {
    dbi::Handle *handle = 0;
    return Data_Wrap_Struct(klass, 0, adapter_free, handle);
}

dbi::Handle* adapter_handle(VALUE self) {
  dbi::Handle *handle;
  Data_Get_Struct(self, dbi::Handle, handle);
  if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");

  return handle;
}

static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);

  dbi::Handle *handle = adapter_handle(self);
  try {
    NIL_P(save_point) ? handle->begin() : handle->begin(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

static VALUE adapter_close(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try { handle->close(); } CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

// TODO:
static VALUE adapter_clone(VALUE self) {
  rb_raise(eSwiftRuntimeError, "clone is not allowed.");
}

static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);
  dbi::Handle *handle = adapter_handle(self);

  try {
    NIL_P(save_point) ? handle->commit() : handle->commit(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

// TODO:
static VALUE adapter_dup(VALUE self) {
  rb_raise(eSwiftRuntimeError, "dup is not allowed.");
}

// TODO: Attempt TO_S() before escaping?
static VALUE adapter_escape(VALUE self, VALUE value) {
  if (TYPE(value) != T_STRING) rb_raise(eSwiftArgumentError, "Cannot escape non-string value.");

  dbi::Handle *handle = adapter_handle(self);
  try {
    std::string safe = handle->escape(std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
    return rb_str_new(safe.data(), safe.length());
  }
  CATCH_DBI_EXCEPTIONS();
}

// TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array.
static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
  VALUE statement, bind_values, block, rows;

  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "1*&", &statement, &bind_values, &block);

  try {
    Query query;
    query.sql    = CSTRING(statement);
    query.handle = handle;

    if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values, handle->driver());
    if (dbi::_trace)                 dbi::logMessage(dbi::_trace_fd, dbi::formatParams(query.sql, query.bind));

    if ((rows = rb_thread_blocking_region(((VALUE (*)(void*))query_execute), &query, RUBY_UBF_IO, 0)) == Qfalse)
      rb_raise(eSwiftRuntimeError, "%s", query.error);

    if (rb_block_given_p()) {
      dbi::AbstractResult *result = handle->results();
      return result_each(result_wrap_handle(cSwiftResult, self, result, false));
    }
    else
      return rows;
  }
  CATCH_DBI_EXCEPTIONS();
}

static VALUE adapter_initialize(VALUE self, VALUE options) {
  VALUE db       = rb_hash_aref(options, ID2SYM(rb_intern("db")));
  VALUE driver   = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
  VALUE user     = rb_hash_aref(options, ID2SYM(rb_intern("user")));

  if (NIL_P(db))     rb_raise(eSwiftArgumentError, "Adapter#new called without :db");
  if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver");

  user = NIL_P(user) ? rb_str_new2(getlogin()) : user;

  try {
    DATA_PTR(self) = new dbi::Handle(
      CSTRING(driver),
      CSTRING(user),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
      CSTRING(db),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
    );
  }
  CATCH_DBI_EXCEPTIONS();

  rb_iv_set(self, "@options", options);
  return Qnil;
}

static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
  VALUE sql, scheme, prepared;
  dbi::AbstractStatement *statement;

  rb_scan_args(argc, argv, "11", &scheme, &sql);
  if (TYPE(scheme) != T_CLASS) {
    sql    = scheme;
    scheme = Qnil;
  }

  dbi::Handle *handle = adapter_handle(self);
  try {
    // TODO: Move to statement_* constructor.
    statement = handle->conn()->prepare(CSTRING(sql));
    prepared  = statement_wrap_handle(cSwiftStatement, self, statement);
    rb_iv_set(prepared, "@scheme",  scheme);
    return prepared;
  }
  CATCH_DBI_EXCEPTIONS();
}

static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "01", &save_point);

  try {
    NIL_P(save_point) ? handle->rollback() : handle->rollback(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
  int status;
  VALUE sp, block;

  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "01&", &sp, &block);

  if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block.");
  std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp);

  try {
    handle->begin(save_point);
    rb_protect(rb_yield, self, &status);
    if (!status && handle->transactions().size() > 0) {
      handle->commit(save_point);
    }
    else if (status && handle->transactions().size() > 0) {
      handle->rollback(save_point);
      rb_jump_tag(status);
    }
  }
  CATCH_DBI_EXCEPTIONS();

  return Qtrue;
}

static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
  uint64_t rows = 0;
  VALUE stream, table, fields;
  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "30", &table, &fields, &stream);
  if (TYPE(stream) != T_STRING && !rb_respond_to(stream, rb_intern("read")))
    rb_raise(eSwiftArgumentError, "Stream must be a String or IO object.");
  if (TYPE(fields) != T_ARRAY)
    rb_raise(eSwiftArgumentError, "Fields must be an Array.");

  try {
    dbi::FieldSet write_fields;
    for (int i = 0; i < RARRAY_LEN(fields); i++) {
      VALUE field = TO_S(rb_ary_entry(fields, i));
      write_fields << std::string(RSTRING_PTR(field), RSTRING_LEN(field));
    }

    /*
      TODO: Adapter specific code is balls.
      This is just for the friggin mysql support - mysql does not like a statement close command being send on a
      handle when the writing has started.
    */
    rb_gc();

    if (TYPE(stream) == T_STRING) {
      dbi::IOStream io(RSTRING_PTR(stream), RSTRING_LEN(stream));
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    else {
      IOStream io(stream);
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    return SIZET2NUM(rows);
  }
  CATCH_DBI_EXCEPTIONS();
}

VALUE adapter_results(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try {
    dbi::AbstractResult *result = handle->results();
    return result_wrap_handle(cSwiftResult, self, result, false);
  }
  CATCH_DBI_EXCEPTIONS();
}

void init_swift_adapter() {
  VALUE mSwift   = rb_define_module("Swift");
  cSwiftAdapter = rb_define_class_under(mSwift, "Adapter", rb_cObject);

  rb_define_method(cSwiftAdapter, "begin",       RUBY_METHOD_FUNC(adapter_begin),       -1);
  rb_define_method(cSwiftAdapter, "clone",       RUBY_METHOD_FUNC(adapter_clone),       0);
  rb_define_method(cSwiftAdapter, "close",       RUBY_METHOD_FUNC(adapter_close),       0);
  rb_define_method(cSwiftAdapter, "commit",      RUBY_METHOD_FUNC(adapter_commit),      -1);
  rb_define_method(cSwiftAdapter, "dup",         RUBY_METHOD_FUNC(adapter_dup),         0);
  rb_define_method(cSwiftAdapter, "escape",      RUBY_METHOD_FUNC(adapter_escape),      1);
  rb_define_method(cSwiftAdapter, "execute",     RUBY_METHOD_FUNC(adapter_execute),     -1);
  rb_define_method(cSwiftAdapter, "initialize",  RUBY_METHOD_FUNC(adapter_initialize),  1);
  rb_define_method(cSwiftAdapter, "prepare",     RUBY_METHOD_FUNC(adapter_prepare),     -1);
  rb_define_method(cSwiftAdapter, "rollback",    RUBY_METHOD_FUNC(adapter_rollback),    -1);
  rb_define_method(cSwiftAdapter, "transaction", RUBY_METHOD_FUNC(adapter_transaction), -1);
  rb_define_method(cSwiftAdapter, "write",       RUBY_METHOD_FUNC(adapter_write),       -1);

  rb_define_alloc_func(cSwiftAdapter, adapter_alloc);

  // TODO Figure out how to avoid race conditions.
  rb_define_method(cSwiftAdapter, "results",     RUBY_METHOD_FUNC(adapter_results),     0);
}

<|endoftext|>