<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsensormanager.h"
#include <QDebug>
#include "qsensorpluginloader_p.h"
#include "qsensorplugin.h"
#include <QSettings>
#include "sensorlog_p.h"

QTM_BEGIN_NAMESPACE

Q_GLOBAL_STATIC_WITH_ARGS(QSensorPluginLoader, pluginLoader, (QSensorPluginInterface_iid, QLatin1String("/sensors")))

typedef QHash<QByteArray,QSensorBackendFactory*> FactoryForIdentifierMap;
typedef QHash<QByteArray,FactoryForIdentifierMap> BackendIdentifiersForTypeMap;

class QSensorManagerPrivate
{
public:
    QSensorManagerPrivate()
        : pluginsLoaded(false)
    {
    }

    bool pluginsLoaded;

    QList<CreatePluginFunc> staticRegistrations;

    // Holds a mapping from type to available identifiers (and from there to the factory)
    BackendIdentifiersForTypeMap backendsByType;

    // Holds the first identifier for each type
    QHash<QByteArray, QByteArray> firstIdentifierForType;
};

Q_GLOBAL_STATIC(QSensorManagerPrivate, sensorManagerPrivate)

static void loadPlugins()
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    d->pluginsLoaded = true;

    SENSORLOG() << "initializing static plugins";
    Q_FOREACH (CreatePluginFunc func, d->staticRegistrations) {
        QSensorPluginInterface *plugin = func();
        plugin->registerSensors();
    }

    SENSORLOG() << "initializing plugins";
    Q_FOREACH (QSensorPluginInterface *plugin, pluginLoader()->plugins()) {
        plugin->registerSensors();
    }
}

// =====================================================================

/*!
    \class QSensorManager
    \ingroup sensors_backend

    \preliminary
    \brief The QSensorManager class handles registration and creation of sensor backends.

    Sensor plugins register backends using the registerBackend() function.

    When QSensor::connect() is called, the createBackend() function will be called.
*/

/*!
    Register a sensor for \a type. The \a identifier must be unique.

    The \a factory will be asked to create instances of the backend.
*/
void QSensorManager::registerBackend(const QByteArray &type, const QByteArray &identifier, QSensorBackendFactory *factory)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->backendsByType.contains(type)) {
        (void)d->backendsByType[type];
        d->firstIdentifierForType[type] = identifier;
    }
    SENSORLOG() << "registering backend for type" << type << "identifier" << identifier;// << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
    FactoryForIdentifierMap &factoryByIdentifier = d->backendsByType[type];
    factoryByIdentifier[identifier] = factory;
}

/*!
    \internal
*/
void QSensorManager::registerStaticPlugin(CreatePluginFunc func)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    d->staticRegistrations.append(func);
}

/*!
    Create a backend for \a sensor. Returns null if no suitable backend exists.
*/
QSensorBackend *QSensorManager::createBackend(QSensor *sensor)
{
    Q_ASSERT(sensor);

    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    SENSORLOG() << "QSensorManager::createBackend" << "type" << sensor->type() << "identifier" << sensor->identifier();

    if (!d->backendsByType.contains(sensor->type())) {
        SENSORLOG() << "no backends of type" << sensor->type() << "have been registered.";
        return 0;
    }

    const FactoryForIdentifierMap &factoryByIdentifier = d->backendsByType[sensor->type()];
    QSensorBackendFactory *factory;
    QSensorBackend *backend;

    if (sensor->identifier().isEmpty()) {
        QByteArray defaultIdentifier = QSensor::defaultSensorForType(sensor->type());
        SENSORLOG() << "Trying the default" << defaultIdentifier;
        // No identifier set, try the default
        factory = factoryByIdentifier[defaultIdentifier];
        SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
        sensor->setIdentifier(defaultIdentifier); // the factory requires this
        backend = factory->createBackend(sensor);
        if (backend) return backend; // Got it!

        // The default failed to instantiate so try any other registered sensors for this type
        Q_FOREACH (const QByteArray &identifier, factoryByIdentifier.keys()) {
            SENSORLOG() << "Trying" << identifier;
            if (identifier == defaultIdentifier) continue; // Don't do the default one again
            factory = factoryByIdentifier[identifier];
            SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
            sensor->setIdentifier(identifier); // the factory requires this
            backend = factory->createBackend(sensor);
            if (backend) return backend; // Got it!
        }
        SENSORLOG() << "FAILED";
        sensor->setIdentifier(QByteArray()); // clear the identifier
    } else {
        if (!factoryByIdentifier.contains(sensor->identifier())) {
            SENSORLOG() << "no backend with identifier" << sensor->identifier() << "for type" << sensor->type();
            return 0;
        }

        // We were given an explicit identifier so don't substitute other backends if it fails to instantiate
        factory = factoryByIdentifier[sensor->identifier()];
        SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
        backend = factory->createBackend(sensor);
        if (backend) return backend; // Got it!
    }

    SENSORLOG() << "no suitable backend found for requested identifier" << sensor->identifier() << "and type" << sensor->type();
    return 0;
}

// =====================================================================

/*!
    Returns a list of all sensor types.
*/
QList<QByteArray> QSensor::sensorTypes()
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    return d->backendsByType.keys();
}

/*!
    Returns a list of ids for each of the sensors for \a type.
    If there are no sensors of that type available the list will be empty.
*/
QList<QByteArray> QSensor::sensorsForType(const QByteArray &type)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    // no sensors of that type exist
    if (!d->backendsByType.contains(type))
        return QList<QByteArray>();

    return d->backendsByType[type].keys();
}

/*!
    Returns the default sensor identifier for \a type.
    This is set in a config file and can be overridden if required.
    If no default is available the system will return the first registered
    sensor for \a type.
*/
QByteArray QSensor::defaultSensorForType(const QByteArray &type)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    // no sensors of that type exist
    if (!d->backendsByType.contains(type))
        return QByteArray();

    QSettings settings(QLatin1String("Nokia"), QLatin1String("Sensors"));
    QVariant value = settings.value(QString(QLatin1String("Default/%1")).arg(QString::fromLatin1(type)));
    if (!value.isNull()) {
        QByteArray defaultIdentifier = value.toByteArray();
        if (d->backendsByType[type].contains(defaultIdentifier)) // Don't return a value that we can't use!
            return defaultIdentifier;
    }

    // This is our fallback
    return d->firstIdentifierForType[type];
}

// =====================================================================

/*!
    \class QSensorBackendFactory
    \ingroup sensors_backend

    \preliminary
    \brief The QSensorBackendFactory class instantiates instances of
           QSensorBackend.

    This interface must be implemented in order to register a sensor backend.

    \sa {Creating a sensor plugin}
*/

/*!
    \fn QSensorBackendFactory::~QSensorBackendFactory()
    \internal
*/

/*!
    \fn QSensorBackendFactory::createBackend(QSensor *sensor)

    Instantiate a backend. If the factory handles multiple identifiers
    it should check with the \a sensor to see which one is requested.

    If the factory cannot create a backend it should return 0.
*/

/*!
    \macro REGISTER_STATIC_PLUGIN(pluginname)
    \relates QSensorManager

    Registers a static plugin, \a pluginname.

    Note that this macro relies on static initialization so it may not be appropriate
    for use in a library and may not work on all platforms.

    \sa {Creating a sensor plugin}
*/

QTM_END_NAMESPACE

<commit_msg>disable these logs because they break on 64-bit builds.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsensormanager.h"
#include <QDebug>
#include "qsensorpluginloader_p.h"
#include "qsensorplugin.h"
#include <QSettings>
#include "sensorlog_p.h"

QTM_BEGIN_NAMESPACE

Q_GLOBAL_STATIC_WITH_ARGS(QSensorPluginLoader, pluginLoader, (QSensorPluginInterface_iid, QLatin1String("/sensors")))

typedef QHash<QByteArray,QSensorBackendFactory*> FactoryForIdentifierMap;
typedef QHash<QByteArray,FactoryForIdentifierMap> BackendIdentifiersForTypeMap;

class QSensorManagerPrivate
{
public:
    QSensorManagerPrivate()
        : pluginsLoaded(false)
    {
    }

    bool pluginsLoaded;

    QList<CreatePluginFunc> staticRegistrations;

    // Holds a mapping from type to available identifiers (and from there to the factory)
    BackendIdentifiersForTypeMap backendsByType;

    // Holds the first identifier for each type
    QHash<QByteArray, QByteArray> firstIdentifierForType;
};

Q_GLOBAL_STATIC(QSensorManagerPrivate, sensorManagerPrivate)

static void loadPlugins()
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    d->pluginsLoaded = true;

    SENSORLOG() << "initializing static plugins";
    Q_FOREACH (CreatePluginFunc func, d->staticRegistrations) {
        QSensorPluginInterface *plugin = func();
        plugin->registerSensors();
    }

    SENSORLOG() << "initializing plugins";
    Q_FOREACH (QSensorPluginInterface *plugin, pluginLoader()->plugins()) {
        plugin->registerSensors();
    }
}

// =====================================================================

/*!
    \class QSensorManager
    \ingroup sensors_backend

    \preliminary
    \brief The QSensorManager class handles registration and creation of sensor backends.

    Sensor plugins register backends using the registerBackend() function.

    When QSensor::connect() is called, the createBackend() function will be called.
*/

/*!
    Register a sensor for \a type. The \a identifier must be unique.

    The \a factory will be asked to create instances of the backend.
*/
void QSensorManager::registerBackend(const QByteArray &type, const QByteArray &identifier, QSensorBackendFactory *factory)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->backendsByType.contains(type)) {
        (void)d->backendsByType[type];
        d->firstIdentifierForType[type] = identifier;
    }
    SENSORLOG() << "registering backend for type" << type << "identifier" << identifier;// << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
    FactoryForIdentifierMap &factoryByIdentifier = d->backendsByType[type];
    factoryByIdentifier[identifier] = factory;
}

/*!
    \internal
*/
void QSensorManager::registerStaticPlugin(CreatePluginFunc func)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    d->staticRegistrations.append(func);
}

/*!
    Create a backend for \a sensor. Returns null if no suitable backend exists.
*/
QSensorBackend *QSensorManager::createBackend(QSensor *sensor)
{
    Q_ASSERT(sensor);

    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    SENSORLOG() << "QSensorManager::createBackend" << "type" << sensor->type() << "identifier" << sensor->identifier();

    if (!d->backendsByType.contains(sensor->type())) {
        SENSORLOG() << "no backends of type" << sensor->type() << "have been registered.";
        return 0;
    }

    const FactoryForIdentifierMap &factoryByIdentifier = d->backendsByType[sensor->type()];
    QSensorBackendFactory *factory;
    QSensorBackend *backend;

    if (sensor->identifier().isEmpty()) {
        QByteArray defaultIdentifier = QSensor::defaultSensorForType(sensor->type());
        SENSORLOG() << "Trying the default" << defaultIdentifier;
        // No identifier set, try the default
        factory = factoryByIdentifier[defaultIdentifier];
        //SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
        sensor->setIdentifier(defaultIdentifier); // the factory requires this
        backend = factory->createBackend(sensor);
        if (backend) return backend; // Got it!

        // The default failed to instantiate so try any other registered sensors for this type
        Q_FOREACH (const QByteArray &identifier, factoryByIdentifier.keys()) {
            SENSORLOG() << "Trying" << identifier;
            if (identifier == defaultIdentifier) continue; // Don't do the default one again
            factory = factoryByIdentifier[identifier];
            //SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
            sensor->setIdentifier(identifier); // the factory requires this
            backend = factory->createBackend(sensor);
            if (backend) return backend; // Got it!
        }
        SENSORLOG() << "FAILED";
        sensor->setIdentifier(QByteArray()); // clear the identifier
    } else {
        if (!factoryByIdentifier.contains(sensor->identifier())) {
            SENSORLOG() << "no backend with identifier" << sensor->identifier() << "for type" << sensor->type();
            return 0;
        }

        // We were given an explicit identifier so don't substitute other backends if it fails to instantiate
        factory = factoryByIdentifier[sensor->identifier()];
        //SENSORLOG() << "factory" << QString().sprintf("0x%08x", (unsigned int)factory);
        backend = factory->createBackend(sensor);
        if (backend) return backend; // Got it!
    }

    SENSORLOG() << "no suitable backend found for requested identifier" << sensor->identifier() << "and type" << sensor->type();
    return 0;
}

// =====================================================================

/*!
    Returns a list of all sensor types.
*/
QList<QByteArray> QSensor::sensorTypes()
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    return d->backendsByType.keys();
}

/*!
    Returns a list of ids for each of the sensors for \a type.
    If there are no sensors of that type available the list will be empty.
*/
QList<QByteArray> QSensor::sensorsForType(const QByteArray &type)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    // no sensors of that type exist
    if (!d->backendsByType.contains(type))
        return QList<QByteArray>();

    return d->backendsByType[type].keys();
}

/*!
    Returns the default sensor identifier for \a type.
    This is set in a config file and can be overridden if required.
    If no default is available the system will return the first registered
    sensor for \a type.
*/
QByteArray QSensor::defaultSensorForType(const QByteArray &type)
{
    QSensorManagerPrivate *d = sensorManagerPrivate();
    if (!d->pluginsLoaded)
        loadPlugins();

    // no sensors of that type exist
    if (!d->backendsByType.contains(type))
        return QByteArray();

    QSettings settings(QLatin1String("Nokia"), QLatin1String("Sensors"));
    QVariant value = settings.value(QString(QLatin1String("Default/%1")).arg(QString::fromLatin1(type)));
    if (!value.isNull()) {
        QByteArray defaultIdentifier = value.toByteArray();
        if (d->backendsByType[type].contains(defaultIdentifier)) // Don't return a value that we can't use!
            return defaultIdentifier;
    }

    // This is our fallback
    return d->firstIdentifierForType[type];
}

// =====================================================================

/*!
    \class QSensorBackendFactory
    \ingroup sensors_backend

    \preliminary
    \brief The QSensorBackendFactory class instantiates instances of
           QSensorBackend.

    This interface must be implemented in order to register a sensor backend.

    \sa {Creating a sensor plugin}
*/

/*!
    \fn QSensorBackendFactory::~QSensorBackendFactory()
    \internal
*/

/*!
    \fn QSensorBackendFactory::createBackend(QSensor *sensor)

    Instantiate a backend. If the factory handles multiple identifiers
    it should check with the \a sensor to see which one is requested.

    If the factory cannot create a backend it should return 0.
*/

/*!
    \macro REGISTER_STATIC_PLUGIN(pluginname)
    \relates QSensorManager

    Registers a static plugin, \a pluginname.

    Note that this macro relies on static initialization so it may not be appropriate
    for use in a library and may not work on all platforms.

    \sa {Creating a sensor plugin}
*/

QTM_END_NAMESPACE

<|endoftext|>


<commit_before>// Copyright (c) 2012 Steinwurf ApS
// All Rights Reserved
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Steinwurf ApS nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Steinwurf ApS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "buffer.hpp"

#include <algorithm>

namespace sak
{

    buffer::buffer()
        : m_size(0)
    { }

    buffer::buffer(uint32_t capacity)
        : m_size(0)
    {
        m_vector.resize(capacity);
    }

    void buffer::append(const uint8_t* data, uint32_t size)
    {
        assert(data != 0);
        assert(size > 0);

        if (m_size + size > m_vector.size())
        {
            m_vector.resize(m_size + size);
        }

        std::copy_n(data, size, &m_vector[m_size]);
        m_size += size;
    }

    void buffer::append(const uint8_t* data_start, const uint8_t* data_end)
    {
        assert(data_start != 0);
        assert(data_end != 0);
        assert(data_start < data_end);

        uint32_t size = static_cast<uint32_t>(data_end - data_start);
        append(data_start, size);
    }

    const uint8_t* buffer::data() const
    {
        return &m_vector[0];
    }

    uint8_t* buffer::data()
    {
        return &m_vector[0];
    }

    uint32_t buffer::size() const
    {
        return m_size;
    }

    void buffer::resize(uint32_t size)
    {
        // Currently our re-allocation strategy is not
        // to "shrink" the buffer. If at some point this
        // should become important in an application
        // we could add that possibility as a reallocation-policy

        if (size <= m_vector.size())
        {
            m_size = size;
        }
        else
        {
            m_vector.resize(size);
            m_size = size;
        }
    }

    void buffer::clear()
    {
        m_size = 0;
    }

}
<commit_msg>Test buffer::append overload<commit_after>// Copyright (c) 2012 Steinwurf ApS
// All Rights Reserved
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Steinwurf ApS nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Steinwurf ApS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "buffer.hpp"

#include <algorithm>

namespace sak
{

    buffer::buffer()
        : m_size(0)
    { }

    buffer::buffer(uint32_t capacity)
        : m_size(0)
    {
        m_vector.resize(capacity);
    }

    void buffer::append(const uint8_t* data, uint32_t size)
    {
        assert(data != 0);
        assert(size > 0);

        if (m_size + size > m_vector.size())
        {
            m_vector.resize(m_size + size);
        }

        std::copy_n(data, size, &m_vector[m_size]);
        m_size += size;
    }

//     void buffer::append(const uint8_t* data_start, const uint8_t* data_end)
//     {
//         assert(data_start != 0);
//         assert(data_end != 0);
//         assert(data_start < data_end);
//
//         uint32_t size = static_cast<uint32_t>(data_end - data_start);
//         append(data_start, size);
//     }

    const uint8_t* buffer::data() const
    {
        return &m_vector[0];
    }

    uint8_t* buffer::data()
    {
        return &m_vector[0];
    }

    uint32_t buffer::size() const
    {
        return m_size;
    }

    void buffer::resize(uint32_t size)
    {
        // Currently our re-allocation strategy is not
        // to "shrink" the buffer. If at some point this
        // should become important in an application
        // we could add that possibility as a reallocation-policy

        if (size <= m_vector.size())
        {
            m_size = size;
        }
        else
        {
            m_vector.resize(size);
            m_size = size;
        }
    }

    void buffer::clear()
    {
        m_size = 0;
    }

}
<|endoftext|>


<commit_before>
#include "nAIControl.hpp"

#include <typeindex>
#include <algorithm>
#include <iostream>
#include <random>

#include "cAIControl.hpp"
#include "cLiving.hpp"
#include "../ec/cPosition.hpp"
#include "../ec/cVelocity.hpp"
#include "AI/pathFinder.hpp"
#include "../context.hpp"
#include "utility.hpp"
#include "cPathFinderRef.hpp"
#include "cPowerup.hpp"
#include "cPickup.hpp"
#include "../ec/engine.hpp"
#include "direction.hpp"
#include "cWIndicator.hpp"
#include "cBalloon.hpp"

nAIControl::nAIControl() :
control(nullptr),
living(nullptr),
pos(nullptr),
vel(nullptr),
prevDest(-1)
{}

bool nAIControl::checkEntity(Entity& entity)
{
    return entity.hasComponent(std::type_index(typeid(cAIControl))) &&
           entity.hasComponent(std::type_index(typeid(cLiving))) &&
           entity.hasComponent(std::type_index(typeid(cPosition))) &&
           entity.hasComponent(std::type_index(typeid(cVelocity))) &&
           entity.hasComponent(std::type_index(typeid(cPathFinderRef)));
}

std::unique_ptr<Node> nAIControl::getNewNode()
{
    return std::unique_ptr<Node>(new nAIControl);
}

void nAIControl::getCReferencesFromEntity(Entity& entity)
{
    control = static_cast<cAIControl*>(entity.getComponent(std::type_index(typeid(cAIControl))));
    living = static_cast<cLiving*>(entity.getComponent(std::type_index(typeid(cLiving))));
    pos = static_cast<cPosition*>(entity.getComponent(std::type_index(typeid(cPosition))));
    vel = static_cast<cVelocity*>(entity.getComponent(std::type_index(typeid(cVelocity))));
    pfref = static_cast<cPathFinderRef*>(entity.getComponent(std::type_index(typeid(cPathFinderRef))));
    entityRemoved = &entity.removed;
}

void nAIControl::update(sf::Time dt, Context context)
{
    if(*entityRemoved)
        return;

    bool justPlacedBalloon = false;
    control->timer -= dt.asSeconds();
    if(control->timer <= 0.0f || control->currentAction == AI::NONE)
    {
        control->timer = control->aiTickTime;
        control->currentAction = control->ai.determineAction(*pos, *living, *control->pf, *context.ecEngine, *context.rGen);
        if(living->kickUpgrade == 0)
            paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x16);
        else
            paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x14);

        const unsigned char* grid = control->pf->getValidGrid(*context.ecEngine);

        int xy = (int)((pos->x + (float)(-GRID_OFFSET_X + GRID_SQUARE_SIZE / 2)) / GRID_SQUARE_SIZE) + (int)((pos->y + (float)(-GRID_OFFSET_Y + GRID_SQUARE_SIZE / 2)) / GRID_SQUARE_SIZE) * GRID_WIDTH;
        int prev, destination = -1;
        std::uniform_int_distribution<> dist(0,paths.size() - 1);
        int r = dist(*context.rGen);

        std::clog << "STATE: " << control->currentAction << '\n';
        vel->x = 0.0f;
        vel->y = 0.0f;

        if(prevDest == -1)
        {
            switch(control->currentAction)
            {
            case AI::PLACE_BALLOON:
                justPlacedBalloon = true;
                Utility::createBalloon(pos->x, pos->y, *living, context, control->ID, &control->fired, *pfref);
                control->timer = 0.0f;
                control->pf->invalidateValidGrid();
                break;
            case AI::GET_POWERUP:
                break;
            case AI::MOVE_TO_ENEMY:
                break;
            case AI::MOVE_TO_BREAKABLE:
                destination = -1;
                for(auto iter = paths.begin(); iter != paths.end(); ++iter)
                {
                    if((iter->first % GRID_WIDTH != 0 && (grid[iter->first - 1] & 0x4) != 0) ||
                       (iter->first % GRID_WIDTH != GRID_WIDTH - 1 && (grid[iter->first + 1] & 0x4) != 0) ||
                       (iter->first - GRID_WIDTH >= 0 && (grid[iter->first - GRID_WIDTH] & 0x4) != 0) ||
                       (iter->first + GRID_WIDTH < GRID_TOTAL && (grid[iter->first + GRID_WIDTH] & 0x4) != 0))
                    {
                        destination = iter->first;
                        if(r <= 0)
                            break;
                    }
                    --r;
                }
                if(destination == -1)
                {
                    std::clog << "WARNING: failed to find adjacent breakable\n";
                    control->timer = 0.0f;
                    break;
                }
                break;
            case AI::MOVE_TO_SAFETY:
                destination = -1;
                for(auto iter = paths.begin(); iter != paths.end(); ++iter)
                {
                    if((grid[iter->first] & 0x36) == 0)
                    {
                        destination = iter->first;
                        if(r <= 0)
                            break;
                    }
                    --r;
                }
                if(destination == -1)
                {
                    std::clog << "WARNING: failed to find safe spot\n";
                    control->timer = 0.0f;
                    break;
                }
                break;
            case AI::KICK_BALLOON:
                break;
            case AI::PANIC:
                break;
            case AI::WAIT:
                control->timer = BALLOON_ALIVE_TIME;
                break;
            default:
                break;
            }
        }
        if(destination != -1 || prevDest != -1)
        {
            if(prevDest != -1)
                destination = prevDest;
            Direction::Direction prevDir = Direction::NONE;
            Direction::Direction destDir = Direction::NONE;
            if(destination != xy && Utility::isAligned(pos->x, pos->y))
            {
                std::map<int,int> rpaths;
                int dest = destination;
                prevDest = destination;
                while(destination != xy)
                {
                    rpaths.insert(std::make_pair(paths[destination], destination));
                    destination = paths[destination];
                }

                int i = GRID_TOTAL;
                destination = xy;
                prev = destination;
                while(destination != dest)
                {
                    destination = rpaths[destination];
                    if(destination % GRID_WIDTH == prev % GRID_WIDTH)
                    {
                        destDir = Direction::VERTICAL;
                    }
                    else
                    {
                        destDir = Direction::HORIZONTAL;
                    }

                    if(prevDir == Direction::NONE)
                        prevDir = destDir;
                    else if(prevDir != destDir)
                        break;
                    prev = destination;

                    if(i-- == 0)
                        break;
                }
                if(prevDir == Direction::NONE)
                    prevDir = destDir;
                if(destination == prevDest)
                    prevDest = -1;
            }
            else
                prevDir = Direction::PLUS;

            if(prevDir == Direction::VERTICAL)
            {
                if(prev < xy)
                {
                    vel->x = 0.0f;
                    vel->y = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    control->timer = (float)((xy / GRID_WIDTH - prev / GRID_WIDTH) * GRID_SQUARE_SIZE) / -vel->y;
                }
                else
                {
                    vel->x = 0.0f;
                    vel->y = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    if(prev == xy)
                        control->timer = 0.0f;
                    else
                        control->timer = (float)((prev / GRID_WIDTH - xy / GRID_WIDTH) * GRID_SQUARE_SIZE) / vel->y;
                }
            }
            else if(prevDir == Direction::HORIZONTAL)
            {
                if(prev < xy)
                {
                    vel->x = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (float)((xy - prev) * GRID_SQUARE_SIZE) / -vel->x;
                }
                else
                {
                    vel->x = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    if(prev == xy)
                        control->timer = 0.0f;
                    else
                        control->timer = (float)((prev - xy) * GRID_SQUARE_SIZE) / vel->x;
                }
            }
            else if(prevDir == Direction::PLUS)
            {
                float offsetx = (pos->x - (float)GRID_OFFSET_X) / (float)GRID_SQUARE_SIZE;
                float offsety = (pos->y - (float)GRID_OFFSET_Y) / (float)GRID_SQUARE_SIZE;
                float x = xy % GRID_WIDTH;
                float y = xy / GRID_WIDTH;
                if(offsetx < x)
                {
                    vel->x = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (pos->x - offsetx) / -vel->x;
                }
                else if(offsetx > x)
                {
                    vel->x = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (offsetx - pos->x) / vel->x;
                }
                else if(offsety < y)
                {
                    vel->x = 0.0f;
                    vel->y = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    control->timer = (pos->y - offsety) / -vel->y;
                }
                else if(offsety >= y)
                {
                    vel->x = 0.0f;
                    vel->y = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    if(offsety != y)
                        control->timer = (offsety - pos->y) / vel->y;
                    else
                        control->timer = 0.0f;
                }
            }
            else
            {
                std::cerr << "ERROR: Failed to validate next tile on move\n";
                std::clog << "prev is " << prev << '\n';
                control->timer = 0.0f;
            }
        }
    }

    HitInfo powerinfo = Utility::collideAgainstComponent(pos->x, pos->y, std::type_index(typeid(cPickup)), *context.ecEngine);
    for(auto piter = powerinfo.hit.begin(); piter != powerinfo.hit.end(); ++piter)
    {
        cPowerup* powerup = static_cast<cPowerup*>((*piter)->getComponent(std::type_index(typeid(cPowerup))));

        switch(powerup->powerup)
        {
        case cPowerup::BALLOON_UP:
            ++(living->balloonUp);
            break;
        case cPowerup::RANGE_UP:
            ++(living->rangeUp);
            break;
        case cPowerup::SPEED_UP:
            ++(living->speedUp);
            break;
        case cPowerup::KICK_UPGRADE:
            ++(living->kickUpgrade);
            break;
        case cPowerup::RCONTROL_UPGRADE:
            ++(living->rControlUpgrade);
            break;
        case cPowerup::SBALLOON_UPGRADE:
            ++(living->sBalloonUpgrade);
            break;
        case cPowerup::PIERCE_UPGRADE:
            ++(living->pierceUpgrade);
            break;
        case cPowerup::SPREAD_UPGRADE:
            ++(living->spreadUpgrade);
            break;
        case cPowerup::GHOST_UPGRADE:
            ++(living->ghostUpgrade);
            break;
        default:
            break;
        }

        context.ecEngine->removeEntity((*piter)->getID());
    }

    if(living->rControlUpgrade > 0)
    {
        bool inDanger = Utility::collidesAgainstComponent(pos->x, pos->y, std::type_index(typeid(cWIndicator)), *context.ecEngine);
        if(justPlacedBalloon || inDanger)
        {
            control->fired = false;
        }
        else if(!justPlacedBalloon && !inDanger)
        {
            control->fired = true;
        }
    }
}
<commit_msg>minor fixes<commit_after>
#include "nAIControl.hpp"

#include <typeindex>
#include <algorithm>
#include <iostream>
#include <random>

#include "cAIControl.hpp"
#include "cLiving.hpp"
#include "../ec/cPosition.hpp"
#include "../ec/cVelocity.hpp"
#include "AI/pathFinder.hpp"
#include "../context.hpp"
#include "utility.hpp"
#include "cPathFinderRef.hpp"
#include "cPowerup.hpp"
#include "cPickup.hpp"
#include "../ec/engine.hpp"
#include "direction.hpp"
#include "cWIndicator.hpp"
#include "cBalloon.hpp"

nAIControl::nAIControl() :
control(nullptr),
living(nullptr),
pos(nullptr),
vel(nullptr),
prevDest(-1)
{}

bool nAIControl::checkEntity(Entity& entity)
{
    return entity.hasComponent(std::type_index(typeid(cAIControl))) &&
           entity.hasComponent(std::type_index(typeid(cLiving))) &&
           entity.hasComponent(std::type_index(typeid(cPosition))) &&
           entity.hasComponent(std::type_index(typeid(cVelocity))) &&
           entity.hasComponent(std::type_index(typeid(cPathFinderRef)));
}

std::unique_ptr<Node> nAIControl::getNewNode()
{
    return std::unique_ptr<Node>(new nAIControl);
}

void nAIControl::getCReferencesFromEntity(Entity& entity)
{
    control = static_cast<cAIControl*>(entity.getComponent(std::type_index(typeid(cAIControl))));
    living = static_cast<cLiving*>(entity.getComponent(std::type_index(typeid(cLiving))));
    pos = static_cast<cPosition*>(entity.getComponent(std::type_index(typeid(cPosition))));
    vel = static_cast<cVelocity*>(entity.getComponent(std::type_index(typeid(cVelocity))));
    pfref = static_cast<cPathFinderRef*>(entity.getComponent(std::type_index(typeid(cPathFinderRef))));
    entityRemoved = &entity.removed;
}

void nAIControl::update(sf::Time dt, Context context)
{
    if(*entityRemoved)
        return;

    bool justPlacedBalloon = false;
    control->timer -= dt.asSeconds();
    if(control->timer <= 0.0f || control->currentAction == AI::NONE)
    {
        control->timer = control->aiTickTime;
        control->currentAction = control->ai.determineAction(*pos, *living, *control->pf, *context.ecEngine, *context.rGen);
        if(Utility::collidesAgainstComponent(pos->x, pos->y, std::type_index(typeid(cWIndicator)), *context.ecEngine))
        {
            if(living->kickUpgrade == 0)
                paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x16);
            else
                paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x14);
        }
        else
        {
            if(living->kickUpgrade == 0)
                paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x36);
            else
                paths = control->pf->getValidDestinations(*pos, *context.ecEngine, 0x34);
        }

        const unsigned char* grid = control->pf->getValidGrid(*context.ecEngine);

        int xy = (int)((pos->x + (float)(-GRID_OFFSET_X + GRID_SQUARE_SIZE / 2)) / GRID_SQUARE_SIZE) + (int)((pos->y + (float)(-GRID_OFFSET_Y + GRID_SQUARE_SIZE / 2)) / GRID_SQUARE_SIZE) * GRID_WIDTH;
        int prev, destination = -1;
        std::uniform_int_distribution<> dist(0,paths.size() - 1);
        int r = dist(*context.rGen);

        std::clog << "STATE: " << control->currentAction << '\n';
        vel->x = 0.0f;
        vel->y = 0.0f;

        if(prevDest == -1)
        {
            switch(control->currentAction)
            {
            case AI::PLACE_BALLOON:
                justPlacedBalloon = true;
                Utility::createBalloon(pos->x, pos->y, *living, context, control->ID, &control->fired, *pfref);
                control->timer = 0.0f;
                control->pf->invalidateValidGrid();
                break;
            case AI::GET_POWERUP:
                destination = -1;
                for(auto iter = paths.begin(); iter != paths.end(); ++iter)
                {
                    if((grid[iter->first] & 0x8) != 0)
                    {
                        destination = iter->first;
                        if(r <= 0)
                            break;
                    }
                    --r;
                }
                if(destination == -1)
                {
                    std::clog << "WARNING: failed to find valid powerup path\n";
                    control->timer = 0.0f;
                    break;
                }
                break;
            case AI::MOVE_TO_ENEMY:
                break;
            case AI::MOVE_TO_BREAKABLE:
                destination = -1;
                for(auto iter = paths.begin(); iter != paths.end(); ++iter)
                {
                    if((iter->first % GRID_WIDTH != 0 && (grid[iter->first - 1] & 0x4) != 0) ||
                       (iter->first % GRID_WIDTH != GRID_WIDTH - 1 && (grid[iter->first + 1] & 0x4) != 0) ||
                       (iter->first - GRID_WIDTH >= 0 && (grid[iter->first - GRID_WIDTH] & 0x4) != 0) ||
                       (iter->first + GRID_WIDTH < GRID_TOTAL && (grid[iter->first + GRID_WIDTH] & 0x4) != 0))
                    {
                        destination = iter->first;
                        if(r <= 0)
                            break;
                    }
                    --r;
                }
                if(destination == -1)
                {
                    std::clog << "WARNING: failed to find adjacent breakable\n";
                    control->timer = 0.0f;
                    break;
                }
                break;
            case AI::MOVE_TO_SAFETY:
                destination = -1;
                for(auto iter = paths.begin(); iter != paths.end(); ++iter)
                {
                    if((grid[iter->first] & 0x36) == 0)
                    {
                        destination = iter->first;
                        if(r <= 0)
                            break;
                    }
                    --r;
                }
                if(destination == -1)
                {
                    std::clog << "WARNING: failed to find safe spot\n";
                    control->timer = 0.0f;
                    break;
                }
                break;
            case AI::KICK_BALLOON:
                break;
            case AI::PANIC:
                break;
            case AI::WAIT:
                control->timer = BALLOON_ALIVE_TIME;
                break;
            default:
                break;
            }
        }
        if(destination != -1 || prevDest != -1)
        {
            if(prevDest != -1)
                destination = prevDest;
            Direction::Direction prevDir = Direction::NONE;
            Direction::Direction destDir = Direction::NONE;
            if(destination != xy && Utility::isAligned(pos->x, pos->y))
            {
                std::map<int,int> rpaths;
                int dest = destination;
                prevDest = destination;
                while(destination != xy)
                {
                    if(paths.find(destination) == paths.end())
                        break;
                    rpaths.insert(std::make_pair(paths[destination], destination));
                    destination = paths[destination];
                }

                int i = GRID_TOTAL;
                destination = xy;
                prev = destination;
                while(destination != dest)
                {
                    destination = rpaths[destination];
                    if(destination % GRID_WIDTH == prev % GRID_WIDTH)
                    {
                        destDir = Direction::VERTICAL;
                    }
                    else
                    {
                        destDir = Direction::HORIZONTAL;
                    }

                    if(prevDir == Direction::NONE)
                        prevDir = destDir;
                    else if(prevDir != destDir)
                        break;
                    prev = destination;

                    if(i-- == 0)
                        break;
                }
                if(prevDir == Direction::NONE)
                    prevDir = destDir;
                if(destination == prevDest)
                    prevDest = -1;
            }
            else
                prevDir = Direction::PLUS;

            if(prevDir == Direction::VERTICAL)
            {
                if(prev < xy)
                {
                    vel->x = 0.0f;
                    vel->y = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    control->timer = (float)((xy / GRID_WIDTH - prev / GRID_WIDTH) * GRID_SQUARE_SIZE) / -vel->y;
                }
                else
                {
                    vel->x = 0.0f;
                    vel->y = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    if(prev == xy)
                        control->timer = 0.0f;
                    else
                        control->timer = (float)((prev / GRID_WIDTH - xy / GRID_WIDTH) * GRID_SQUARE_SIZE) / vel->y;
                }
            }
            else if(prevDir == Direction::HORIZONTAL)
            {
                if(prev < xy)
                {
                    vel->x = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (float)((xy - prev) * GRID_SQUARE_SIZE) / -vel->x;
                }
                else
                {
                    vel->x = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    if(prev == xy)
                        control->timer = 0.0f;
                    else
                        control->timer = (float)((prev - xy) * GRID_SQUARE_SIZE) / vel->x;
                }
            }
            else if(prevDir == Direction::PLUS)
            {
                float offsetx = (pos->x - (float)GRID_OFFSET_X) / (float)GRID_SQUARE_SIZE;
                float offsety = (pos->y - (float)GRID_OFFSET_Y) / (float)GRID_SQUARE_SIZE;
                float x = xy % GRID_WIDTH;
                float y = xy / GRID_WIDTH;
                if(offsetx < x)
                {
                    vel->x = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (pos->x - offsetx) / -vel->x;
                }
                else if(offsetx > x)
                {
                    vel->x = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    vel->y = 0.0f;
                    control->timer = (offsetx - pos->x) / vel->x;
                }
                else if(offsety < y)
                {
                    vel->x = 0.0f;
                    vel->y = -DEFAULT_SPEED - SPEED_UP_MULT * (float)living->speedUp;
                    control->timer = (pos->y - offsety) / -vel->y;
                }
                else if(offsety >= y)
                {
                    vel->x = 0.0f;
                    vel->y = DEFAULT_SPEED + SPEED_UP_MULT * (float)living->speedUp;
                    if(offsety != y)
                        control->timer = (offsety - pos->y) / vel->y;
                    else
                        control->timer = 0.0f;
                }
            }
            else
            {
                std::cerr << "ERROR: Failed to validate next tile on move\n";
                std::clog << "prev is " << prev << '\n';
                control->timer = 0.0f;
            }
        }
    }

    HitInfo powerinfo = Utility::collideAgainstComponent(pos->x, pos->y, std::type_index(typeid(cPickup)), *context.ecEngine);
    for(auto piter = powerinfo.hit.begin(); piter != powerinfo.hit.end(); ++piter)
    {
        cPowerup* powerup = static_cast<cPowerup*>((*piter)->getComponent(std::type_index(typeid(cPowerup))));

        switch(powerup->powerup)
        {
        case cPowerup::BALLOON_UP:
            ++(living->balloonUp);
            break;
        case cPowerup::RANGE_UP:
            ++(living->rangeUp);
            break;
        case cPowerup::SPEED_UP:
            ++(living->speedUp);
            break;
        case cPowerup::KICK_UPGRADE:
            ++(living->kickUpgrade);
            break;
        case cPowerup::RCONTROL_UPGRADE:
            ++(living->rControlUpgrade);
            break;
        case cPowerup::SBALLOON_UPGRADE:
            ++(living->sBalloonUpgrade);
            break;
        case cPowerup::PIERCE_UPGRADE:
            ++(living->pierceUpgrade);
            break;
        case cPowerup::SPREAD_UPGRADE:
            ++(living->spreadUpgrade);
            break;
        case cPowerup::GHOST_UPGRADE:
            ++(living->ghostUpgrade);
            break;
        default:
            break;
        }

        context.ecEngine->removeEntity((*piter)->getID());
    }

    if(living->rControlUpgrade > 0)
    {
        bool inDanger = Utility::collidesAgainstComponent(pos->x, pos->y, std::type_index(typeid(cWIndicator)), *context.ecEngine);
        if(justPlacedBalloon || inDanger)
        {
            control->fired = false;
        }
        else if(!justPlacedBalloon && !inDanger)
        {
            control->fired = true;
        }
    }
}
<|endoftext|>


<commit_before>#include "scanner.h"

#include <algorithm>

#include "exp.h"
#include "stream.h"
#include "yaml-cpp/exceptions.h"  // IWYU pragma: keep

namespace YAML {

int Scanner::MatchScalarEmpty(Exp::Source<4>) {
  // This is checked by !INPUT as well
  return -1;
}

int Scanner::MatchScalarSingleQuoted(Exp::Source<4> in) {
  using namespace Exp;
  return (Matcher<Char<'\''>>::Matches(in) &&
          !EscSingleQuote::Matches(in)) ? 1 : -1;
}

int Scanner::MatchScalarDoubleQuoted(Exp::Source<4> in) {
  using namespace Exp;
  return Matcher<Char<'\"'>>::Match(in);
}

int Scanner::MatchScalarEnd(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEnd = Matcher<
      OR < SEQ < Char<':'>,
                 OR < detail::BlankOrBreak, Empty>>,
           SEQ < detail::BlankOrBreak,
                 detail::Comment>>>;

  return ScalarEnd::Match(in);
}

int Scanner::MatchScalarEndInFlow(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEndInFlow = Matcher <
      OR < SEQ < Char<':'>,
                 OR < detail::Blank,
                      Char<','>,
                      Char<']'>,
                      Char<'}'>,
                      detail::Break,
                      Empty >>,
           Char<','>,
           Char<'?'>,
           Char<'['>,
           Char<']'>,
           Char<'{'>,
           Char<'}'>,
           SEQ < detail::BlankOrBreak,
                 detail::Comment>>>;

   return ScalarEndInFlow::Match(in);
}

int Scanner::MatchScalarIndent(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEndInFlow = Matcher <
      SEQ < detail::Blank,
            detail::Comment>>;

  return ScalarEndInFlow::Match(in);
}

static bool MatchDocIndicator(const Stream& in) {
 using namespace Exp;
 using DocIndicator = Matcher<OR <detail::DocStart, detail::DocEnd>>;

 return DocIndicator::Matches(in);
}

struct ScanResult {
  bool foundNonEmptyLine = false;
  bool escapedNewline = false;
  int endMatch = -1;
  std::size_t lastNonWhitespaceChar;
  std::size_t lastEscapedChar = std::string::npos;
};

static void EatToIndentation(Stream& INPUT, ScanScalarParams& params, bool foundEmptyLine);

static void EatAfterIndentation(Stream& INPUT, ScanScalarParams& params);

static void PostProcess(std::string& scalar, ScanScalarParams& params, size_t lastEscapedChar);

static int HandleFolding(std::string& scalar, const ScanScalarParams& params,
                         int column, bool escapedNewline,
                         bool emptyLine, bool moreIndented,
                         bool nextEmptyLine, bool nextMoreIndented,
                         bool foundNonEmptyLine,
                         bool foldedNewlineStartedMoreIndented,
                         int foldedNewlineCount);

static void ScanLine(Stream& INPUT, const ScanScalarParams& params,
                     std::string& scalar, ScanResult& out);
//#define TEST_NO_INLINE __attribute__((noinline))
#define TEST_NO_INLINE

// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
//   1. Scan until newline
//   2. Eat newline
//   3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
//   and different places in the above flow.
std::string Scanner::ScanScalar(ScanScalarParams& params) {

  bool emptyLine = false;
  bool moreIndented = false;
  bool foldedNewlineStartedMoreIndented = false;
  bool pastOpeningBreak = (params.fold == FOLD_FLOW);

  int foldedNewlineCount = 0;
  std::string scalar;

  ScanResult r;

  params.leadingSpaces = false;

  while (INPUT) {
    // ********************************
    // Phase #1: scan until line ending

    ScanLine(INPUT, params, scalar, r);
    pastOpeningBreak |= r.foundNonEmptyLine;

    // eof? if we're looking to eat something, then we throw
    if (!INPUT) {
      if (params.eatEnd) {
        throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR);
      }
      break;
    }

    // doc indicator?
    if (params.onDocIndicator == BREAK && INPUT.column() == 0) {
      if (MatchDocIndicator(INPUT)) {
        break;
      }
    }

    // are we done via character match?
    if (r.endMatch >= 0) {
      if (params.eatEnd) {
        INPUT.eat(r.endMatch);
      }
      break;
    }

    // do we remove trailing whitespace?
    if (params.fold == FOLD_FLOW) {
      if (r.lastNonWhitespaceChar < scalar.size()) {
        scalar.erase(r.lastNonWhitespaceChar);
      }
    }
    // ********************************
    // Phase #2: eat line ending
    assert(INPUT.EatLineBreak());

    // ********************************
    // Phase #3: scan initial spaces

    EatToIndentation(INPUT, params, !r.foundNonEmptyLine);

    // update indent if we're auto-detecting
    if (params.detectIndent && !r.foundNonEmptyLine) {
      params.indent = std::max(params.indent, INPUT.column());
    }

    // and then the rest of the whitespace
    if (INPUT.peek() == ' ' || INPUT.peek() == '\t') {
      EatAfterIndentation(INPUT, params);
    }

    // was this an empty line?
    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);
    bool nextEmptyLine = Exp::Break::Matches(input);
    bool nextMoreIndented = Exp::Blank::Matches(input);
    if (params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine)
      foldedNewlineStartedMoreIndented = moreIndented;

    if (pastOpeningBreak) {
      foldedNewlineCount = HandleFolding(scalar, params, INPUT.column(),
                                         r.escapedNewline,
                                         emptyLine, moreIndented,
                                         nextEmptyLine, nextMoreIndented,
                                         r.foundNonEmptyLine,
                                         foldedNewlineStartedMoreIndented,
                                         foldedNewlineCount);
    }

    emptyLine = nextEmptyLine;
    moreIndented = nextMoreIndented;
    pastOpeningBreak = true;

    // are we done via indentation?
    if (!emptyLine && INPUT.column() < params.indent) {
      params.leadingSpaces = true;
      break;
    }
  }

  PostProcess(scalar, params, r.lastEscapedChar);

  return scalar;
}

TEST_NO_INLINE
static void ScanLine(Stream& INPUT, const ScanScalarParams& params,
                           std::string& scalar, ScanResult& out) {

  const size_t bufferSize = 256;
  char buffer[bufferSize];
  size_t bufferFill = 0;
  size_t scalarLength = scalar.length();

  out.lastNonWhitespaceChar = scalarLength;
  out.escapedNewline = false;

  while (INPUT) {

    // find break posiion
    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);

    // space 0x20 : 0010000ß
    // tab   0x09 : 00001001
    // LF    0x0A : 00001010
    // CR    0x0D : 00001101
    bool isWhiteSpace = false;

    if ((input[0] & 0x28) != 0) {
      isWhiteSpace = Exp::Blank::Matches(input);
      if (!isWhiteSpace) {
        if (Exp::Break::Matches(input)) {
          break;
        }
        // document indicator?
        if (unlikely(INPUT.column() == 0) &&
            MatchDocIndicator(INPUT)) {

          if (params.onDocIndicator == BREAK) {
            break;
          } else if (params.onDocIndicator == THROW) {
            throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
          }
        }
      }
    }

    // keep end posiion
    if ((out.endMatch = params.end(input)) >= 0) {
      break;
    }

    out.foundNonEmptyLine = true;

    if (likely(params.escape != input[0])) {
      // just add the character
      if (unlikely(bufferFill == bufferSize)) {
        scalar.insert(scalar.size(), buffer, bufferSize);
        bufferFill = 0;
      }

      buffer[bufferFill++] = input[0];

      scalarLength++;

      INPUT.eat();

      if (!isWhiteSpace) {
        out.lastNonWhitespaceChar = scalarLength;
      }

    } else {
      // escaped newline? (only if we're escaping on slash)
      if (params.escape == '\\' && Exp::EscBreak::Matches(input)) {
        // eat escape character and get out (but preserve trailing whitespace!)
        INPUT.eat();
        out.lastEscapedChar = out.lastNonWhitespaceChar = scalarLength;
        out.escapedNewline = true;
        break;

      } else {
        if (bufferFill > 0) {
          scalar.insert(scalar.size(), buffer, bufferFill);
          bufferFill = 0;
        }

        scalar += Exp::Escape(INPUT);
        scalarLength = scalar.size();

        out.lastEscapedChar = out.lastNonWhitespaceChar = scalarLength;
      }
    }
  }

  if (bufferFill > 0) {
    scalar.insert(scalar.size(), buffer, bufferFill);
  }
}

TEST_NO_INLINE
static void EatToIndentation(Stream& INPUT, ScanScalarParams& params, bool foundEmptyLine) {

  using namespace Exp;

  using _ = Char<' '>;

  using SpaceInvaders = Matcher<Count<_,_,_,_,_,_,_,_>>;

  // first the required indentation
  // This can be:
  // - BlockScalar (detectIndent/colum<indent)
  // - PlainScalar (colum<indent) /
  int max = params.indent - INPUT.column();
  if (params.detectIndent && foundEmptyLine) {
    max = std::numeric_limits<int>::max();
  }

  // Don't eat the whitespace before comments
  while (max > 0) {

    auto input = INPUT.GetLookaheadBuffer(8);

    int pos = SpaceInvaders::Match(input);

    // No, nothing to eat!
    if (pos == 0) { break; }

    // Pos can be up to 8. Dont eat before potential comment.
    if (params.indentFn && (pos == 8 || input[pos] == '#')) {
      pos -= 1;
    }
    if (max < pos) { pos = max; }

    // Eat spaces
    for (int i = 0; i < pos; i++) {
      INPUT.eat();
    }

    if (pos < 7 || input[7] != ' ') {
      break;
    }
    max -= pos;
  }
}

TEST_NO_INLINE
static void EatAfterIndentation(Stream& INPUT, ScanScalarParams& params) {

  for (char c = INPUT.peek(); (c == ' ' || c == '\t'); c = INPUT.peek()) {
    // we check for tabs that masquerade as indentation
    if (c == '\t' && INPUT.column() < params.indent &&
        params.onTabInIndentation == THROW) {
      throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);
    }

    if (!params.eatLeadingWhitespace) {
      break;
    }
    // FIXME: 2
    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);
    if (params.indentFn && params.indentFn(input) >= 0) {
      break;
    }
    //printf("check 2\n");

    INPUT.eat();
  }
}

TEST_NO_INLINE
static int HandleFolding(std::string& scalar,
                         const ScanScalarParams& params,
                         int column, bool escapedNewline,
                         bool emptyLine, bool moreIndented,
                         bool nextEmptyLine, bool nextMoreIndented,
                         bool foundNonEmptyLine,
                         bool foldedNewlineStartedMoreIndented,
                         int foldedNewlineCount) {

  // for block scalars, we always start with a newline, so we should ignore it
  // (not fold or keep)
  switch (params.fold) {
    case DONT_FOLD:
      scalar += '\n';
      break;
    case FOLD_BLOCK:
      if (!emptyLine && !nextEmptyLine && !moreIndented &&
          !nextMoreIndented && column >= params.indent) {
        scalar += ' ';
      } else if (nextEmptyLine) {
        foldedNewlineCount++;
      } else {
        scalar += '\n';
      }

      if (!nextEmptyLine && foldedNewlineCount > 0) {
        scalar += std::string(foldedNewlineCount - 1, '\n');
        if (foldedNewlineStartedMoreIndented ||
            nextMoreIndented | !foundNonEmptyLine) {
          scalar += '\n';
        }
        foldedNewlineCount = 0;
      }
      break;
    case FOLD_FLOW:
      if (nextEmptyLine) {
        scalar += '\n';
      } else if (!emptyLine && !nextEmptyLine && !escapedNewline) {
        scalar += ' ';
      }
      break;
  }
  return foldedNewlineCount;
}

TEST_NO_INLINE
static void PostProcess(std::string& scalar, ScanScalarParams& params, size_t lastEscapedChar) {
  // post-processing
  if (params.trimTrailingSpaces) {
    std::size_t pos = scalar.size()-1;
    while (pos != std::string::npos && scalar[pos] == ' ') { pos--; }
    //std::size_t pos = scalar.find_last_not_of(' ');

    if (lastEscapedChar != std::string::npos) {
      if (pos < lastEscapedChar || pos == std::string::npos) {
        pos = lastEscapedChar;
      }
    }
    if (pos < scalar.size()) {
      scalar.erase(pos + 1);
    }
  }

  switch (params.chomp) {
    case CLIP: {
      std::size_t pos = scalar.find_last_not_of('\n');
      if (lastEscapedChar != std::string::npos) {
        if (pos < lastEscapedChar || pos == std::string::npos) {
          pos = lastEscapedChar;
        }
      }
      if (pos == std::string::npos) {
        scalar.erase();
      } else if (pos + 1 < scalar.size()) {
        scalar.erase(pos + 2);
      }
    } break;
    case STRIP: {
      std::size_t pos = scalar.find_last_not_of('\n');
      if (lastEscapedChar != std::string::npos) {
        if (pos < lastEscapedChar || pos == std::string::npos) {
          pos = lastEscapedChar;
        }
      }
      if (pos == std::string::npos) {
        scalar.erase();
      } else if (pos < scalar.size()) {
        scalar.erase(pos + 1);
      }
    } break;
    default:
      break;
  }
}

}
<commit_msg>cleanup ScanScalar<commit_after>#include "scanner.h"

#include <algorithm>

#include "exp.h"
#include "stream.h"
#include "yaml-cpp/exceptions.h"  // IWYU pragma: keep

namespace YAML {

int Scanner::MatchScalarEmpty(Exp::Source<4>) {
  // This is checked by !INPUT as well
  return -1;
}

int Scanner::MatchScalarSingleQuoted(Exp::Source<4> in) {
  using namespace Exp;
  return (Matcher<Char<'\''>>::Matches(in) &&
          !EscSingleQuote::Matches(in)) ? 1 : -1;
}

int Scanner::MatchScalarDoubleQuoted(Exp::Source<4> in) {
  using namespace Exp;
  return Matcher<Char<'\"'>>::Match(in);
}

int Scanner::MatchScalarEnd(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEnd = Matcher<
      OR < SEQ < Char<':'>,
                 OR < detail::BlankOrBreak, Empty>>,
           SEQ < detail::BlankOrBreak,
                 detail::Comment>>>;

  return ScalarEnd::Match(in);
}

int Scanner::MatchScalarEndInFlow(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEndInFlow = Matcher <
      OR < SEQ < Char<':'>,
                 OR < detail::Blank,
                      Char<','>,
                      Char<']'>,
                      Char<'}'>,
                      detail::Break,
                      Empty >>,
           Char<','>,
           Char<'?'>,
           Char<'['>,
           Char<']'>,
           Char<'{'>,
           Char<'}'>,
           SEQ < detail::BlankOrBreak,
                 detail::Comment>>>;

   return ScalarEndInFlow::Match(in);
}

int Scanner::MatchScalarIndent(Exp::Source<4> in) {
  using namespace Exp;
  using ScalarEndInFlow = Matcher <
      SEQ < detail::Blank,
            detail::Comment>>;

  return ScalarEndInFlow::Match(in);
}

static bool MatchDocIndicator(const Stream& in) {
 using namespace Exp;
 using DocIndicator = Matcher<OR <detail::DocStart, detail::DocEnd>>;

 return DocIndicator::Matches(in);
}

struct ScanResult {
  bool foundNonEmptyLine = false;
  bool escapedNewline = false;
  int endMatch = -1;
  std::size_t lastNonWhitespaceChar;
  std::size_t lastEscapedChar = std::string::npos;
};

static void EatToIndentation(Stream& INPUT, ScanScalarParams& params, bool foundEmptyLine);

static void EatAfterIndentation(Stream& INPUT, ScanScalarParams& params);

static void PostProcess(std::string& scalar, ScanScalarParams& params, size_t lastEscapedChar);

static int HandleFolding(std::string& scalar, const ScanScalarParams& params,
                         int column, bool escapedNewline,
                         bool emptyLine, bool moreIndented,
                         bool nextEmptyLine, bool nextMoreIndented,
                         bool foundNonEmptyLine,
                         bool foldedNewlineStartedMoreIndented,
                         int foldedNewlineCount);

static void ScanLine(Stream& INPUT, const ScanScalarParams& params,
                     std::string& scalar, ScanResult& out);
//#define TEST_NO_INLINE __attribute__((noinline))
#define TEST_NO_INLINE

// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
//   1. Scan until newline
//   2. Eat newline
//   3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
//   and different places in the above flow.
std::string Scanner::ScanScalar(ScanScalarParams& params) {

  bool emptyLine = false;
  bool moreIndented = false;
  bool foldedNewlineStartedMoreIndented = false;
  bool pastOpeningBreak = (params.fold == FOLD_FLOW);

  int foldedNewlineCount = 0;
  std::string scalar;

  ScanResult r;

  params.leadingSpaces = false;

  while (INPUT) {
    // ********************************
    // Phase #1: scan until line ending

    ScanLine(INPUT, params, scalar, r);
    pastOpeningBreak |= r.foundNonEmptyLine;

    // eof? if we're looking to eat something, then we throw
    if (!INPUT) {
      if (params.eatEnd) {
        throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR);
      }
      break;
    }

    // doc indicator?
    if (params.onDocIndicator == BREAK && INPUT.column() == 0) {
      if (MatchDocIndicator(INPUT)) {
        break;
      }
    }

    // are we done via character match?
    if (r.endMatch >= 0) {
      if (params.eatEnd) {
        INPUT.eat(r.endMatch);
      }
      break;
    }

    // do we remove trailing whitespace?
    if (params.fold == FOLD_FLOW) {
      if (r.lastNonWhitespaceChar < scalar.size()) {
        scalar.erase(r.lastNonWhitespaceChar);
      }
    }
    // ********************************
    // Phase #2: eat line ending
    assert(INPUT.EatLineBreak());

    // ********************************
    // Phase #3: scan initial spaces

    EatToIndentation(INPUT, params, !r.foundNonEmptyLine);

    // update indent if we're auto-detecting
    if (params.detectIndent && !r.foundNonEmptyLine) {
      params.indent = std::max(params.indent, INPUT.column());
    }

    // and then the rest of the whitespace
    if (INPUT.peek() == ' ' || INPUT.peek() == '\t') {
      EatAfterIndentation(INPUT, params);
    }

    // was this an empty line?
    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);
    bool nextEmptyLine = Exp::Break::Matches(input);
    bool nextMoreIndented = Exp::Blank::Matches(input);
    if (params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine)
      foldedNewlineStartedMoreIndented = moreIndented;

    if (pastOpeningBreak) {
      foldedNewlineCount = HandleFolding(scalar, params, INPUT.column(),
                                         r.escapedNewline,
                                         emptyLine, moreIndented,
                                         nextEmptyLine, nextMoreIndented,
                                         r.foundNonEmptyLine,
                                         foldedNewlineStartedMoreIndented,
                                         foldedNewlineCount);
    }

    emptyLine = nextEmptyLine;
    moreIndented = nextMoreIndented;
    pastOpeningBreak = true;

    // are we done via indentation?
    if (!emptyLine && INPUT.column() < params.indent) {
      params.leadingSpaces = true;
      break;
    }
  }

  PostProcess(scalar, params, r.lastEscapedChar);

  return scalar;
}

TEST_NO_INLINE
static void ScanLine(Stream& INPUT, const ScanScalarParams& params,
                           std::string& scalar, ScanResult& out) {

  const size_t bufferSize = 256;
  char buffer[bufferSize];
  size_t bufferFill = 0;
  size_t scalarLength = scalar.length();

  out.lastNonWhitespaceChar = scalarLength;
  out.escapedNewline = false;

  while (INPUT) {

    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);

    bool isWhiteSpace = Exp::Blank::Matches(input);

    if (!isWhiteSpace) {
      if (Exp::Break::Matches(input)) { break; }

      // document indicator?
      if (unlikely(INPUT.column() == 0) &&
          MatchDocIndicator(INPUT)) {
        if (params.onDocIndicator == BREAK) {
          break;
        } else if (params.onDocIndicator == THROW) {
          throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
        }
      }
    }

    // keep end posiion
    if ((out.endMatch = params.end(input)) >= 0) {
      break;
    }

    out.foundNonEmptyLine = true;

    if (likely(params.escape != input[0])) {
      // just add the character
      if (unlikely(bufferFill == bufferSize)) {
        scalar.insert(scalar.size(), buffer, bufferSize);
        bufferFill = 0;
      }

      buffer[bufferFill++] = input[0];

      scalarLength++;

      INPUT.eat();

      if (!isWhiteSpace) {
        out.lastNonWhitespaceChar = scalarLength;
      }

    } else {
      // escaped newline? (only if we're escaping on slash)
      if (params.escape == '\\' && Exp::EscBreak::Matches(input)) {
        // eat escape character and get out (but preserve trailing whitespace!)
        INPUT.eat();
        out.lastEscapedChar = out.lastNonWhitespaceChar = scalarLength;
        out.escapedNewline = true;
        break;

      } else {
        if (bufferFill > 0) {
          scalar.insert(scalar.size(), buffer, bufferFill);
          bufferFill = 0;
        }

        scalar += Exp::Escape(INPUT);
        scalarLength = scalar.size();

        out.lastEscapedChar = out.lastNonWhitespaceChar = scalarLength;
      }
    }
  }

  if (bufferFill > 0) {
    scalar.insert(scalar.size(), buffer, bufferFill);
  }
}

TEST_NO_INLINE
static void EatToIndentation(Stream& INPUT, ScanScalarParams& params, bool foundEmptyLine) {

  using namespace Exp;

  using _ = Char<' '>;

  using SpaceInvaders = Matcher<Count<_,_,_,_,_,_,_,_>>;

  // first the required indentation
  // This can be:
  // - BlockScalar (detectIndent/colum<indent)
  // - PlainScalar (colum<indent) /
  int max = params.indent - INPUT.column();
  if (params.detectIndent && foundEmptyLine) {
    max = std::numeric_limits<int>::max();
  }

  // Don't eat the whitespace before comments
  while (max > 0) {

    auto input = INPUT.GetLookaheadBuffer(8);

    int pos = SpaceInvaders::Match(input);

    // No, nothing to eat!
    if (pos == 0) { break; }

    // Pos can be up to 8. Dont eat before potential comment.
    if (params.indentFn && (pos == 8 || input[pos] == '#')) {
      pos -= 1;
    }
    if (max < pos) { pos = max; }

    // Eat spaces
    for (int i = 0; i < pos; i++) {
      INPUT.eat();
    }

    if (pos < 7 || input[7] != ' ') {
      break;
    }
    max -= pos;
  }
}

TEST_NO_INLINE
static void EatAfterIndentation(Stream& INPUT, ScanScalarParams& params) {

  for (char c = INPUT.peek(); (c == ' ' || c == '\t'); c = INPUT.peek()) {
    // we check for tabs that masquerade as indentation
    if (c == '\t' && INPUT.column() < params.indent &&
        params.onTabInIndentation == THROW) {
      throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);
    }

    if (!params.eatLeadingWhitespace) {
      break;
    }
    // FIXME: 2
    Exp::Source<4> input;
    INPUT.LookaheadBuffer(input);
    if (params.indentFn && params.indentFn(input) >= 0) {
      break;
    }
    //printf("check 2\n");

    INPUT.eat();
  }
}

TEST_NO_INLINE
static int HandleFolding(std::string& scalar,
                         const ScanScalarParams& params,
                         int column, bool escapedNewline,
                         bool emptyLine, bool moreIndented,
                         bool nextEmptyLine, bool nextMoreIndented,
                         bool foundNonEmptyLine,
                         bool foldedNewlineStartedMoreIndented,
                         int foldedNewlineCount) {

  // for block scalars, we always start with a newline, so we should ignore it
  // (not fold or keep)
  switch (params.fold) {
    case DONT_FOLD:
      scalar += '\n';
      break;
    case FOLD_BLOCK:
      if (!emptyLine && !nextEmptyLine && !moreIndented &&
          !nextMoreIndented && column >= params.indent) {
        scalar += ' ';
      } else if (nextEmptyLine) {
        foldedNewlineCount++;
      } else {
        scalar += '\n';
      }

      if (!nextEmptyLine && foldedNewlineCount > 0) {
        scalar += std::string(foldedNewlineCount - 1, '\n');
        if (foldedNewlineStartedMoreIndented ||
            nextMoreIndented | !foundNonEmptyLine) {
          scalar += '\n';
        }
        foldedNewlineCount = 0;
      }
      break;
    case FOLD_FLOW:
      if (nextEmptyLine) {
        scalar += '\n';
      } else if (!emptyLine && !nextEmptyLine && !escapedNewline) {
        scalar += ' ';
      }
      break;
  }
  return foldedNewlineCount;
}

TEST_NO_INLINE
static void PostProcess(std::string& scalar, ScanScalarParams& params, size_t lastEscapedChar) {
  // post-processing
  if (params.trimTrailingSpaces) {
    std::size_t pos = scalar.size()-1;
    while (pos != std::string::npos && scalar[pos] == ' ') { pos--; }
    //std::size_t pos = scalar.find_last_not_of(' ');

    if (lastEscapedChar != std::string::npos) {
      if (pos < lastEscapedChar || pos == std::string::npos) {
        pos = lastEscapedChar;
      }
    }
    if (pos < scalar.size()) {
      scalar.erase(pos + 1);
    }
  }

  switch (params.chomp) {
    case CLIP: {
      std::size_t pos = scalar.find_last_not_of('\n');
      if (lastEscapedChar != std::string::npos) {
        if (pos < lastEscapedChar || pos == std::string::npos) {
          pos = lastEscapedChar;
        }
      }
      if (pos == std::string::npos) {
        scalar.erase();
      } else if (pos + 1 < scalar.size()) {
        scalar.erase(pos + 2);
      }
    } break;
    case STRIP: {
      std::size_t pos = scalar.find_last_not_of('\n');
      if (lastEscapedChar != std::string::npos) {
        if (pos < lastEscapedChar || pos == std::string::npos) {
          pos = lastEscapedChar;
        }
      }
      if (pos == std::string::npos) {
        scalar.erase();
      } else if (pos < scalar.size()) {
        scalar.erase(pos + 1);
      }
    } break;
    default:
      break;
  }
}

}
<|endoftext|>


<commit_before>#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"

namespace YAML
{
	// ScanScalar
	// . This is where the scalar magic happens.
	//
	// . We do the scanning in three phases:
	//   1. Scan until newline
	//   2. Eat newline
	//   3. Scan leading blanks.
	//
	// . Depending on the parameters given, we store or stop
	//   and different places in the above flow.
	std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
	{
		bool foundNonEmptyLine = false, pastOpeningBreak = false;
		bool emptyLine = false, moreIndented = false;
		std::string scalar;
		params.leadingSpaces = false;

		while(INPUT) {
			// ********************************
			// Phase #1: scan until line ending
			while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
				if(!INPUT)
					break;

				// document indicator?
				if(INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT)) {
					if(params.onDocIndicator == BREAK)
						break;
					else if(params.onDocIndicator == THROW)
						throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
				}

				foundNonEmptyLine = true;
				pastOpeningBreak = true;

				// escaped newline? (only if we're escaping on slash)
				if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
					int n = Exp::EscBreak.Match(INPUT);
					INPUT.eat(n);
					continue;
				}

				// escape this?
				if(INPUT.peek() == params.escape) {
					scalar += Exp::Escape(INPUT);
					continue;
				}

				// otherwise, just add the damn character
				scalar += INPUT.get();
			}

			// eof? if we're looking to eat something, then we throw
			if(!INPUT) {
				if(params.eatEnd)
					throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR);
				break;
			}

			// doc indicator?
			if(params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT))
				break;

			// are we done via character match?
			int n = params.end.Match(INPUT);
			if(n >= 0) {
				if(params.eatEnd)
					INPUT.eat(n);
				break;
			}

			// ********************************
			// Phase #2: eat line ending
			n = Exp::Break.Match(INPUT);
			INPUT.eat(n);

			// ********************************
			// Phase #3: scan initial spaces

			// first the required indentation
			while(INPUT.peek() == ' ' && (INPUT.column() < params.indent || (params.detectIndent && !foundNonEmptyLine)))
				INPUT.eat(1);

			// update indent if we're auto-detecting
			if(params.detectIndent && !foundNonEmptyLine)
				params.indent = std::max(params.indent, INPUT.column());

			// and then the rest of the whitespace
			while(Exp::Blank.Matches(INPUT)) {
				// we check for tabs that masquerade as indentation
				if(INPUT.peek() == '\t'&& INPUT.column() < params.indent && params.onTabInIndentation == THROW)
					throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);

				if(!params.eatLeadingWhitespace)
					break;

				INPUT.eat(1);
			}

			// was this an empty line?
			bool nextEmptyLine = Exp::Break.Matches(INPUT);
			bool nextMoreIndented = (INPUT.peek() == ' ');

			// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
			if(pastOpeningBreak) {
				if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented && INPUT.column() >= params.indent)
					scalar += " ";
				else
					scalar += "\n";
			}

			emptyLine = nextEmptyLine;
			moreIndented = nextMoreIndented;
			pastOpeningBreak = true;

			// are we done via indentation?
			if(!emptyLine && INPUT.column() < params.indent) {
				params.leadingSpaces = true;
				break;
			}
		}

		// post-processing
		if(params.trimTrailingSpaces) {
			std::size_t pos = scalar.find_last_not_of(' ');
			if(pos < scalar.size())
				scalar.erase(pos + 1);
		}

		if(params.chomp == STRIP || params.chomp == CLIP) {
			std::size_t pos = scalar.find_last_not_of('\n');
			if(params.chomp == CLIP && pos + 1 < scalar.size())
				scalar.erase(pos + 2);
			else if(params.chomp == STRIP && pos < scalar.size())
				scalar.erase(pos + 1);
		}

		return scalar;
	}
}
<commit_msg>Fixed newlines in folded scalars bug<commit_after>#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"

namespace YAML
{
	// ScanScalar
	// . This is where the scalar magic happens.
	//
	// . We do the scanning in three phases:
	//   1. Scan until newline
	//   2. Eat newline
	//   3. Scan leading blanks.
	//
	// . Depending on the parameters given, we store or stop
	//   and different places in the above flow.
	std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
	{
		bool foundNonEmptyLine = false, pastOpeningBreak = false;
		bool emptyLine = false, moreIndented = false;
		int foldedNewlineCount = 0;
		bool foldedNewlineStartedMoreIndented = false;
		std::string scalar;
		params.leadingSpaces = false;

		while(INPUT) {
			// ********************************
			// Phase #1: scan until line ending
			while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
				if(!INPUT)
					break;

				// document indicator?
				if(INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT)) {
					if(params.onDocIndicator == BREAK)
						break;
					else if(params.onDocIndicator == THROW)
						throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
				}

				foundNonEmptyLine = true;
				pastOpeningBreak = true;

				// escaped newline? (only if we're escaping on slash)
				if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
					int n = Exp::EscBreak.Match(INPUT);
					INPUT.eat(n);
					continue;
				}

				// escape this?
				if(INPUT.peek() == params.escape) {
					scalar += Exp::Escape(INPUT);
					continue;
				}

				// otherwise, just add the damn character
				scalar += INPUT.get();
			}

			// eof? if we're looking to eat something, then we throw
			if(!INPUT) {
				if(params.eatEnd)
					throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR);
				break;
			}

			// doc indicator?
			if(params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT))
				break;

			// are we done via character match?
			int n = params.end.Match(INPUT);
			if(n >= 0) {
				if(params.eatEnd)
					INPUT.eat(n);
				break;
			}

			// ********************************
			// Phase #2: eat line ending
			n = Exp::Break.Match(INPUT);
			INPUT.eat(n);

			// ********************************
			// Phase #3: scan initial spaces

			// first the required indentation
			while(INPUT.peek() == ' ' && (INPUT.column() < params.indent || (params.detectIndent && !foundNonEmptyLine)))
				INPUT.eat(1);

			// update indent if we're auto-detecting
			if(params.detectIndent && !foundNonEmptyLine)
				params.indent = std::max(params.indent, INPUT.column());

			// and then the rest of the whitespace
			while(Exp::Blank.Matches(INPUT)) {
				// we check for tabs that masquerade as indentation
				if(INPUT.peek() == '\t'&& INPUT.column() < params.indent && params.onTabInIndentation == THROW)
					throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);

				if(!params.eatLeadingWhitespace)
					break;

				INPUT.eat(1);
			}

			// was this an empty line?
			bool nextEmptyLine = Exp::Break.Matches(INPUT);
			bool nextMoreIndented = (INPUT.peek() == ' ');
			if(params.fold && foldedNewlineCount == 0 && nextEmptyLine)
				foldedNewlineStartedMoreIndented = moreIndented;

			// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
			if(pastOpeningBreak) {
				if(params.fold) {
					if(!emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented && INPUT.column() >= params.indent)
						scalar += " ";
					else if(nextEmptyLine)
						foldedNewlineCount++;
					else
						scalar += "\n";

					if(!nextEmptyLine && foldedNewlineCount > 0) {
						if(foldedNewlineStartedMoreIndented || nextMoreIndented)
							scalar += std::string("\n", foldedNewlineCount);
						foldedNewlineCount = 0;
					}
				} else {
					scalar += "\n";
				}
			}

			emptyLine = nextEmptyLine;
			moreIndented = nextMoreIndented;
			pastOpeningBreak = true;

			// are we done via indentation?
			if(!emptyLine && INPUT.column() < params.indent) {
				params.leadingSpaces = true;
				break;
			}
		}

		// post-processing
		if(params.trimTrailingSpaces) {
			std::size_t pos = scalar.find_last_not_of(' ');
			if(pos < scalar.size())
				scalar.erase(pos + 1);
		}

		if(params.chomp == STRIP || params.chomp == CLIP) {
			std::size_t pos = scalar.find_last_not_of('\n');
			if(params.chomp == CLIP && pos + 1 < scalar.size())
				scalar.erase(pos + 2);
			else if(params.chomp == STRIP && pos < scalar.size())
				scalar.erase(pos + 1);
		}

		return scalar;
	}
}
<|endoftext|>