<commit_before>/****************************************************************************
**
** Copyright (C) 2012 - 2014 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertarget.h"

#include "merconstants.h"
#include "merqtversion.h"
#include "mersdk.h"
#include "mersdkkitinformation.h"
#include "mersdkmanager.h"
#include "mertargetkitinformation.h"
#include "mertoolchain.h"

#include <debugger/debuggeritemmanager.h>
#include <debugger/debuggerkitinformation.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionfactory.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>

#include <QDir>
#include <QStringList>

namespace Mer {
namespace Internal {

using namespace Mer::Constants;

const char* wrapperScripts[] =
{
    MER_WRAPPER_QMAKE,
    MER_WRAPPER_MAKE,
    MER_WRAPPER_GCC,
    MER_WRAPPER_DEPLOY,
    MER_WRAPPER_RPM,
    MER_WRAPPER_RPMBUILD,
    MER_WRAPPER_RPMVALIDATION,
};

MerTarget::MerTarget(MerSdk* mersdk):
    m_sdk(mersdk),
    m_defaultGdb(QLatin1String(Constants::MER_DEBUGGER_DEFAULT_FILENAME))
{
}

MerTarget::~MerTarget()
{
}

QString MerTarget::name() const
{
    return m_name;
}

void MerTarget::setName(const QString &name)
{
    m_name = name;
}

void MerTarget::setQmakeQuery(const QString &qmakeQuery)
{
    m_qmakeQuery = qmakeQuery;
}

void MerTarget::setGccDumpMachine(const QString &gccMachineDump)
{
    m_gccMachineDump = gccMachineDump;
    // the dump contains a linefeed
    m_gccMachineDump.remove(QRegExp(QLatin1String("\\n")));
}

void MerTarget::setDefaultGdb(const QString &name)
{
    m_defaultGdb=name;
}

bool MerTarget::fromMap(const QVariantMap &data)
{
    m_name = data.value(QLatin1String(Constants::MER_TARGET_NAME)).toString();
    m_qmakeQuery = data.value(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP)).toString();
    m_gccMachineDump = data.value(QLatin1String(Constants::MER_TARGET_GCC_DUMP)).toString();
    m_defaultGdb = data.value(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER)).toString();
    return isValid();
}

QVariantMap MerTarget::toMap() const
{
    QVariantMap result;
    result.insert(QLatin1String(Constants::MER_TARGET_NAME), m_name);
    result.insert(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP), m_qmakeQuery);
    result.insert(QLatin1String(Constants::MER_TARGET_GCC_DUMP), m_gccMachineDump);
    result.insert(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER), m_defaultGdb);
    return result;
}

bool MerTarget::isValid() const
{
    return !m_name.isEmpty() && !m_qmakeQuery.isEmpty() && !m_gccMachineDump.isEmpty();
}

QString MerTarget::targetPath() const
{
    return MerSdkManager::sdkToolsDirectory() + m_sdk->virtualMachineName() + QLatin1Char('/') + m_name;
}

bool MerTarget::createScripts() const
{
    if (!isValid())
        return false;

    const QString targetPath(this->targetPath());

    QDir targetDir(targetPath);
    if (!targetDir.exists() && !targetDir.mkpath(targetPath)) {
        qWarning() << "Could not create target directory." << targetDir;
        return false;
    }
    bool result = true;
    for (size_t i = 0; i < sizeof(wrapperScripts) / sizeof(wrapperScripts[0]); ++i)
         result &= createScript(targetPath, i);

    const QString qmakepath = targetPath + QLatin1Char('/') + QLatin1String(Constants::QMAKE_QUERY);
    const QString gccpath = targetPath + QLatin1Char('/') + QLatin1String(Constants::GCC_DUMPMACHINE);

    QString patchedQmakeQuery = m_qmakeQuery;
    patchedQmakeQuery.replace(QLatin1String(":/"),
                              QString::fromLatin1(":%1/%2/").arg(m_sdk->sharedTargetsPath()).arg(m_name));

    result &= createCacheFile(qmakepath, patchedQmakeQuery);
    result &= createCacheFile(gccpath, m_gccMachineDump);

    return result;
}

void MerTarget::deleteScripts() const
{
    Utils::FileUtils::removeRecursively(Utils::FileName::fromString(targetPath()));
}

ProjectExplorer::Kit* MerTarget::createKit() const
{
    if (!isValid())
        return 0;
    const QString sysroot(m_sdk->sharedTargetsPath() + QLatin1String("/") + m_name);

    Utils::FileName path = Utils::FileName::fromString(sysroot);
    if (!path.toFileInfo().exists()) {
        qWarning() << "Sysroot does not exist" << sysroot;
        return 0;
    }

    ProjectExplorer::Kit *k = new ProjectExplorer::Kit();
    k->setAutoDetected(true);
    k->setDisplayName(QString::fromLatin1("%1-%2").arg(m_sdk->virtualMachineName(), m_name));
    k->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::MER_OPTIONS_CATEGORY_ICON)));
    ProjectExplorer::SysRootKitInformation::setSysRoot(k, Utils::FileName::fromUserInput(sysroot));

    ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE);
    k->setMutable(ProjectExplorer::DeviceKitInformation::id(), true);

    const QString gdb = Utils::HostOsInfo::withExecutableSuffix(m_defaultGdb);
    QString gdbDir = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost()) {
        QDir dir = QDir(gdbDir);
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
        gdbDir = dir.path();
    }
    Utils::FileName gdbFileName = Utils::FileName::fromString(gdbDir + QLatin1String("/") + gdb);

    Debugger::DebuggerItem debugger;
    debugger.setCommand(gdbFileName);
    debugger.setEngineType(Debugger::GdbEngineType);
    const QString vmName = m_sdk->virtualMachineName();
    debugger.setDisplayName(QObject::tr("GDB for %1 %2").arg(vmName, m_name));
    debugger.setAutoDetected(true);
    debugger.setAbi(ProjectExplorer::Abi::abiFromTargetTriplet(m_gccMachineDump)); // TODO is this OK?
    QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger);
    Debugger::DebuggerKitInformation::setDebugger(k, id);

    MerSdkKitInformation::setSdk(k,m_sdk);
    MerTargetKitInformation::setTargetName(k,name());
    return k;
}

MerQtVersion* MerTarget::createQtVersion() const
{
    const Utils::FileName qmake =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_QMAKE));
    // Is there a qtversion present for this qmake?
    QtSupport::BaseQtVersion *qtv = QtSupport::QtVersionManager::qtVersionForQMakeBinary(qmake);
    if (qtv && !qtv->isValid()) {
        QtSupport::QtVersionManager::removeVersion(qtv);
        qtv = 0;
    }
    if (!qtv)
        qtv = new MerQtVersion(qmake, true, targetPath());

    //QtSupport::QtVersionFactory::createQtVersionFromQMakePath(qmake, true, targetPath());

    QTC_ASSERT(qtv && qtv->type() == QLatin1String(Constants::MER_QT), return 0);

    MerQtVersion *merqtv = static_cast<MerQtVersion *>(qtv);
    const QString vmName = m_sdk->virtualMachineName();
    merqtv->setVirtualMachineName(vmName);
    merqtv->setTargetName(m_name);
    merqtv->setDisplayName(
                QString::fromLatin1("Qt %1 in %2 %3").arg(qtv->qtVersionString(),
                                                          vmName, m_name));
    return merqtv;
}

MerToolChain* MerTarget::createToolChain() const
{
    const Utils::FileName gcc =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_GCC));
    QList<ProjectExplorer::ToolChain *> toolChains = ProjectExplorer::ToolChainManager::toolChains();

    foreach (ProjectExplorer::ToolChain *tc, toolChains) {
        if (tc->compilerCommand() == gcc && tc->isAutoDetected()) {
            QTC_ASSERT(tc->type() == QLatin1String(Constants::MER_TOOLCHAIN_TYPE), return 0);
        }
    }

    MerToolChain* mertoolchain = new MerToolChain(ProjectExplorer::ToolChain::AutoDetection);
    const QString vmName = m_sdk->virtualMachineName();
    mertoolchain->setDisplayName(QString::fromLatin1("GCC (%1 %2)").arg(vmName, m_name));
    mertoolchain->setVirtualMachine(vmName);
    mertoolchain->setTargetName(m_name);
    mertoolchain->setCompilerCommand(gcc);
    return mertoolchain;
}

bool MerTarget::createScript(const QString &targetPath, int scriptIndex) const
{
    bool ok = false;
    const char* wrapperScriptCopy = wrapperScripts[scriptIndex];
    const QFile::Permissions rwrw = QFile::ReadOwner|QFile::ReadUser|QFile::ReadGroup
            |QFile::WriteOwner|QFile::WriteUser|QFile::WriteGroup;
    const QFile::Permissions rwxrwx = rwrw|QFile::ExeOwner|QFile::ExeUser|QFile::ExeGroup;

    QString wrapperScriptCommand = QLatin1String(wrapperScriptCopy);
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperScriptCommand.chop(4); // remove the ".cmd"

    QString wrapperBinaryPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperBinaryPath += QLatin1String("/merssh.exe");
    else if (Utils::HostOsInfo::isMacHost())
        wrapperBinaryPath += QLatin1String("/../Resources/merssh");
    else
        wrapperBinaryPath += QLatin1String("/merssh");

    using namespace Utils;
    const QString scriptCopyPath = targetPath + QLatin1Char('/') + QLatin1String(wrapperScriptCopy);
    QDir targetDir(targetPath);
    const QString targetName = targetDir.dirName();
    targetDir.cdUp();
    const QString merDevToolsDir = QDir::toNativeSeparators(targetDir.canonicalPath());
    QFile script(scriptCopyPath);
    ok = script.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open script" << scriptCopyPath;
        return false;
    }

    QString scriptContent;

    if (HostOsInfo::isWindowsHost()) {
        scriptContent += QLatin1String("@echo off\nSetLocal EnableDelayedExpansion\n");
        scriptContent += QLatin1String("set ARGUMENTS=\nFOR %%a IN (%*) DO set ARGUMENTS=!ARGUMENTS! ^ '%%a'\n");
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("SetLocal DisableDelayedExpansion\n");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("%ARGUMENTS%\n");
    }

    if (HostOsInfo::isAnyUnixHost()) {
        scriptContent += QLatin1String("#!/bin/sh\n");
        scriptContent += QLatin1String("ARGUMENTS=\"\";for ARGUMENT in \"$@\"; do ARGUMENTS=\"${ARGUMENTS} '${ARGUMENT}'\" ; done;\n");
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("exec ");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("${ARGUMENTS}");
    }

    ok = script.write(scriptContent.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write script" << scriptCopyPath;
        return false;
    }

    ok = QFile::setPermissions(scriptCopyPath, rwxrwx);
    if (!ok) {
        qWarning() << "Could not set file permissions on script" << scriptCopyPath;
        return false;
    }
    return ok;
}

bool MerTarget::createCacheFile(const QString &fileName, const QString &content) const
{
    bool ok = false;

    QFile file(fileName);
    ok = file.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open cache file" << fileName;
        return false;
    }

    ok = file.write(content.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write cache file " << fileName;
        return false;
    }
    file.close();
    return ok;
}

bool MerTarget::operator==(const MerTarget &other) const
{
    return m_name == other.m_name && m_qmakeQuery == other.m_qmakeQuery && m_gccMachineDump == other.m_gccMachineDump;
}

}
}
<commit_msg>Mer: Update after Kit: Introduce variables for Kit display names<commit_after>/****************************************************************************
**
** Copyright (C) 2012 - 2014 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertarget.h"

#include "merconstants.h"
#include "merqtversion.h"
#include "mersdk.h"
#include "mersdkkitinformation.h"
#include "mersdkmanager.h"
#include "mertargetkitinformation.h"
#include "mertoolchain.h"

#include <debugger/debuggeritemmanager.h>
#include <debugger/debuggerkitinformation.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionfactory.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>

#include <QDir>
#include <QStringList>

namespace Mer {
namespace Internal {

using namespace Mer::Constants;

const char* wrapperScripts[] =
{
    MER_WRAPPER_QMAKE,
    MER_WRAPPER_MAKE,
    MER_WRAPPER_GCC,
    MER_WRAPPER_DEPLOY,
    MER_WRAPPER_RPM,
    MER_WRAPPER_RPMBUILD,
    MER_WRAPPER_RPMVALIDATION,
};

MerTarget::MerTarget(MerSdk* mersdk):
    m_sdk(mersdk),
    m_defaultGdb(QLatin1String(Constants::MER_DEBUGGER_DEFAULT_FILENAME))
{
}

MerTarget::~MerTarget()
{
}

QString MerTarget::name() const
{
    return m_name;
}

void MerTarget::setName(const QString &name)
{
    m_name = name;
}

void MerTarget::setQmakeQuery(const QString &qmakeQuery)
{
    m_qmakeQuery = qmakeQuery;
}

void MerTarget::setGccDumpMachine(const QString &gccMachineDump)
{
    m_gccMachineDump = gccMachineDump;
    // the dump contains a linefeed
    m_gccMachineDump.remove(QRegExp(QLatin1String("\\n")));
}

void MerTarget::setDefaultGdb(const QString &name)
{
    m_defaultGdb=name;
}

bool MerTarget::fromMap(const QVariantMap &data)
{
    m_name = data.value(QLatin1String(Constants::MER_TARGET_NAME)).toString();
    m_qmakeQuery = data.value(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP)).toString();
    m_gccMachineDump = data.value(QLatin1String(Constants::MER_TARGET_GCC_DUMP)).toString();
    m_defaultGdb = data.value(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER)).toString();
    return isValid();
}

QVariantMap MerTarget::toMap() const
{
    QVariantMap result;
    result.insert(QLatin1String(Constants::MER_TARGET_NAME), m_name);
    result.insert(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP), m_qmakeQuery);
    result.insert(QLatin1String(Constants::MER_TARGET_GCC_DUMP), m_gccMachineDump);
    result.insert(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER), m_defaultGdb);
    return result;
}

bool MerTarget::isValid() const
{
    return !m_name.isEmpty() && !m_qmakeQuery.isEmpty() && !m_gccMachineDump.isEmpty();
}

QString MerTarget::targetPath() const
{
    return MerSdkManager::sdkToolsDirectory() + m_sdk->virtualMachineName() + QLatin1Char('/') + m_name;
}

bool MerTarget::createScripts() const
{
    if (!isValid())
        return false;

    const QString targetPath(this->targetPath());

    QDir targetDir(targetPath);
    if (!targetDir.exists() && !targetDir.mkpath(targetPath)) {
        qWarning() << "Could not create target directory." << targetDir;
        return false;
    }
    bool result = true;
    for (size_t i = 0; i < sizeof(wrapperScripts) / sizeof(wrapperScripts[0]); ++i)
         result &= createScript(targetPath, i);

    const QString qmakepath = targetPath + QLatin1Char('/') + QLatin1String(Constants::QMAKE_QUERY);
    const QString gccpath = targetPath + QLatin1Char('/') + QLatin1String(Constants::GCC_DUMPMACHINE);

    QString patchedQmakeQuery = m_qmakeQuery;
    patchedQmakeQuery.replace(QLatin1String(":/"),
                              QString::fromLatin1(":%1/%2/").arg(m_sdk->sharedTargetsPath()).arg(m_name));

    result &= createCacheFile(qmakepath, patchedQmakeQuery);
    result &= createCacheFile(gccpath, m_gccMachineDump);

    return result;
}

void MerTarget::deleteScripts() const
{
    Utils::FileUtils::removeRecursively(Utils::FileName::fromString(targetPath()));
}

ProjectExplorer::Kit* MerTarget::createKit() const
{
    if (!isValid())
        return 0;
    const QString sysroot(m_sdk->sharedTargetsPath() + QLatin1String("/") + m_name);

    Utils::FileName path = Utils::FileName::fromString(sysroot);
    if (!path.toFileInfo().exists()) {
        qWarning() << "Sysroot does not exist" << sysroot;
        return 0;
    }

    ProjectExplorer::Kit *k = new ProjectExplorer::Kit();
    k->setAutoDetected(true);
    k->setUnexpandedDisplayName(QString::fromLatin1("%1-%2").arg(m_sdk->virtualMachineName(), m_name));
    k->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::MER_OPTIONS_CATEGORY_ICON)));
    ProjectExplorer::SysRootKitInformation::setSysRoot(k, Utils::FileName::fromUserInput(sysroot));

    ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE);
    k->setMutable(ProjectExplorer::DeviceKitInformation::id(), true);

    const QString gdb = Utils::HostOsInfo::withExecutableSuffix(m_defaultGdb);
    QString gdbDir = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost()) {
        QDir dir = QDir(gdbDir);
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
        gdbDir = dir.path();
    }
    Utils::FileName gdbFileName = Utils::FileName::fromString(gdbDir + QLatin1String("/") + gdb);

    Debugger::DebuggerItem debugger;
    debugger.setCommand(gdbFileName);
    debugger.setEngineType(Debugger::GdbEngineType);
    const QString vmName = m_sdk->virtualMachineName();
    debugger.setDisplayName(QObject::tr("GDB for %1 %2").arg(vmName, m_name));
    debugger.setAutoDetected(true);
    debugger.setAbi(ProjectExplorer::Abi::abiFromTargetTriplet(m_gccMachineDump)); // TODO is this OK?
    QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger);
    Debugger::DebuggerKitInformation::setDebugger(k, id);

    MerSdkKitInformation::setSdk(k,m_sdk);
    MerTargetKitInformation::setTargetName(k,name());
    return k;
}

MerQtVersion* MerTarget::createQtVersion() const
{
    const Utils::FileName qmake =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_QMAKE));
    // Is there a qtversion present for this qmake?
    QtSupport::BaseQtVersion *qtv = QtSupport::QtVersionManager::qtVersionForQMakeBinary(qmake);
    if (qtv && !qtv->isValid()) {
        QtSupport::QtVersionManager::removeVersion(qtv);
        qtv = 0;
    }
    if (!qtv)
        qtv = new MerQtVersion(qmake, true, targetPath());

    //QtSupport::QtVersionFactory::createQtVersionFromQMakePath(qmake, true, targetPath());

    QTC_ASSERT(qtv && qtv->type() == QLatin1String(Constants::MER_QT), return 0);

    MerQtVersion *merqtv = static_cast<MerQtVersion *>(qtv);
    const QString vmName = m_sdk->virtualMachineName();
    merqtv->setVirtualMachineName(vmName);
    merqtv->setTargetName(m_name);
    merqtv->setDisplayName(
                QString::fromLatin1("Qt %1 in %2 %3").arg(qtv->qtVersionString(),
                                                          vmName, m_name));
    return merqtv;
}

MerToolChain* MerTarget::createToolChain() const
{
    const Utils::FileName gcc =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_GCC));
    QList<ProjectExplorer::ToolChain *> toolChains = ProjectExplorer::ToolChainManager::toolChains();

    foreach (ProjectExplorer::ToolChain *tc, toolChains) {
        if (tc->compilerCommand() == gcc && tc->isAutoDetected()) {
            QTC_ASSERT(tc->type() == QLatin1String(Constants::MER_TOOLCHAIN_TYPE), return 0);
        }
    }

    MerToolChain* mertoolchain = new MerToolChain(ProjectExplorer::ToolChain::AutoDetection);
    const QString vmName = m_sdk->virtualMachineName();
    mertoolchain->setDisplayName(QString::fromLatin1("GCC (%1 %2)").arg(vmName, m_name));
    mertoolchain->setVirtualMachine(vmName);
    mertoolchain->setTargetName(m_name);
    mertoolchain->setCompilerCommand(gcc);
    return mertoolchain;
}

bool MerTarget::createScript(const QString &targetPath, int scriptIndex) const
{
    bool ok = false;
    const char* wrapperScriptCopy = wrapperScripts[scriptIndex];
    const QFile::Permissions rwrw = QFile::ReadOwner|QFile::ReadUser|QFile::ReadGroup
            |QFile::WriteOwner|QFile::WriteUser|QFile::WriteGroup;
    const QFile::Permissions rwxrwx = rwrw|QFile::ExeOwner|QFile::ExeUser|QFile::ExeGroup;

    QString wrapperScriptCommand = QLatin1String(wrapperScriptCopy);
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperScriptCommand.chop(4); // remove the ".cmd"

    QString wrapperBinaryPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperBinaryPath += QLatin1String("/merssh.exe");
    else if (Utils::HostOsInfo::isMacHost())
        wrapperBinaryPath += QLatin1String("/../Resources/merssh");
    else
        wrapperBinaryPath += QLatin1String("/merssh");

    using namespace Utils;
    const QString scriptCopyPath = targetPath + QLatin1Char('/') + QLatin1String(wrapperScriptCopy);
    QDir targetDir(targetPath);
    const QString targetName = targetDir.dirName();
    targetDir.cdUp();
    const QString merDevToolsDir = QDir::toNativeSeparators(targetDir.canonicalPath());
    QFile script(scriptCopyPath);
    ok = script.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open script" << scriptCopyPath;
        return false;
    }

    QString scriptContent;

    if (HostOsInfo::isWindowsHost()) {
        scriptContent += QLatin1String("@echo off\nSetLocal EnableDelayedExpansion\n");
        scriptContent += QLatin1String("set ARGUMENTS=\nFOR %%a IN (%*) DO set ARGUMENTS=!ARGUMENTS! ^ '%%a'\n");
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("SetLocal DisableDelayedExpansion\n");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("%ARGUMENTS%\n");
    }

    if (HostOsInfo::isAnyUnixHost()) {
        scriptContent += QLatin1String("#!/bin/sh\n");
        scriptContent += QLatin1String("ARGUMENTS=\"\";for ARGUMENT in \"$@\"; do ARGUMENTS=\"${ARGUMENTS} '${ARGUMENT}'\" ; done;\n");
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("exec ");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("${ARGUMENTS}");
    }

    ok = script.write(scriptContent.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write script" << scriptCopyPath;
        return false;
    }

    ok = QFile::setPermissions(scriptCopyPath, rwxrwx);
    if (!ok) {
        qWarning() << "Could not set file permissions on script" << scriptCopyPath;
        return false;
    }
    return ok;
}

bool MerTarget::createCacheFile(const QString &fileName, const QString &content) const
{
    bool ok = false;

    QFile file(fileName);
    ok = file.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open cache file" << fileName;
        return false;
    }

    ok = file.write(content.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write cache file " << fileName;
        return false;
    }
    file.close();
    return ok;
}

bool MerTarget::operator==(const MerTarget &other) const
{
    return m_name == other.m_name && m_qmakeQuery == other.m_qmakeQuery && m_gccMachineDump == other.m_gccMachineDump;
}

}
}
<|endoftext|>


<commit_before>/* <x0/mod_accesslog.cpp>
 *
 * This file is part of the x0 web server project and is released under LGPL-3.
 *
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <x0/server.hpp>
#include <x0/request.hpp>
#include <x0/response.hpp>
#include <x0/header.hpp>
#include <x0/strutils.hpp>
#include <x0/types.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <cstring>
#include <cerrno>

/**
 * \ingroup modules
 * \brief implements an accesslog log facility - in spirit of "combined" mode of apache's accesslog logs.
 */
class accesslog_plugin :
	public x0::plugin
{
private:
	struct logstream
	{
		std::string filename_;
		int fd_;

		explicit logstream(const std::string& filename)
		{
			filename_ = filename;
			fd_ = ::open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
		}

		~logstream()
		{
			if (fd_)
				::close(fd_);
		}

		void write(const std::string message)
		{
			if (fd_ < 0)
				return;

			if (::write(fd_, message.c_str(), message.size()) < 0)
				DEBUG("Couldn't write accesslog(%s): %s", filename_.c_str(), strerror(errno));
		}
	};

	struct context
	{
		logstream *stream;

		context() : stream(0) {}
		context(const context& v) : stream(v.stream) {}
		~context() {}
	};

	logstream *getlogstream(const std::string& filename)
	{
		auto i = streams_.find(filename);

		if (i != streams_.end())
			return i->second.get();

		return (streams_[filename] = std::shared_ptr<logstream>(new logstream(filename))).get();
	}

	logstream *getlogstream(x0::request& in)
	{
		try
		{
			context& ctx = server_.context<context>(this, in.hostid());
			return ctx.stream;
		}
		catch (...)
		{
			return 0;
		}
	}

private:
	x0::signal<void(x0::request&, x0::response&)>::connection c;
	std::map<std::string, std::shared_ptr<logstream>> streams_;

public:
	accesslog_plugin(x0::server& srv, const std::string& name) :
		x0::plugin(srv, name),
		streams_()
	{
		c = srv.request_done.connect(boost::bind(&accesslog_plugin::request_done, this, _1, _2));
	}

	~accesslog_plugin()
	{
		server_.request_done.disconnect(c);
	}

	virtual void configure()
	{
		std::string default_filename;
		bool has_global = server_.config().load("AccessLog", default_filename);

		auto hosts = server_.config()["Hosts"].keys<std::string>();
		for (auto i = hosts.begin(), e = hosts.end(); i != e; ++i)
		{
			std::string hostid(*i);
			context& ctx = server_.create_context<context>(this, hostid);

			std::string filename;
			if (server_.config()["Hosts"][hostid]["AccessLog"].load(filename))
			{
				ctx.stream = getlogstream(filename);
			}
			else if (has_global)
			{
				ctx.stream = getlogstream(default_filename);
			}
		}
	}

private:
	void request_done(x0::request& in, x0::response& out)
	{
		if (auto stream = getlogstream(in))
		{
			std::stringstream sstr;
			sstr << hostname(in);
			sstr << " - "; // identity as of identd
			sstr << username(in) << ' ';
			sstr << now() << " \"";
			sstr << request_line(in) << "\" ";
			sstr << out.status << ' ';
			sstr << out.content_length() << ' ';
			sstr << '"' << getheader(in, "Referer") << "\" ";
			sstr << '"' << getheader(in, "User-Agent") << '"';
			sstr << std::endl;

			stream->write(sstr.str());
		}
	}

	inline std::string hostname(x0::request& in)
	{
		std::string name = in.connection.socket().remote_endpoint().address().to_string();
		return !name.empty() ? name : "-";
	}

	inline std::string username(x0::request& in)
	{
		return !in.username.empty() ? in.username : "-";
	}

	inline std::string request_line(x0::request& in)
	{
		std::stringstream str;

		str << in.method << ' ' << in.uri
			<< " HTTP/" << in.http_version_major << '.' << in.http_version_minor;

		return str.str();
	}

	inline std::string now()
	{
		return server_.now().htlog_str();
	}

	inline std::string getheader(const x0::request& in, const std::string& name)
	{
		std::string value(in.header(name));
		return !value.empty() ? value : "-";
	}
};

X0_EXPORT_PLUGIN(accesslog);
<commit_msg>code cleanup<commit_after>/* <x0/mod_accesslog.cpp>
 *
 * This file is part of the x0 web server project and is released under LGPL-3.
 *
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <x0/server.hpp>
#include <x0/request.hpp>
#include <x0/response.hpp>
#include <x0/header.hpp>
#include <x0/strutils.hpp>
#include <x0/types.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <cstring>
#include <cerrno>

/**
 * \ingroup modules
 * \brief implements an accesslog log facility - in spirit of "combined" mode of apache's accesslog logs.
 */
class accesslog_plugin :
	public x0::plugin
{
private:
	struct logstream
	{
		std::string filename_;
		int fd_;

		explicit logstream(const std::string& filename)
		{
			filename_ = filename;
			fd_ = ::open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
		}

		~logstream()
		{
			if (fd_)
				::close(fd_);
		}

		void write(const std::string message)
		{
			if (fd_ < 0)
				return;

			if (::write(fd_, message.c_str(), message.size()) < 0)
				DEBUG("Couldn't write accesslog(%s): %s", filename_.c_str(), strerror(errno));
		}
	};

	struct context
	{
		logstream *stream;

		context() : stream(0) {}
		context(const context& v) : stream(v.stream) {}
		~context() {}
	};

	logstream *getlogstream(const std::string& filename)
	{
		auto i = streams_.find(filename);

		if (i != streams_.end())
			return i->second.get();

		return (streams_[filename] = std::shared_ptr<logstream>(new logstream(filename))).get();
	}

	logstream *getlogstream(x0::request& in)
	{
		try
		{
			context& ctx = server_.context<context>(this, in.hostid());
			return ctx.stream;
		}
		catch (...)
		{
			return 0;
		}
	}

private:
	x0::signal<void(x0::request&, x0::response&)>::connection c;
	std::map<std::string, std::shared_ptr<logstream>> streams_;

public:
	accesslog_plugin(x0::server& srv, const std::string& name) :
		x0::plugin(srv, name),
		streams_()
	{
		c = srv.request_done.connect(boost::bind(&accesslog_plugin::request_done, this, _1, _2));
	}

	~accesslog_plugin()
	{
		server_.request_done.disconnect(c);
	}

	virtual void configure()
	{
		std::string default_filename;
		bool has_global = server_.config().load("AccessLog", default_filename);

		auto hosts = server_.config()["Hosts"].keys<std::string>();
		for (auto i = hosts.begin(), e = hosts.end(); i != e; ++i)
		{
			std::string hostid(*i);
			context& ctx = server_.create_context<context>(this, hostid);

			std::string filename;
			if (server_.config()["Hosts"][hostid]["AccessLog"].load(filename))
			{
				ctx.stream = getlogstream(filename);
			}
			else if (has_global)
			{
				ctx.stream = getlogstream(default_filename);
			}
		}
	}

private:
	void request_done(x0::request& in, x0::response& out)
	{
		if (auto stream = getlogstream(in))
		{
			std::stringstream sstr;
			sstr << hostname(in);
			sstr << " - "; // identity as of identd
			sstr << username(in) << ' ';
			sstr << server_.now().htlog_str() << " \"";
			sstr << request_line(in) << "\" ";
			sstr << out.status << ' ';
			sstr << out.content_length() << ' ';
			sstr << '"' << getheader(in, "Referer") << "\" ";
			sstr << '"' << getheader(in, "User-Agent") << '"';
			sstr << std::endl;

			stream->write(sstr.str());
		}
	}

	inline std::string hostname(x0::request& in)
	{
		std::string name = in.connection.socket().remote_endpoint().address().to_string();
		return !name.empty() ? name : "-";
	}

	inline std::string username(x0::request& in)
	{
		return !in.username.empty() ? in.username : "-";
	}

	inline std::string request_line(x0::request& in)
	{
		std::stringstream str;

		str << in.method << ' ' << in.uri
			<< " HTTP/" << in.http_version_major << '.' << in.http_version_minor;

		return str.str();
	}

	inline std::string getheader(const x0::request& in, const std::string& name)
	{
		std::string value(in.header(name));
		return !value.empty() ? value : "-";
	}
};

X0_EXPORT_PLUGIN(accesslog);
<|endoftext|>


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

/*
 * Input Capture Timer example. Take #1
 * TODO
 *
 * Wiring:
 * - TODO
 */

#include <fastarduino/flash.h>
#include <fastarduino/gpio.h>
#include <fastarduino/timer.h>

#if defined(ARDUINO_UNO) || defined(BREADBOARD_ATMEGA328P) || defined(ARDUINO_NANO)
#define HAS_UART 1
#define UART_NUM 0
static constexpr const board::USART UART = board::USART::USART0;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#elif defined (ARDUINO_MEGA)
#define HAS_UART 1
#define UART_NUM 0
static constexpr const board::USART UART = board::USART::USART0;
#define TIMER_NUM 4
static constexpr const board::Timer NTIMER = board::Timer::TIMER4;
#elif defined (ARDUINO_LEONARDO)
#define HAS_UART 1
#define UART_NUM 1
static constexpr const board::USART UART = board::USART::USART1;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#elif defined (BREADBOARD_ATTINYX4)
#define HAS_UART 0
static constexpr const board::DigitalPin TX = board::DigitalPin::D1_PA1;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#else
#error "Current target is not yet supported!"
#endif

#if HAS_UART
#include <fastarduino/uart.h>
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
// Define vectors we need in the example
REGISTER_UATX_ISR(UART_NUM)
#else
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 32;
#include <fastarduino/soft_uart.h>
#endif

// Buffers for UART
static char output_buffer[OUTPUT_BUFFER_SIZE];

// Timer stuff
using TIMER = timer::Timer<NTIMER>;
using TYPE = TIMER::TYPE;
static constexpr const board::DigitalPin ICP = TIMER::ICP_PIN;
static constexpr const uint32_t PRECISION = 10000UL;
using CALC = timer::Calculator<NTIMER>;
static constexpr const TIMER::PRESCALER PRESCALER = CALC::tick_prescaler(PRECISION);
#if F_CPU == 16000000UL
static_assert(PRESCALER == TIMER::PRESCALER::DIV_1024, "PRESCALER should be DIV_1024");
#elif F_CPU == 8000000UL
static_assert(PRESCALER == TIMER::PRESCALER::DIV_256, "PRESCALER should be DIV_256");
#else
#error "Current frequency not supported!"
#endif

static constexpr uint32_t milliseconds(uint32_t ticks, uint32_t overflows)
{
	return (ticks + overflows * TIMER::TIMER_MAX) * 1000UL / CALC::CTC_frequency(PRESCALER);
}

using timer::TimerInputCapture;
using timer::TimerInterrupt;

class Capture
{
public:
	Capture(TIMER& timer):_timer{timer}, _input{gpio::PinMode::INPUT_PULLUP}, _ready{false}, _capture{0} {}

	void on_capture(TYPE capture)
	{
		if (!_input.value())
		{
			// Button pushed, prepare for next capture (on button release)
			_timer.reset_();
			_overflows = 0;
			_timer.set_input_capture(TimerInputCapture::RISING_EDGE);
		}
		else
		{
			// Button released, stop capture and get captured value
			_timer.set_interrupts();
			_capture = capture;
			_ready = true;
		}
	}

	void on_overflow()
	{
		++_overflows;
	}

	void start()
	{
		synchronized
		{
			_ready = false;
			_capture = 0;
			_overflows = 0;
			_timer.set_input_capture(TimerInputCapture::FALLING_EDGE);
			_timer.set_interrupts(TimerInterrupt::INPUT_CAPTURE | TimerInterrupt::OVERFLOW);
		}
	}

	TYPE capture()
	{
		while (!_ready) ;
		synchronized return _capture;
	}

	uint16_t overflows()
	{
		synchronized return _overflows;
	}

private:
	TIMER& _timer;
	gpio::FastPinType<ICP>::TYPE _input;
	volatile bool _ready;
	volatile TYPE _capture;
	volatile uint16_t _overflows;
};

REGISTER_TIMER_CAPTURE_ISR_METHOD(TIMER_NUM, Capture, &Capture::on_capture)
REGISTER_TIMER_OVERFLOW_ISR_METHOD(TIMER_NUM, Capture, &Capture::on_overflow)

int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();
	// Start UART
#if HAS_UART
	serial::hard::UATX<UART> uatx{output_buffer};
	uatx.register_handler();
#else
	serial::soft::UATX<TX> uatx{output_buffer};
#endif
	uatx.begin(115200);

	streams::ostream out = uatx.out();
	out.width(0);
	out << F("Started\n");

	TIMER timer{timer::TimerMode::NORMAL, PRESCALER};

	timer.begin();
	Capture capture{timer};
	interrupt::register_handler(capture);

	// Event Loop
	while (true)
	{
		out << F("Waiting for button push...") << streams::endl;
		capture.start();
		TYPE duration = capture.capture();
		uint16_t overflows = capture.overflows();
		out << F("Push lasted ") << duration << F(" ticks, ") << overflows << F(" overflows") << streams::endl;
		out << F("Push lasted ") << milliseconds(duration, overflows) << F(" ms") << streams::endl;
	}
}
<commit_msg>Fix example static assert.<commit_after>//   Copyright 2016-2018 Jean-Francois Poilpret
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

/*
 * Input Capture Timer example. Take #1
 * TODO
 *
 * Wiring:
 * - TODO
 */

#include <fastarduino/flash.h>
#include <fastarduino/gpio.h>
#include <fastarduino/timer.h>

#if defined(ARDUINO_UNO) || defined(BREADBOARD_ATMEGA328P) || defined(ARDUINO_NANO)
#define HAS_UART 1
#define UART_NUM 0
static constexpr const board::USART UART = board::USART::USART0;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#elif defined (ARDUINO_MEGA)
#define HAS_UART 1
#define UART_NUM 0
static constexpr const board::USART UART = board::USART::USART0;
#define TIMER_NUM 4
static constexpr const board::Timer NTIMER = board::Timer::TIMER4;
#elif defined (ARDUINO_LEONARDO)
#define HAS_UART 1
#define UART_NUM 1
static constexpr const board::USART UART = board::USART::USART1;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#elif defined (BREADBOARD_ATTINYX4)
#define HAS_UART 0
static constexpr const board::DigitalPin TX = board::DigitalPin::D1_PA1;
#define TIMER_NUM 1
static constexpr const board::Timer NTIMER = board::Timer::TIMER1;
#else
#error "Current target is not yet supported!"
#endif

#if HAS_UART
#include <fastarduino/uart.h>
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
// Define vectors we need in the example
REGISTER_UATX_ISR(UART_NUM)
#else
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 32;
#include <fastarduino/soft_uart.h>
#endif

// Buffers for UART
static char output_buffer[OUTPUT_BUFFER_SIZE];

// Timer stuff
using TIMER = timer::Timer<NTIMER>;
using TYPE = TIMER::TYPE;
static constexpr const board::DigitalPin ICP = TIMER::ICP_PIN;
static constexpr const uint32_t PRECISION = 10000UL;
using CALC = timer::Calculator<NTIMER>;
static constexpr const TIMER::PRESCALER PRESCALER = CALC::tick_prescaler(PRECISION);

static_assert(PRESCALER == TIMER::PRESCALER::DIV_1024, "PRESCALER should be DIV_1024");

static constexpr uint32_t milliseconds(uint32_t ticks, uint32_t overflows)
{
	return (ticks + overflows * TIMER::TIMER_MAX) * 1000UL / CALC::CTC_frequency(PRESCALER);
}

using timer::TimerInputCapture;
using timer::TimerInterrupt;

class Capture
{
public:
	Capture(TIMER& timer):_timer{timer}, _input{gpio::PinMode::INPUT_PULLUP}, _ready{false}, _capture{0} {}

	void on_capture(TYPE capture)
	{
		if (!_input.value())
		{
			// Button pushed, prepare for next capture (on button release)
			_timer.reset_();
			_overflows = 0;
			_timer.set_input_capture(TimerInputCapture::RISING_EDGE);
		}
		else
		{
			// Button released, stop capture and get captured value
			_timer.set_interrupts();
			_capture = capture;
			_ready = true;
		}
	}

	void on_overflow()
	{
		++_overflows;
	}

	void start()
	{
		synchronized
		{
			_ready = false;
			_capture = 0;
			_overflows = 0;
			_timer.set_input_capture(TimerInputCapture::FALLING_EDGE);
			_timer.set_interrupts(TimerInterrupt::INPUT_CAPTURE | TimerInterrupt::OVERFLOW);
		}
	}

	TYPE capture()
	{
		while (!_ready) ;
		synchronized return _capture;
	}

	uint16_t overflows()
	{
		synchronized return _overflows;
	}

private:
	TIMER& _timer;
	gpio::FastPinType<ICP>::TYPE _input;
	volatile bool _ready;
	volatile TYPE _capture;
	volatile uint16_t _overflows;
};

REGISTER_TIMER_CAPTURE_ISR_METHOD(TIMER_NUM, Capture, &Capture::on_capture)
REGISTER_TIMER_OVERFLOW_ISR_METHOD(TIMER_NUM, Capture, &Capture::on_overflow)

int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();
	// Start UART
#if HAS_UART
	serial::hard::UATX<UART> uatx{output_buffer};
	uatx.register_handler();
#else
	serial::soft::UATX<TX> uatx{output_buffer};
#endif
	uatx.begin(115200);

	streams::ostream out = uatx.out();
	out.width(0);
	out << F("Started\n");

	TIMER timer{timer::TimerMode::NORMAL, PRESCALER};

	timer.begin();
	Capture capture{timer};
	interrupt::register_handler(capture);

	// Event Loop
	while (true)
	{
		out << F("Waiting for button push...") << streams::endl;
		capture.start();
		TYPE duration = capture.capture();
		uint16_t overflows = capture.overflows();
		out << F("Push lasted ") << duration << F(" ticks, ") << overflows << F(" overflows") << streams::endl;
		out << F("Push lasted ") << milliseconds(duration, overflows) << F(" ms") << streams::endl;
	}
}
<|endoftext|>


<commit_before>/**
 * @file
 *
 * @brief Write key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "write.hpp"
#include "log.hpp"
#include "yaml-cpp/yaml.h"

#include <kdbassert.h>
#include <kdbease.h>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <fstream>

using namespace std;
using namespace kdb;

using std::stack;

namespace
{

using KeySetPair = pair<KeySet, KeySet>;

/**
 * @brief This function returns all array parents for a given key set.
 *
 * @param keys This parameter contains the key set this function searches for array parents.
 *
 * @return A key set that contains all array parents stored in `keys`
 */
KeySet splitArrayParents (KeySet const & keys)
{
	KeySet arrayParents;
	for (auto const & key : keys)
	{
		if (key.hasMeta ("array")) arrayParents.append (key);
	}

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Array parents:");
	logKeySet (arrayParents);
#endif

	return arrayParents;
}

/**
 * @brief This function splits `keys` into two key sets, one for array parents and elements, and the other one for all other keys.
 *
 * @param arrayParents This key set contains (a copy of) all array parents of `keys`.
 * @param keys This parameter contains the key set this function splits.
 *
 * @return A pair of key sets, where the first key set contains all array parents and elements,
 *         and the second key set contains all other keys
 */
KeySetPair splitArrayOther (KeySet const & arrayParents, KeySet const & keys)
{
	KeySet others = keys.dup ();
	KeySet arrays;

	for (auto const & parent : arrayParents)
	{
		arrays.append (others.cut (parent));
	}

	return make_pair (arrays, others);
}

/**
 * @brief This function determines all keys “missing” from the given keyset.
 *
 * The term “missing” refers to keys that are not part of the hierarchy. For example in a key set with the parent key
 *
 *  - `user/parent`
 *
 * that contains the keys
 *
 * - `user/parent/level1/level2`, and
 * - `user/parent/level1/level2/level3/level4`
 *
 * , the keys
 *
 * - `user/parent/level1`, and
 * - `user/parent/level1/level2/level3`
 *
 * are missing.
 *
 * @param keys This parameter contains the key set for which this function determines missing keys.
 * @param parent This value stores the parent key of `keys`.
 *
 * @return A key set that contains all keys missing from `keys`
 */
KeySet missingKeys (KeySet const & keys, Key const & parent)
{
	KeySet missing;

	keys.rewind ();
	Key previous{ parent.getName (), KEY_BINARY, KEY_END };
	for (; keys.next (); previous = keys.current ())
	{
		if (keys.current ().isDirectBelow (previous) || !keys.current ().isBelow (previous)) continue;

		Key current{ keys.current ().getName (), KEY_BINARY, KEY_END };
		while (!current.isDirectBelow (previous))
		{
			ckdb::keySetBaseName (*current, NULL);
			missing.append (current);
			current = current.dup ();
		}
	}

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Add missing keys:");
	logKeySet (missing);
#endif

	return missing;
}

/**
 * @brief This function returns a `NameIterator` starting at the first level that is not part of `parent`.
 *
 * @pre The parameter `key` must be a child of `parent`.
 *
 * @param key This is the key for which this function returns a relative iterator.
 * @param parent This key specifies the part of the name iterator that will not be part of the return value of this function.
 *
 * @returns A relative iterator that starts with the first part of the name of `key` not contained in `parent`.
 */
NameIterator relativeKeyIterator (Key const & key, Key const & parent)
{
	auto parentIterator = parent.begin ();
	auto keyIterator = key.begin ();
	while (parentIterator != parent.end () && keyIterator != key.end ())
	{
		parentIterator++;
		keyIterator++;
	}
	return keyIterator;
}

/**
 * @brief This function returns the array index for a given key part.
 *
 * @param nameIterator This iterator specifies the name of the key.
 *
 * @retval The index of the array element, or `0` if the given key part is not an array element.
 */
unsigned long long getArrayIndex (NameIterator const & nameIterator)
{
	string const name = *nameIterator;
	auto const offsetIndex = ckdb::elektraArrayValidateBaseNameString (name.c_str ());
	auto const isArrayElement = offsetIndex >= 1;
	return isArrayElement ? stoull (name.substr (static_cast<size_t> (offsetIndex))) : 0;
}

/**
 * @brief This function creates a YAML node representing a key value.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 plugin before you use YAML CPP.
 *
 * @returns A new YAML node containing the data specified in `key`
 */
YAML::Node createNode (Key const & key)
{
	if (key.hasMeta ("array"))
	{
		return YAML::Node (YAML::NodeType::Sequence);
	}
	if (key.getBinarySize () == 0)
	{
		return YAML::Node (YAML::NodeType::Null);
	}
	if (key.isBinary ())
	{
		return YAML::Node ("Unsupported binary value!");
	}

	auto value = key.get<string> ();
	if (value == "0" || value == "1")
	{
		return YAML::Node (key.get<bool> ());
	}
	return YAML::Node (value);
}

/**
 * @brief This function creates a YAML Node containing a key value and optionally metadata.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data and metadata specified in `key`
 */
YAML::Node createLeafNode (Key & key)
{

	YAML::Node metaNode{ YAML::Node (YAML::NodeType::Map) };
	YAML::Node dataNode = createNode (key);

	key.rewindMeta ();
	while (Key meta = key.nextMeta ())
	{
		if (meta.getName () == "array" || meta.getName () == "binary") continue;
		if (meta.getName () == "type" && meta.getString () == "binary")
		{
			dataNode.SetTag ("tag:yaml.org,2002:binary");
			continue;
		}
		metaNode[meta.getName ()] = meta.getString ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", meta.getName ().c_str (), meta.getString ().c_str ());
	}

	if (metaNode.size () <= 0)
	{
		ELEKTRA_LOG_DEBUG ("Return leaf node with value “%s”",
				   dataNode.IsNull () ? "~" : dataNode.IsSequence () ? "[]" : dataNode.as<string> ().c_str ());
		return dataNode;
	}

	YAML::Node node{ YAML::Node (YAML::NodeType::Sequence) };
	node.SetTag ("!elektra/meta");
	node.push_back (dataNode);
	node.push_back (metaNode);

#ifdef HAVE_LOGGER
	ostringstream data;
	data << node;
	ELEKTRA_LOG_DEBUG ("Return meta leaf node with value “%s”", data.str ().c_str ());
#endif

	return node;
}

/**
 * @brief This function adds `null` elements to the given YAML collection.
 *
 * @param sequence This node stores the collection to which this function adds `numberOfElements` empty elements.
 * @param numberOfElements This parameter specifies the number of empty element this function adds to `sequence`.
 */
void addEmptyArrayElements (YAML::Node & sequence, unsigned long long const numberOfElements)
{
	ELEKTRA_LOG_DEBUG ("Add %lld empty array elements", numberOfElements);
	for (auto missingFields = numberOfElements; missingFields > 0; missingFields--)
	{
		sequence.push_back ({});
	}
}

/**
 * @brief This function adds a key that is not part of any array to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 */
void addKeyNoArray (YAML::Node & data, NameIterator & keyIterator, Key & key)
{
	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

	ELEKTRA_LOG_DEBUG ("Add key part “%s”", (*keyIterator).c_str ());

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}
	if (keyIterator == --key.end ())
	{
		data[*keyIterator] = createLeafNode (key);
		return;
	}

	YAML::Node node;

	node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
	data[*keyIterator] = node;
	addKeyNoArray (node, ++keyIterator, key);
}

/**
 * @brief This function adds a key that is either, element of an array, or an array parent to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 * @param converted This partial key specifies the part of `key` that is already part of `data`.
 * @param arrayParent This key stores the (possible) array parent of the current part of `key` this
 *                    function should add to `data`.
 */
void addKeyArray (YAML::Node & data, NameIterator & keyIterator, Key & key, Key & converted, Key * arrayParent)
{
	converted.addBaseName (*keyIterator);
	auto const isArrayElement = arrayParent && converted.isDirectBelow (*arrayParent);
	auto const arrayIndex = isArrayElement ? getArrayIndex (keyIterator) : 0;

	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}

	ELEKTRA_LOG_DEBUG ("Add key part “%s”", (*keyIterator).c_str ());
	if (keyIterator == --key.end ())
	{
		if (isArrayElement)
		{
			addEmptyArrayElements (data, arrayIndex - data.size ());
			data.push_back (createLeafNode (key));
		}
		else
		{
			data[*keyIterator] = createLeafNode (key);
		}

		return;
	}

	YAML::Node node;

	if (isArrayElement)
	{
		node = (arrayIndex < data.size () && !data[arrayIndex].IsScalar ()) ? data[arrayIndex] : YAML::Node ();
		addEmptyArrayElements (data, arrayIndex - data.size ());
		data[arrayIndex] = node;
	}
	else
	{
		node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
		data[*keyIterator] = node;
	}
	addKeyArray (node, ++keyIterator, key, converted, arrayParent);
}

/**
 * @brief This function adds a key set to a YAML node.
 *
 * @param data This node stores the data specified via `mappings`.
 * @param mappings This keyset specifies all keys and values this function adds to `data`.
 * @param parent This key is the root of all keys stored in `mappings`.
 * @param isArray This value specifies if the keys inside `keys` are all part of an array (either element or parent), or if none of them is
 *                part of an array.
 */
void addKeys (YAML::Node & data, KeySet const & mappings, Key const & parent, bool const isArray = false)
{
	stack<Key> arrayParents;

	for (auto key : mappings)
	{
		ELEKTRA_LOG_DEBUG ("Convert key “%s”: “%s”", key.getName ().c_str (),
				   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
		NameIterator keyIterator = relativeKeyIterator (key, parent);

		if (isArray)
		{
			if (key.hasMeta ("array"))
			{
				ELEKTRA_LOG_DEBUG ("Add array parent “%s”", key.getName ().c_str ());
				arrayParents.push (key);
			}
			else if (!arrayParents.empty () && !key.isBelow (arrayParents.top ()))
			{
				ELEKTRA_LOG_DEBUG ("Remove array parent “%s”", arrayParents.top ().getName ().c_str ());
				arrayParents.pop ();
			}

			Key converted{ parent.getName (), KEY_END };
			addKeyArray (data, keyIterator, key, converted, !arrayParents.empty () ? &arrayParents.top () : nullptr);
		}
		else
		{
			addKeyNoArray (data, keyIterator, key);
		}

#ifdef HAVE_LOGGER
		ostringstream output;
		output << data;

		ELEKTRA_LOG_DEBUG ("Converted Data:");
		ELEKTRA_LOG_DEBUG ("——————————");

		istringstream stream (output.str ());
		for (string line; std::getline (stream, line);)
		{
			ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
		}

		ELEKTRA_LOG_DEBUG ("——————————");
#endif
	}
}

} // end namespace

/**
 * @brief This function saves the key-value pairs stored in `mappings` as YAML data in the location specified via `parent`.
 *
 * @param mappings This key set stores the mappings that should be saved as YAML data.
 * @param parent This key specifies the path to the YAML data file that should be written.
 */
void yamlcpp::yamlWrite (KeySet const & mappings, Key const & parent)
{
	KeySet keys = mappings;
	auto missing = missingKeys (keys, parent);
	keys.append (missing);

	KeySet arrayParents;
	KeySet arrays;
	KeySet nonArrays;

	arrayParents = splitArrayParents (keys);
	tie (arrays, nonArrays) = splitArrayOther (arrayParents, keys);

	auto data = YAML::Node ();
	addKeys (data, nonArrays, parent);
	addKeys (data, arrays, parent, true);

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Write Data:");
	ELEKTRA_LOG_DEBUG ("——————————");

	ostringstream outputString;
	outputString << data;
	istringstream stream (outputString.str ());
	for (string line; std::getline (stream, line);)
	{
		ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
	}

	ELEKTRA_LOG_DEBUG ("——————————");
#endif

	ofstream output (parent.getString ());
	output << data << endl;
}
<commit_msg>YAML CPP: Fix infinite loop<commit_after>/**
 * @file
 *
 * @brief Write key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "write.hpp"
#include "log.hpp"
#include "yaml-cpp/yaml.h"

#include <kdbassert.h>
#include <kdbease.h>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <fstream>

using namespace std;
using namespace kdb;

using std::stack;

namespace
{

using KeySetPair = pair<KeySet, KeySet>;

/**
 * @brief This function returns all array parents for a given key set.
 *
 * @param keys This parameter contains the key set this function searches for array parents.
 *
 * @return A key set that contains all array parents stored in `keys`
 */
KeySet splitArrayParents (KeySet const & keys)
{
	KeySet arrayParents;
	for (auto const & key : keys)
	{
		if (key.hasMeta ("array")) arrayParents.append (key);
	}

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Array parents:");
	logKeySet (arrayParents);
#endif

	return arrayParents;
}

/**
 * @brief This function splits `keys` into two key sets, one for array parents and elements, and the other one for all other keys.
 *
 * @param arrayParents This key set contains (a copy of) all array parents of `keys`.
 * @param keys This parameter contains the key set this function splits.
 *
 * @return A pair of key sets, where the first key set contains all array parents and elements,
 *         and the second key set contains all other keys
 */
KeySetPair splitArrayOther (KeySet const & arrayParents, KeySet const & keys)
{
	KeySet others = keys.dup ();
	KeySet arrays;

	for (auto const & parent : arrayParents)
	{
		arrays.append (others.cut (parent));
	}

	return make_pair (arrays, others);
}

/**
 * @brief This function determines all keys “missing” from the given keyset.
 *
 * The term “missing” refers to keys that are not part of the hierarchy. For example in a key set with the parent key
 *
 *  - `user/parent`
 *
 * that contains the keys
 *
 * - `user/parent/level1/level2`, and
 * - `user/parent/level1/level2/level3/level4`
 *
 * , the keys
 *
 * - `user/parent/level1`, and
 * - `user/parent/level1/level2/level3`
 *
 * are missing.
 *
 * @param keys This parameter contains the key set for which this function determines missing keys.
 * @param parent This value stores the parent key of `keys`.
 *
 * @return A key set that contains all keys missing from `keys`
 */
KeySet missingKeys (KeySet const & keys, Key const & parent)
{
	KeySet missing;

	keys.rewind ();
	Key previous{ parent.getName (), KEY_BINARY, KEY_END };
	for (; keys.next (); previous = keys.current ())
	{
		if (keys.current ().isDirectBelow (previous) || !keys.current ().isBelow (previous)) continue;

		Key current{ keys.current ().getName (), KEY_BINARY, KEY_END };
		while (!current.isDirectBelow (previous))
		{
			ckdb::keySetBaseName (*current, NULL);
			missing.append (current);
			current = current.dup ();
		}
	}

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Add missing keys:");
	logKeySet (missing);
#endif

	return missing;
}

/**
 * @brief This function returns a `NameIterator` starting at the first level that is not part of `parent`.
 *
 * @pre The parameter `key` must be a child of `parent`.
 *
 * @param key This is the key for which this function returns a relative iterator.
 * @param parent This key specifies the part of the name iterator that will not be part of the return value of this function.
 *
 * @returns A relative iterator that starts with the first part of the name of `key` not contained in `parent`.
 */
NameIterator relativeKeyIterator (Key const & key, Key const & parent)
{
	auto parentIterator = parent.begin ();
	auto keyIterator = key.begin ();
	while (parentIterator != parent.end () && keyIterator != key.end ())
	{
		parentIterator++;
		keyIterator++;
	}
	return keyIterator;
}

/**
 * @brief This function returns the array index for a given key part.
 *
 * @param nameIterator This iterator specifies the name of the key.
 *
 * @retval The index of the array element, or `0` if the given key part is not an array element.
 */
unsigned long long getArrayIndex (NameIterator const & nameIterator)
{
	string const name = *nameIterator;
	auto const offsetIndex = ckdb::elektraArrayValidateBaseNameString (name.c_str ());
	auto const isArrayElement = offsetIndex >= 1;
	return isArrayElement ? stoull (name.substr (static_cast<size_t> (offsetIndex))) : 0;
}

/**
 * @brief This function creates a YAML node representing a key value.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 plugin before you use YAML CPP.
 *
 * @returns A new YAML node containing the data specified in `key`
 */
YAML::Node createNode (Key const & key)
{
	if (key.hasMeta ("array"))
	{
		return YAML::Node (YAML::NodeType::Sequence);
	}
	if (key.getBinarySize () == 0)
	{
		return YAML::Node (YAML::NodeType::Null);
	}
	if (key.isBinary ())
	{
		return YAML::Node ("Unsupported binary value!");
	}

	auto value = key.get<string> ();
	if (value == "0" || value == "1")
	{
		return YAML::Node (key.get<bool> ());
	}
	return YAML::Node (value);
}

/**
 * @brief This function creates a YAML Node containing a key value and optionally metadata.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data and metadata specified in `key`
 */
YAML::Node createLeafNode (Key & key)
{

	YAML::Node metaNode{ YAML::Node (YAML::NodeType::Map) };
	YAML::Node dataNode = createNode (key);

	key.rewindMeta ();
	while (Key meta = key.nextMeta ())
	{
		if (meta.getName () == "array" || meta.getName () == "binary") continue;
		if (meta.getName () == "type" && meta.getString () == "binary")
		{
			dataNode.SetTag ("tag:yaml.org,2002:binary");
			continue;
		}
		metaNode[meta.getName ()] = meta.getString ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", meta.getName ().c_str (), meta.getString ().c_str ());
	}

	if (metaNode.size () <= 0)
	{
		ELEKTRA_LOG_DEBUG ("Return leaf node with value “%s”",
				   dataNode.IsNull () ? "~" : dataNode.IsSequence () ? "[]" : dataNode.as<string> ().c_str ());
		return dataNode;
	}

	YAML::Node node{ YAML::Node (YAML::NodeType::Sequence) };
	node.SetTag ("!elektra/meta");
	node.push_back (dataNode);
	node.push_back (metaNode);

#ifdef HAVE_LOGGER
	ostringstream data;
	data << node;
	ELEKTRA_LOG_DEBUG ("Return meta leaf node with value “%s”", data.str ().c_str ());
#endif

	return node;
}

/**
 * @brief This function adds `null` elements to the given YAML collection.
 *
 * @param sequence This node stores the collection to which this function adds `numberOfElements` empty elements.
 * @param numberOfElements This parameter specifies the number of empty element this function adds to `sequence`.
 */
void addEmptyArrayElements (YAML::Node & sequence, unsigned long long const numberOfElements)
{
	ELEKTRA_LOG_DEBUG ("Add %lld empty array elements", numberOfElements);
	for (auto missingFields = numberOfElements; missingFields > 0; missingFields--)
	{
		sequence.push_back ({});
	}
}

/**
 * @brief This function adds a key that is not part of any array to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 */
void addKeyNoArray (YAML::Node & data, NameIterator & keyIterator, Key & key)
{
	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

	ELEKTRA_LOG_DEBUG ("Add key part “%s”", (*keyIterator).c_str ());

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}
	if (keyIterator == --key.end ())
	{
		data[*keyIterator] = createLeafNode (key);
		return;
	}

	YAML::Node node;

	node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
	data[*keyIterator] = node;
	addKeyNoArray (node, ++keyIterator, key);
}

/**
 * @brief This function adds a key that is either, element of an array, or an array parent to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 * @param converted This partial key specifies the part of `key` that is already part of `data`.
 * @param arrayParent This key stores the (possible) array parent of the current part of `key` this
 *                    function should add to `data`.
 */
void addKeyArray (YAML::Node & data, NameIterator & keyIterator, Key & key, Key & converted, Key * arrayParent)
{
	converted.addBaseName (*keyIterator);
	auto const isArrayElement = arrayParent && converted.isDirectBelow (*arrayParent);
	auto const arrayIndex = isArrayElement ? getArrayIndex (keyIterator) : 0;

	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}

	ELEKTRA_LOG_DEBUG ("Add key part “%s”", (*keyIterator).c_str ());
	if (keyIterator == --key.end ())
	{
		if (isArrayElement)
		{
			if (arrayIndex > data.size ())
			{
				addEmptyArrayElements (data, arrayIndex - data.size ());
			}
			data.push_back (createLeafNode (key));
		}
		else
		{
			data[*keyIterator] = createLeafNode (key);
		}

		return;
	}

	YAML::Node node;

	if (isArrayElement)
	{
		node = (arrayIndex < data.size () && !data[arrayIndex].IsScalar ()) ? data[arrayIndex] : YAML::Node ();
		if (arrayIndex > data.size ())
		{
			addEmptyArrayElements (data, arrayIndex - data.size ());
		}
		data[arrayIndex] = node;
	}
	else
	{
		node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
		data[*keyIterator] = node;
	}
	addKeyArray (node, ++keyIterator, key, converted, arrayParent);
}

/**
 * @brief This function adds a key set to a YAML node.
 *
 * @param data This node stores the data specified via `mappings`.
 * @param mappings This keyset specifies all keys and values this function adds to `data`.
 * @param parent This key is the root of all keys stored in `mappings`.
 * @param isArray This value specifies if the keys inside `keys` are all part of an array (either element or parent), or if none of them is
 *                part of an array.
 */
void addKeys (YAML::Node & data, KeySet const & mappings, Key const & parent, bool const isArray = false)
{
	stack<Key> arrayParents;

	for (auto key : mappings)
	{
		ELEKTRA_LOG_DEBUG ("Convert key “%s”: “%s”", key.getName ().c_str (),
				   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
		NameIterator keyIterator = relativeKeyIterator (key, parent);

		if (isArray)
		{
			if (key.hasMeta ("array"))
			{
				ELEKTRA_LOG_DEBUG ("Add array parent “%s”", key.getName ().c_str ());
				arrayParents.push (key);
			}
			else if (!arrayParents.empty () && !key.isBelow (arrayParents.top ()))
			{
				ELEKTRA_LOG_DEBUG ("Remove array parent “%s”", arrayParents.top ().getName ().c_str ());
				arrayParents.pop ();
			}

			Key converted{ parent.getName (), KEY_END };
			addKeyArray (data, keyIterator, key, converted, !arrayParents.empty () ? &arrayParents.top () : nullptr);
		}
		else
		{
			addKeyNoArray (data, keyIterator, key);
		}

#ifdef HAVE_LOGGER
		ostringstream output;
		output << data;

		ELEKTRA_LOG_DEBUG ("Converted Data:");
		ELEKTRA_LOG_DEBUG ("——————————");

		istringstream stream (output.str ());
		for (string line; std::getline (stream, line);)
		{
			ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
		}

		ELEKTRA_LOG_DEBUG ("——————————");
#endif
	}
}

} // end namespace

/**
 * @brief This function saves the key-value pairs stored in `mappings` as YAML data in the location specified via `parent`.
 *
 * @param mappings This key set stores the mappings that should be saved as YAML data.
 * @param parent This key specifies the path to the YAML data file that should be written.
 */
void yamlcpp::yamlWrite (KeySet const & mappings, Key const & parent)
{
	KeySet keys = mappings;
	auto missing = missingKeys (keys, parent);
	keys.append (missing);

	KeySet arrayParents;
	KeySet arrays;
	KeySet nonArrays;

	arrayParents = splitArrayParents (keys);
	tie (arrays, nonArrays) = splitArrayOther (arrayParents, keys);

	auto data = YAML::Node ();
	addKeys (data, nonArrays, parent);
	addKeys (data, arrays, parent, true);

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Write Data:");
	ELEKTRA_LOG_DEBUG ("——————————");

	ostringstream outputString;
	outputString << data;
	istringstream stream (outputString.str ());
	for (string line; std::getline (stream, line);)
	{
		ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
	}

	ELEKTRA_LOG_DEBUG ("——————————");
#endif

	ofstream output (parent.getString ());
	output << data << endl;
}
<|endoftext|>


<commit_before>/**
 * JACUI - Just Another C++ User Interface Library
 *
 * Copyright (C) 2011 The JACUI Project
 *
 * http://code.google.com/p/jacui/
 *
 * 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.
 */

#ifndef JACUI_TYPES_HPP
#define JACUI_TYPES_HPP

#include <cstddef>

namespace jacui {
    /**
       \brief JACUI point type
    */
    struct point2d {
        point2d() : x(0), y(0) { }

        point2d(std::size_t xpos, std::size_t ypos) : x(xpos), y(ypos) { }

        std::size_t x;
        std::size_t y;
    };

    inline bool operator==(const point2d& lhs, const point2d& rhs)
    {
        return lhs.x == rhs.x && lhs.y == rhs.y;
    }

    inline bool operator!=(const point2d& lhs, const point2d& rhs)
    {
        return lhs.x != rhs.x || lhs.y != rhs.y;
    }

    /**
       \brief JACUI size type
    */
    struct size2d {
        size2d() : width(0), height(0) { }

        size2d(std::size_t xsize, std::size_t ysize) : width(xsize), height(ysize) { }

        bool empty() const { return width == 0 || height == 0; }

        std::size_t width;
        std::size_t height;
    };

    inline bool operator==(const size2d& lhs, const size2d& rhs)
    {
        return lhs.width == rhs.width && lhs.height == rhs.height;
    }

    inline bool operator!=(const size2d& lhs, const size2d& rhs)
    {
        return lhs.width != rhs.width || lhs.height != rhs.height;
    }

    /**
       \brief JACUI rectangle type
    */
    struct rect2d {
        rect2d() : x(0), y(0), width(0), height(0) { }

        rect2d(const size2d& s) : x(0), y(0), width(s.width), height(s.height) { }

        rect2d(std::size_t xsize, std::size_t ysize)
            : x(0), y(0), width(xsize), height(ysize) { }

        rect2d(std::size_t xpos, std::size_t ypos, std::size_t xsize, std::size_t ysize)
            : x(xpos), y(ypos), width(xsize), height(ysize) { }

        bool empty() const { return width == 0 || height == 0; }

        size2d size() const { return size2d(width, height); }

        std::size_t x;
        std::size_t y;
        std::size_t width;
        std::size_t height;
    };

    inline bool operator==(const rect2d& lhs, const rect2d& rhs)
    {
        return lhs.x == rhs.x
            && lhs.y == rhs.y
            && lhs.width == rhs.width
            && lhs.height == rhs.height;
    }

    inline bool operator!=(const rect2d& lhs, const rect2d& rhs)
    {
        return lhs.x != rhs.x
            || lhs.y != rhs.y
            || lhs.width != rhs.width
            || lhs.height != rhs.height;
    }

    /**
       \brief JACUI color type
    */
    struct color {
    public:
        color() : r(0), g(0), b(0), a(0) { }

        color(int red, int green, int blue) : r(red), g(green), b(blue), a(0xff) { }

        color(int red, int green, int blue, int alpha) : r(red), g(green), b(blue), a(alpha) { }

        unsigned long rgb() const { return r << 16 | g << 8 | b; }

        unsigned long rgba() const { return r << 24 | g << 16 | b << 8 | a; }

        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
    };

    /**
       \brief create color from RGB value, e.g. 0xffff00 -> yellow
    */
    inline color make_rgb(unsigned long rgb)
    {
        return color((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
    }

    /**
       \brief create color from RGBA value, e.g. 0xffff0080 -> yellow with 50% transparency
    */
    inline color make_rgba(unsigned long rgba)
    {
        return color((rgba >> 24) & 0xff, (rgba >> 16) & 0xff, (rgba >> 8) & 0xff, rgba & 0xff);
    }

    inline bool operator==(const color& lhs, const color& rhs)
    {
        return lhs.rgba() == rhs.rgba();
    }

    inline bool operator!=(const color& lhs, const color& rhs)
    {
        return lhs.rgba() != rhs.rgba();
    }
}

#endif
<commit_msg>added rect2d::offset and rect2d::includes<commit_after>/**
 * JACUI - Just Another C++ User Interface Library
 *
 * Copyright (C) 2011 The JACUI Project
 *
 * http://code.google.com/p/jacui/
 *
 * 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.
 */

#ifndef JACUI_TYPES_HPP
#define JACUI_TYPES_HPP

#include <cstddef>

namespace jacui {
    /**
       \brief JACUI point type
    */
    struct point2d {
        point2d() : x(0), y(0) { }

        point2d(std::size_t xpos, std::size_t ypos) : x(xpos), y(ypos) { }

        std::size_t x;
        std::size_t y;
    };

    inline bool operator==(const point2d& lhs, const point2d& rhs)
    {
        return lhs.x == rhs.x && lhs.y == rhs.y;
    }

    inline bool operator!=(const point2d& lhs, const point2d& rhs)
    {
        return lhs.x != rhs.x || lhs.y != rhs.y;
    }

    /**
       \brief JACUI size type
    */
    struct size2d {
        size2d() : width(0), height(0) { }

        size2d(std::size_t xsize, std::size_t ysize) : width(xsize), height(ysize) { }

        bool empty() const { return width == 0 || height == 0; }

        std::size_t width;
        std::size_t height;
    };

    inline bool operator==(const size2d& lhs, const size2d& rhs)
    {
        return lhs.width == rhs.width && lhs.height == rhs.height;
    }

    inline bool operator!=(const size2d& lhs, const size2d& rhs)
    {
        return lhs.width != rhs.width || lhs.height != rhs.height;
    }

    /**
       \brief JACUI rectangle type
    */
    struct rect2d {
        rect2d() : x(0), y(0), width(0), height(0) { }

        rect2d(std::size_t xsize, std::size_t ysize)
            : x(0), y(0), width(xsize), height(ysize) { }

        rect2d(std::size_t xoff, std::size_t yoff, std::size_t xsize, std::size_t ysize)
            : x(xoff), y(yoff), width(xsize), height(ysize) { }

        rect2d(const size2d& s)
            : x(0), y(0), width(s.width), height(s.height) { }

        rect2d(const point2d& p, const size2d& s)
            : x(p.x), y(p.y), width(s.width), height(s.height) { }

        bool empty() const { return width == 0 || height == 0; }

        size2d size() const { return size2d(width, height); }

        point2d offset() const { return point2d(x, y); }

        bool includes(const point2d& p) const {
            return includes(p.x, p.y);
        }

        bool includes(std::size_t px, std::size_t py) const {
            return px >= x && py >= y && px < x + width && py < y + height;
        }

        std::size_t x;
        std::size_t y;
        std::size_t width;
        std::size_t height;
    };

    inline bool operator==(const rect2d& lhs, const rect2d& rhs)
    {
        return lhs.x == rhs.x
            && lhs.y == rhs.y
            && lhs.width == rhs.width
            && lhs.height == rhs.height;
    }

    inline bool operator!=(const rect2d& lhs, const rect2d& rhs)
    {
        return lhs.x != rhs.x
            || lhs.y != rhs.y
            || lhs.width != rhs.width
            || lhs.height != rhs.height;
    }

    /**
       \brief JACUI color type
    */
    struct color {
    public:
        color() : r(0), g(0), b(0), a(0) { }

        color(int red, int green, int blue) : r(red), g(green), b(blue), a(0xff) { }

        color(int red, int green, int blue, int alpha) : r(red), g(green), b(blue), a(alpha) { }

        unsigned long rgb() const { return r << 16 | g << 8 | b; }

        unsigned long rgba() const { return r << 24 | g << 16 | b << 8 | a; }

        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
    };

    /**
       \brief create color from 24bit RGB value, e.g. 0xffff00 ->
       yellow
    */
    inline color make_rgb(unsigned long rgb)
    {
        return color((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
    }

    /**
       \brief create color from 32bit RGBA value, e.g. 0xffff0080 ->
       yellow with 50% transparency
    */
    inline color make_rgba(unsigned long rgba)
    {
        return color((rgba >> 24) & 0xff, (rgba >> 16) & 0xff, (rgba >> 8) & 0xff, rgba & 0xff);
    }

    inline bool operator==(const color& lhs, const color& rhs)
    {
        return lhs.rgba() == rhs.rgba();
    }

    inline bool operator!=(const color& lhs, const color& rhs)
    {
        return lhs.rgba() != rhs.rgba();
    }
}

#endif
<|endoftext|>


<commit_before>#include "allocator.hpp"
#include <mutex>
#include <vector>
#include <iostream>

template <typename T>
class stack {
public:
	explicit stack(size_t size = 0);
	auto operator=(stack const & other) /*strong*/ -> stack &;

	auto count() const -> size_t { return allocator_.count(); } /*noexcept*/
	bool empty() const { return allocator_.empty(); } /*noexcept*/

	void push(T const & value); /*strong*/
	void pop(); /*strong*/
	auto top()->T &; /*strong*/
	//auto top()->T const &; /*strong*/
private:
	allocator<T> allocator_;
	std::mutex mutex1;
};

template<typename T>
stack<T>::stack(size_t size) : allocator_(size) {
	;
}

template<typename T>
auto stack<T>::operator=(stack const & other) -> stack & {
	if (this != &other) {
		mutex1.lock();
		(allocator<T>(other.allocator_)).swap(allocator_);
		mutex1.unlock();
	}
	return *this;
}

template<typename T>
void stack<T>::push(T const & value) {
	mutex1.lock();
	if (allocator_.full()) {
		allocator_.resize();
	}
	allocator_.construct(this->count(), value);
	mutex1.unlock();
}

template<typename T>
void stack<T>::pop() {
	mutex1.lock();
	if (this->count() > 0) {
		allocator_.destroy(this->count() - 1);
	}
	else {
		mutex1.unlock();
		throw("stack is empty");
	}
	mutex1.unlock();
}

template<typename T>
auto stack<T>::top() -> T & {
	mutex1.lock();
	if (this->count() > 0) {
		mutex1.unlock();
		return allocator_.getElement(this->count() - 1);
	}
	else {
		mutex1.unlock();
		throw("stack is empty");
	}
}

/*template<typename T>
auto stack<T>::top() -> T const & {
	mutex1.lock();
	if (this->count() > 0) {
		mutex1.unlock();
		return allocator_.getElement(this->count() - 1);
	}
	else {
		mutex1.unlock();
		throw("stack is empty");
	}
}*/
<commit_msg>Update stack.hpp<commit_after>#include "allocator.hpp"
#include <mutex>
#include <vector>

template <typename T>
class stack {
public:
	explicit stack(size_t size = 0);
	stack(stack const & other);
	auto operator=(stack const & other) /*strong*/ -> stack &;

	auto count() const -> size_t { return allocator_.count(); } /*noexcept*/
	bool empty() const { return allocator_.empty(); } /*noexcept*/

	void push(T const & value); /*strong*/
	void pop(); /*strong*/
	auto top()->T &; /*strong*/
	//auto top()->T const &; /*strong*/
private:
	allocator<T> allocator_;
	std::mutex mutex;
};

template<typename T>
stack<T>::stack(size_t size) : allocator_(size) {
	;
}

template<typename T>
stack<T>::stack(stack const & other) : allocator_(other.allocator_) {
	;
}

template<typename T>
auto stack<T>::operator=(stack const & other) -> stack & {
	if (this != &other) {
		mutex.lock();
		(allocator<T>(other.allocator_)).swap(allocator_);
		mutex.unlock();
	}
	return *this;
}

template<typename T>
void stack<T>::push(T const & value) {
	mutex.lock();
	if (allocator_.full()) {
		allocator_.resize();
	}
	allocator_.construct(this->count(), value);
	mutex.unlock();
}

template<typename T>
void stack<T>::pop() {
	mutex.lock();
	if (this->count() > 0) {
		allocator_.destroy(this->count() - 1);
	}
	else {
		mutex.unlock();
		throw("stack is empty");
	}
	mutex.unlock();
}

template<typename T>
auto stack<T>::top() -> T & {
	mutex.lock();
	if (this->count() > 0) {
		mutex.unlock();
		return allocator_.getElement(this->count() - 1);
	}
	else {
		mutex.unlock();
		throw("stack is empty");
	}
}

/*template<typename T>
auto stack<T>::top() -> T const & {
	mutex.lock();
	if (this->count() > 0) {
		mutex.unlock();
		return allocator_.getElement(this->count() - 1);
	}
	else {
		mutex.unlock();
		throw("stack is empty");
	}
}*/
<|endoftext|>


<commit_before>// This file is part of OpenMVG, an Open Multiple View Geometry C++ library.

// Copyright (c) 2017 Romuald Perrot.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef _OPENMVG_CLUSTERING_K_MEANS_HPP_
#define _OPENMVG_CLUSTERING_K_MEANS_HPP_

#include "openMVG/clustering/kmeans_trait.hpp"
#include "openMVG/numeric/eigen_alias_definition.hpp"

#include <limits>
#include <random>
#include <vector>

namespace openMVG
{
namespace clustering
{

/**
* @brief Kind of initialization of the kmeans centers
*/
enum class KMeansInitType
{
  KMEANS_INIT_RANDOM, /* Standard Llyod algoritm */
  KMEANS_INIT_PP,     /* Kmeans++ initialization */
};

/**
* @brief Compute minimum distance to any center
* @param pts Input points
* @param centers Centers
* @param[out] dists computed (minimum) distance to any center
*/
template< typename DataType >
void MinimumDistanceToAnyCenter( const std::vector< DataType > & pts,
                                 const std::vector< DataType > & centers,
                                 std::vector< typename KMeansVectorDataTrait<DataType>::scalar_type > & dists )
{
  using trait = KMeansVectorDataTrait<DataType>;

  dists.resize( pts.size(), std::numeric_limits<typename trait::scalar_type>::max() );

  #pragma omp parallel for
  for( size_t id_pt = 0; id_pt < pts.size(); ++id_pt )
  {
    const auto & pt = pts[ id_pt ];
    for( const auto & c : centers )
    {
      const typename trait::scalar_type cur_d = trait::L2( pt, c );
      dists[ id_pt ] = std::min( dists[ id_pt ], cur_d );
    }
  }
}

/**
* @brief Compute Nearest center Id of a given point
* @param pt Query point
* @param centers list of test centers
* @return id of the nearest center (0-based)
*/
template< typename DataType >
uint32_t NearestCenterID( const DataType & pt,
                          const std::vector< DataType > & centers )
{
  using trait = KMeansVectorDataTrait<DataType>;
  const uint32_t nb_cluster = static_cast<uint32_t>( centers.size() );

  typename trait::scalar_type min_dist = std::numeric_limits<typename trait::scalar_type>::max();
  uint32_t nearest_center = nb_cluster;

  for( uint32_t cur_center = 0; cur_center < nb_cluster; ++cur_center )
  {
    const typename trait::scalar_type cur_dist = trait::L2( pt, centers[ cur_center ] );
    if( cur_dist < min_dist )
    {
      min_dist = cur_dist;
      nearest_center = cur_center;
    }
  }
  return nearest_center;
}

/**
* @brief Compute center of mass of a set a points
* @param pts List of points
* @param assigned_center Id of the center to be affected to a given point
* @param nb_center Number of center of mass in the result
* @return New centers of mass
*/
template< typename DataType >
std::vector< DataType > ComputeCenterOfMass( const std::vector< DataType > & pts,
    const std::vector< uint32_t > & assigned_center,
    const uint32_t nb_center )
{
  using trait = KMeansVectorDataTrait<DataType>;

  std::vector< DataType > new_centers( nb_center, trait::null( pts[0] ) );
  std::vector< uint32_t > nb_per_center( nb_center, 0 );

  // Affect points to centers
  for( size_t id_pt = 0; id_pt < pts.size(); ++id_pt )
  {
    const uint32_t id_center = assigned_center[id_pt];
    trait::accumulate( new_centers[ id_center ], pts[id_pt] );
    ++nb_per_center[id_center];
  }

  // Compute mean of centers based on the number of points affected to each centers
  #pragma omp parallel for
  for( uint32_t id_center = 0; id_center < nb_center; ++id_center )
  {
    trait::divide( new_centers[id_center], nb_per_center[id_center] );
  }

  return new_centers;
}

/**
* @brief Compute simple kmeans clustering on specified data
* @param source_data Input data
* @param[out] cluster_assignment index for each point in the input set to a specified cluster
* @param[out] centers Centers of the clusters
* @param nb_cluster requested number of cluster in the output
* @param max_nb_iteration maximum number of iteration to do for clustering
* @note This is the standard llyod algorithm
*/
template< typename DataType >
void KMeans( const std::vector< DataType > & source_data,
             std::vector< uint32_t > & cluster_assignment,
             std::vector< DataType > & centers,
             const uint32_t nb_cluster,
             const uint32_t max_nb_iteration = std::numeric_limits<uint32_t>::max(),
             const KMeansInitType init_type = KMeansInitType::KMEANS_INIT_PP )
{
  if( source_data.size() == 0 )
  {
    return;
  }

  using trait = KMeansVectorDataTrait<DataType>;

  std::mt19937_64 rng(std::mt19937_64::default_seed);

  // 1 - init center of mass
  if( init_type == KMeansInitType::KMEANS_INIT_PP )
  {
    // Kmeans++ init:
    // first one is a random one
    // the others based on the importance probability (Di / \sum_i Di) where:
    // Di is the minimum distance to any created centers already created
    std::uniform_int_distribution<size_t> distrib_first( 0, source_data.size() - 1 );
    centers.reserve( nb_cluster );
    centers.emplace_back( source_data[ distrib_first( rng ) ] );

    std::vector< typename trait::scalar_type > dists;

    for( uint32_t id_center = 1; id_center < nb_cluster; ++id_center )
    {
      // Compute Di / \sum Di pdf
      MinimumDistanceToAnyCenter( source_data, centers, dists );
      std::discrete_distribution<size_t> distrib_c( dists.cbegin(), dists.cend() );

      // Sample a point from this distribution
      centers.emplace_back( source_data[distrib_c( rng )] );
    }
  }
  else if (init_type == KMeansInitType::KMEANS_INIT_RANDOM)
  {
    DataType min, max;
    trait::minMax( source_data, min, max );

    // Standard Llyod init
    centers.resize( nb_cluster );
    std::uniform_int_distribution<size_t> distrib( 0, source_data.size() - 1 );
    for( auto & cur_center : centers  )
    {
      cur_center = source_data[distrib( rng )];
    }
  }
  else // Invalid Kmeans initialization type
  {
    return;
  }

  // Assign all element to the first center
  cluster_assignment.resize( source_data.size(), nb_cluster );

  bool changed;
  uint32_t id_iteration = 0;

  // 2 - Perform kmeans
  do
  {
    changed = false;

    // 2.1 affect center to each points
    #pragma omp parallel for shared(changed)
    for( size_t id_pt = 0; id_pt < source_data.size(); ++id_pt )
    {
      const DataType & cur_pt = source_data[id_pt];
      // Compute nearest center of this point
      const uint32_t nearest_center = NearestCenterID( cur_pt, centers );
      if( cluster_assignment[id_pt] != nearest_center )
      {
        cluster_assignment[id_pt] = nearest_center;
        changed = true;
      }
    }

    // 2.2 Compute new centers of mass
    centers = ComputeCenterOfMass( source_data, cluster_assignment, nb_cluster );

    ++id_iteration;
  }
  while( changed && id_iteration < max_nb_iteration );
}

} // namespace clustering
} // namespace openMVG

#endif
<commit_msg>[clustering] K-means #1476<commit_after>// This file is part of OpenMVG, an Open Multiple View Geometry C++ library.

// Copyright (c) 2017 Romuald Perrot.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef _OPENMVG_CLUSTERING_K_MEANS_HPP_
#define _OPENMVG_CLUSTERING_K_MEANS_HPP_

#include "openMVG/clustering/kmeans_trait.hpp"
#include "openMVG/numeric/eigen_alias_definition.hpp"

#include <limits>
#include <random>
#include <vector>

namespace openMVG
{
namespace clustering
{

/**
* @brief Kind of initialization of the kmeans centers
*/
enum class KMeansInitType
{
  KMEANS_INIT_RANDOM, /* Standard Llyod algoritm */
  KMEANS_INIT_PP,     /* Kmeans++ initialization */
};

/**
* @brief Compute minimum distance to any center
* @param pts Input points
* @param centers Centers
* @param[out] dists computed (minimum) distance to any center
*/
template< typename DataType >
void MinimumDistanceToAnyCenter( const std::vector< DataType > & pts,
                                 const std::vector< DataType > & centers,
                                 std::vector< typename KMeansVectorDataTrait<DataType>::scalar_type > & dists )
{
  using trait = KMeansVectorDataTrait<DataType>;

  dists.resize( pts.size(), std::numeric_limits<typename trait::scalar_type>::max() );

  #pragma omp parallel for
  for( int id_pt = 0; id_pt < static_cast<int>(pts.size()); ++id_pt )
  {
    const auto & pt = pts[ id_pt ];
    for( const auto & c : centers )
    {
      const typename trait::scalar_type cur_d = trait::L2( pt, c );
      dists[ id_pt ] = std::min( dists[ id_pt ], cur_d );
    }
  }
}

/**
* @brief Compute Nearest center Id of a given point
* @param pt Query point
* @param centers list of test centers
* @return id of the nearest center (0-based)
*/
template< typename DataType >
uint32_t NearestCenterID( const DataType & pt,
                          const std::vector< DataType > & centers )
{
  using trait = KMeansVectorDataTrait<DataType>;
  const uint32_t nb_cluster = static_cast<uint32_t>( centers.size() );

  typename trait::scalar_type min_dist = std::numeric_limits<typename trait::scalar_type>::max();
  uint32_t nearest_center = nb_cluster;

  for( uint32_t cur_center = 0; cur_center < nb_cluster; ++cur_center )
  {
    const typename trait::scalar_type cur_dist = trait::L2( pt, centers[ cur_center ] );
    if( cur_dist < min_dist )
    {
      min_dist = cur_dist;
      nearest_center = cur_center;
    }
  }
  return nearest_center;
}

/**
* @brief Compute center of mass of a set a points
* @param pts List of points
* @param assigned_center Id of the center to be affected to a given point
* @param nb_center Number of center of mass in the result
* @return New centers of mass
*/
template< typename DataType >
std::vector< DataType > ComputeCenterOfMass( const std::vector< DataType > & pts,
    const std::vector< uint32_t > & assigned_center,
    const uint32_t nb_center )
{
  using trait = KMeansVectorDataTrait<DataType>;

  std::vector< DataType > new_centers( nb_center, trait::null( pts[0] ) );
  std::vector< uint32_t > nb_per_center( nb_center, 0 );

  // Affect points to centers
  for( size_t id_pt = 0; id_pt < pts.size(); ++id_pt )
  {
    const uint32_t id_center = assigned_center[id_pt];
    trait::accumulate( new_centers[ id_center ], pts[id_pt] );
    ++nb_per_center[id_center];
  }

  // Compute mean of centers based on the number of points affected to each centers
  #pragma omp parallel for
  for( int id_center = 0; id_center < static_cast<int>(nb_center); ++id_center )
  {
    trait::divide( new_centers[id_center], nb_per_center[id_center] );
  }

  return new_centers;
}

/**
* @brief Compute simple kmeans clustering on specified data
* @param source_data Input data
* @param[out] cluster_assignment index for each point in the input set to a specified cluster
* @param[out] centers Centers of the clusters
* @param nb_cluster requested number of cluster in the output
* @param max_nb_iteration maximum number of iteration to do for clustering
* @note This is the standard llyod algorithm
*/
template< typename DataType >
void KMeans( const std::vector< DataType > & source_data,
             std::vector< uint32_t > & cluster_assignment,
             std::vector< DataType > & centers,
             const uint32_t nb_cluster,
             const uint32_t max_nb_iteration = std::numeric_limits<uint32_t>::max(),
             const KMeansInitType init_type = KMeansInitType::KMEANS_INIT_PP )
{
  if( source_data.size() == 0 )
  {
    return;
  }

  using trait = KMeansVectorDataTrait<DataType>;

  std::mt19937_64 rng(std::mt19937_64::default_seed);

  // 1 - init center of mass
  if( init_type == KMeansInitType::KMEANS_INIT_PP )
  {
    // Kmeans++ init:
    // first one is a random one
    // the others based on the importance probability (Di / \sum_i Di) where:
    // Di is the minimum distance to any created centers already created
    std::uniform_int_distribution<size_t> distrib_first( 0, source_data.size() - 1 );
    centers.reserve( nb_cluster );
    centers.emplace_back( source_data[ distrib_first( rng ) ] );

    std::vector< typename trait::scalar_type > dists;

    for( uint32_t id_center = 1; id_center < nb_cluster; ++id_center )
    {
      // Compute Di / \sum Di pdf
      MinimumDistanceToAnyCenter( source_data, centers, dists );
      std::discrete_distribution<size_t> distrib_c( dists.cbegin(), dists.cend() );

      // Sample a point from this distribution
      centers.emplace_back( source_data[distrib_c( rng )] );
    }
  }
  else if (init_type == KMeansInitType::KMEANS_INIT_RANDOM)
  {
    DataType min, max;
    trait::minMax( source_data, min, max );

    // Standard Llyod init
    centers.resize( nb_cluster );
    std::uniform_int_distribution<size_t> distrib( 0, source_data.size() - 1 );
    for( auto & cur_center : centers  )
    {
      cur_center = source_data[distrib( rng )];
    }
  }
  else // Invalid Kmeans initialization type
  {
    return;
  }

  // Assign all element to the first center
  cluster_assignment.resize( source_data.size(), nb_cluster );

  bool changed;
  uint32_t id_iteration = 0;

  // 2 - Perform kmeans
  do
  {
    changed = false;

    // 2.1 affect center to each points
    #pragma omp parallel for shared(changed)
    for( int id_pt = 0; id_pt < static_cast<int>(source_data.size()); ++id_pt )
    {
      const DataType & cur_pt = source_data[id_pt];
      // Compute nearest center of this point
      const uint32_t nearest_center = NearestCenterID( cur_pt, centers );
      if( cluster_assignment[id_pt] != nearest_center )
      {
        cluster_assignment[id_pt] = nearest_center;
        changed = true;
      }
    }

    // 2.2 Compute new centers of mass
    centers = ComputeCenterOfMass( source_data, cluster_assignment, nb_cluster );

    ++id_iteration;
  }
  while( changed && id_iteration < max_nb_iteration );
}

} // namespace clustering
} // namespace openMVG

#endif
<|endoftext|>