<commit_before>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

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

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

****************************************************************************/

#include "rpcconnection.h"
#include <QMetaMethod>
#include <QStringList>
#include <QVarLengthArray>
#include <QTcpServer>
#include <QTcpSocket>
#include <netinet/in.h>

#include "jsonparser/json.h"

RpcConnection::RpcConnection(RpcConnection::Mode mode, const QHostAddress &address, qint32 port, QObject *parent)
    : QObject(parent), m_mode(mode), m_server(0), m_socket(0), m_id(1)
{
    init();
    if (m_mode == Server) {
        listen(address, port);
    } else if (m_mode == Client) {
        connectToHost(address, port);
    }
}

RpcConnection::RpcConnection(RpcConnection::Mode mode, QObject *parent)
    : QObject(parent), m_mode(mode), m_server(0), m_socket(0), m_id(1)
{
    init();
}

void RpcConnection::init()
{
    if (m_mode == Client) {
        m_socket = new QTcpSocket(this);
        connect(m_socket, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
        connect(m_socket, SIGNAL(connected()), this, SIGNAL(clientConnected()));
        connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(clientDisconnected()));
    } else {
        m_server = new QTcpServer(this);
        connect(m_server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
    }
}

void RpcConnection::registerObject(QObject *object)
{
    if (object->objectName().startsWith("rpc.")) {
        qWarning() << "Method names starting with 'rpc.' are reserved by the specification";
        return;
    }
    m_objects[object->objectName()] = object;
}

void RpcConnection::connectToHost(const QHostAddress &address, quint16 port)
{
    Q_ASSERT(m_mode == Client);
    m_socket->connectToHost(address, port);
}

void RpcConnection::disconnectFromHost()
{
    Q_ASSERT(m_mode == Client);
    m_socket->disconnectFromHost();
}

bool RpcConnection::waitForConnected(int msecs)
{
    Q_ASSERT(m_mode == Client);
    return m_socket->waitForConnected(msecs);
}

bool RpcConnection::listen(const QHostAddress &address, quint16 port)
{
    Q_ASSERT(m_mode == Server);
    return m_server->listen(address, port);
}

void RpcConnection::handleNewConnection()
{
    QTcpSocket *socket = m_server->nextPendingConnection();
    connect(socket, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    qDebug() << "Connected to client";
}

void RpcConnection::sendError(QTcpSocket *socket, const QString &id, int error, const QString &message, const QString &data)
{
    QVariantMap responseMap;
    responseMap.insert("jsonrpc", "2.0");
    responseMap.insert("id", id);

    QVariantMap errorMap;
    errorMap.insert("code", error);
    errorMap.insert("message", message);
    errorMap.insert("data", data);
    responseMap.insert("error", errorMap);

    QByteArray jsonRpc = Json::stringify(QVariant(responseMap));

    struct Header { int length; } header;
    header.length = htonl(jsonRpc.length());
    socket->write((const char *)&header, sizeof(header));
    socket->write(jsonRpc);
    socket->flush();
}

void RpcConnection::handleReadyRead()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
    Q_ASSERT(socket);
    do {
        struct Header { int length; } header;
        socket->read((char *)&header, sizeof(header));
        header.length = ntohl(header.length);
        QByteArray msg = socket->read(header.length); // ## assumes we got complete message

        QString error;
        QVariantMap map = Json::parse(msg, &error).toMap();
        if (!error.isEmpty() || map["jsonrpc"].toString() != "2.0") {
            if (m_mode == Server)
                sendError(socket, map["id"].toString(), ParseError, "Malformatted JSON-RPC", error);
            qWarning() << "Malformatted JSON-RPC " << error;
            return;
        }

        if (map.contains("method")) {
            handleRpcCall(socket, map);
        } else if (map.contains("result")) {
            handleRpcResponse(map);
        } else if (map.contains("error")) {
            handleRpcError(map);
        } else {
            sendError(socket, map["id"].toString(), InvalidRequest, "Invalid request. Mot a method call, result or error");
            qWarning() << "No idea what to do with this message" << msg;
        }
    } while (socket->bytesAvailable() != 0);
}

void RpcConnection::handleRpcCall(QTcpSocket *socket, const QVariantMap &map)
{
    QVariantList list = map["params"].toList();
    QVarLengthArray<void *, 10> args(list.count()+1);
    for (int i = 0; i < list.count(); i++) {
        args[i+1] = list[i].data();
    }

    QString rpcMethod = map["method"].toString();
    int idx = rpcMethod.indexOf('.');
    QString objName = rpcMethod.mid(0, idx);
    QString method = rpcMethod.mid(idx+1);
    QObject *object = m_objects.value(objName);
    if (!object) {
        sendError(socket, map["id"].toString(), MethodNotFound, "No such object");
        qWarning() << "RPC object " << rpcMethod << " not found";
        return;
    }

    idx = object->metaObject()->indexOfMethod(method.toLatin1());
    if (idx < 0) {
        sendError(socket, map["id"].toString(), MethodNotFound, "No such method");
        qWarning() << "Object '" << objName << "' does not have method named " << method;
        return;
    }
    QMetaMethod mm = object->metaObject()->method(idx);
    if (mm.access() == QMetaMethod::Private || mm.methodType() == QMetaMethod::Signal) {
        sendError(socket, map["id"].toString(), MethodNotFound, "Method is private or is a signal");
        qWarning() << "Method " << method << " is a signal or has private access";
        return;
    }

    QVariant result;
    if (mm.typeName()) {
        result = QVariant(QVariant::nameToType(mm.typeName()), (void *)0);
        args[0] = result.data();
    } else {
        args[0] = 0;
    }

    QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx, args.data());

    sendResponse(socket, map["id"].toString(), args[0] ? *static_cast<QVariant *>(args[0]) : QVariant());
}

void RpcConnection::sendResponse(QTcpSocket *socket, const QString &id, const QVariant &result)
{
    QVariantMap responseMap;
    responseMap.insert("jsonrpc", "2.0");
    responseMap.insert("result", result);
    responseMap.insert("id", id);
    QByteArray jsonRpc = Json::stringify(QVariant(responseMap));

    struct Header { int length; } header;
    header.length = htonl(jsonRpc.length());
    socket->write((const char *)&header, sizeof(header));
    socket->write(jsonRpc);
    socket->flush();
}

void RpcConnection::handleRpcResponse(const QVariantMap &map)
{
    qDebug() << "Response" << map;
}

void RpcConnection::handleRpcError(const QVariantMap &map)
{
    qDebug() << "ERROR" << map;
}

int RpcConnection::call(const QByteArray &method, const QVariant &arg0, const QVariant &arg1,
                        const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5,
                        const QVariant &arg6, const QVariant &arg7, const QVariant &arg8, const QVariant &arg9)
{
    Q_ASSERT(m_mode == Client); // can't invoke methods in server mode since we use m_socket below
    struct Header { int length; } header;

    QVariantList params;
    params << arg0 << arg1 << arg2 << arg3 << arg4 << arg5 << arg6 << arg7 << arg8 << arg9;
    int i;
    for (i = 0; i < params.count(); i++) {
        if (params[i].isNull())
            break;
    }
    params = params.mid(0, i);

    QVariantMap map;
    map.insert("jsonrpc", "2.0");
    map.insert("method", method);
    map.insert("params", params);
    map.insert("id", m_id++);
    QByteArray jsonRpc = Json::stringify(QVariant(map));

    header.length = htonl(jsonRpc.length());
    m_socket->write((const char *)&header, sizeof(header));
    m_socket->write(jsonRpc);
    m_socket->flush();

    return m_id;
}

<commit_msg>Minor refactoring in handleRpcCall<commit_after>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

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

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

****************************************************************************/

#include "rpcconnection.h"
#include <QMetaMethod>
#include <QStringList>
#include <QVarLengthArray>
#include <QTcpServer>
#include <QTcpSocket>
#include <netinet/in.h>

#include "jsonparser/json.h"

RpcConnection::RpcConnection(RpcConnection::Mode mode, const QHostAddress &address, qint32 port, QObject *parent)
    : QObject(parent), m_mode(mode), m_server(0), m_socket(0), m_id(1)
{
    init();
    if (m_mode == Server) {
        listen(address, port);
    } else if (m_mode == Client) {
        connectToHost(address, port);
    }
}

RpcConnection::RpcConnection(RpcConnection::Mode mode, QObject *parent)
    : QObject(parent), m_mode(mode), m_server(0), m_socket(0), m_id(1)
{
    init();
}

void RpcConnection::init()
{
    if (m_mode == Client) {
        m_socket = new QTcpSocket(this);
        connect(m_socket, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
        connect(m_socket, SIGNAL(connected()), this, SIGNAL(clientConnected()));
        connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(clientDisconnected()));
    } else {
        m_server = new QTcpServer(this);
        connect(m_server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
    }
}

void RpcConnection::registerObject(QObject *object)
{
    if (object->objectName().startsWith("rpc.")) {
        qWarning() << "Method names starting with 'rpc.' are reserved by the specification";
        return;
    }
    m_objects[object->objectName()] = object;
}

void RpcConnection::connectToHost(const QHostAddress &address, quint16 port)
{
    Q_ASSERT(m_mode == Client);
    m_socket->connectToHost(address, port);
}

void RpcConnection::disconnectFromHost()
{
    Q_ASSERT(m_mode == Client);
    m_socket->disconnectFromHost();
}

bool RpcConnection::waitForConnected(int msecs)
{
    Q_ASSERT(m_mode == Client);
    return m_socket->waitForConnected(msecs);
}

bool RpcConnection::listen(const QHostAddress &address, quint16 port)
{
    Q_ASSERT(m_mode == Server);
    return m_server->listen(address, port);
}

void RpcConnection::handleNewConnection()
{
    QTcpSocket *socket = m_server->nextPendingConnection();
    connect(socket, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    qDebug() << "Connected to client";
}

void RpcConnection::sendError(QTcpSocket *socket, const QString &id, int error, const QString &message, const QString &data)
{
    QVariantMap responseMap;
    responseMap.insert("jsonrpc", "2.0");
    responseMap.insert("id", id);

    QVariantMap errorMap;
    errorMap.insert("code", error);
    errorMap.insert("message", message);
    errorMap.insert("data", data);
    responseMap.insert("error", errorMap);

    QByteArray jsonRpc = Json::stringify(QVariant(responseMap));

    struct Header { int length; } header;
    header.length = htonl(jsonRpc.length());
    socket->write((const char *)&header, sizeof(header));
    socket->write(jsonRpc);
    socket->flush();
}

void RpcConnection::handleReadyRead()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
    Q_ASSERT(socket);
    do {
        struct Header { int length; } header;
        socket->read((char *)&header, sizeof(header));
        header.length = ntohl(header.length);
        QByteArray msg = socket->read(header.length); // ## assumes we got complete message

        QString error;
        QVariantMap map = Json::parse(msg, &error).toMap();
        if (!error.isEmpty() || map["jsonrpc"].toString() != "2.0") {
            if (m_mode == Server)
                sendError(socket, map["id"].toString(), ParseError, "Malformatted JSON-RPC", error);
            qWarning() << "Malformatted JSON-RPC " << error;
            return;
        }

        if (map.contains("method")) {
            handleRpcCall(socket, map);
        } else if (map.contains("result")) {
            handleRpcResponse(map);
        } else if (map.contains("error")) {
            handleRpcError(map);
        } else {
            sendError(socket, map["id"].toString(), InvalidRequest, "Invalid request. Mot a method call, result or error");
            qWarning() << "No idea what to do with this message" << msg;
        }
    } while (socket->bytesAvailable() != 0);
}

void RpcConnection::handleRpcCall(QTcpSocket *socket, const QVariantMap &map)
{
    QVariantList params = map["params"].toList();

    QString rpcMethod = map["method"].toString();
    int idx = rpcMethod.indexOf('.');
    QString objName = rpcMethod.mid(0, idx);
    QString method = rpcMethod.mid(idx+1);
    QObject *object = m_objects.value(objName);
    if (!object) {
        sendError(socket, map["id"].toString(), MethodNotFound, "No such object");
        qWarning() << "RPC object " << rpcMethod << " not found";
        return;
    }

    idx = object->metaObject()->indexOfMethod(method.toLatin1());
    if (idx < 0) {
        sendError(socket, map["id"].toString(), MethodNotFound, "No such method");
        qWarning() << "Object '" << objName << "' does not have method named " << method;
        return;
    }
    QMetaMethod mm = object->metaObject()->method(idx);
    if (mm.access() == QMetaMethod::Private || mm.methodType() == QMetaMethod::Signal) {
        sendError(socket, map["id"].toString(), MethodNotFound, "Method is private or is a signal");
        qWarning() << "Method " << method << " is a signal or has private access";
        return;
    }

    QVarLengthArray<void *, 10> args(params.count()+1);
    QVariant result;
    if (mm.typeName()) {
        result = QVariant(QVariant::nameToType(mm.typeName()), (void *)0);
        args[0] = result.data();
    } else {
        args[0] = 0;
    }

    for (int i = 0; i < params.count(); i++) {
        args[i+1] = params[i].data();
    }

    QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx, args.data());

    sendResponse(socket, map["id"].toString(), args[0] ? *static_cast<QVariant *>(args[0]) : QVariant());
}

void RpcConnection::sendResponse(QTcpSocket *socket, const QString &id, const QVariant &result)
{
    QVariantMap responseMap;
    responseMap.insert("jsonrpc", "2.0");
    responseMap.insert("result", result);
    responseMap.insert("id", id);
    QByteArray jsonRpc = Json::stringify(QVariant(responseMap));

    struct Header { int length; } header;
    header.length = htonl(jsonRpc.length());
    socket->write((const char *)&header, sizeof(header));
    socket->write(jsonRpc);
    socket->flush();
}

void RpcConnection::handleRpcResponse(const QVariantMap &map)
{
    qDebug() << "Response" << map;
}

void RpcConnection::handleRpcError(const QVariantMap &map)
{
    qDebug() << "ERROR" << map;
}

int RpcConnection::call(const QByteArray &method, const QVariant &arg0, const QVariant &arg1,
                        const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5,
                        const QVariant &arg6, const QVariant &arg7, const QVariant &arg8, const QVariant &arg9)
{
    Q_ASSERT(m_mode == Client); // can't invoke methods in server mode since we use m_socket below
    struct Header { int length; } header;

    QVariantList params;
    params << arg0 << arg1 << arg2 << arg3 << arg4 << arg5 << arg6 << arg7 << arg8 << arg9;
    int i;
    for (i = 0; i < params.count(); i++) {
        if (params[i].isNull())
            break;
    }
    params = params.mid(0, i);

    QVariantMap map;
    map.insert("jsonrpc", "2.0");
    map.insert("method", method);
    map.insert("params", params);
    map.insert("id", m_id++);
    QByteArray jsonRpc = Json::stringify(QVariant(map));

    header.length = htonl(jsonRpc.length());
    m_socket->write((const char *)&header, sizeof(header));
    m_socket->write(jsonRpc);
    m_socket->flush();

    return m_id;
}

<|endoftext|>


<commit_before>/*-----------------------------------------------------------------------------
 This source file is part of Hopsan NG

 Copyright (c) 2011
    Mikael Axin, Robert Braun, Alessandro Dell'Amico, Björn Eriksson,
    Peter Nordin, Karl Pettersson, Petter Krus, Ingo Staack

 This file is provided "as is", with no guarantee or warranty for the
 functionality or reliability of the contents. All contents in this file is
 the original work of the copyright holders at the Division of Fluid and
 Mechatronic Systems (Flumes) at Linköping University. Modifying, using or
 redistributing any part of this file is prohibited without explicit
 permission from the copyright holders.
-----------------------------------------------------------------------------*/

//!
//! @file   Delay.hpp
//! @author Peter Nordin <peter.nordin@liu.se>
//! @date   2010-12-01
//!
//! @brief Contains the Core Utility Delay Circle Buffer class
//!
//$Id$

#ifndef DELAY_HPP_INCLUDED
#define DELAY_HPP_INCLUDED

#include "win32dll.h"
#include "assert.h"
#include <cmath>
#include <iostream>
//#include <vector>

namespace hopsan {

    class DLLIMPORTEXPORT Delay
    {
    public:
        Delay()
        {
            mpArray = 0;
        }

        ~Delay()
        {
            if (mpArray != 0)
            {
                delete mpArray;
            }
        }

        //! @brief Initialize Delay size based on timeDelay at a given timestep
        //! @param [in] timeDelay The total time delay for a value to come out on the other side of the circle buffer
        //! @param [in] Ts The timestep between each call
        //! @param [in] initValue The initial value of all buffer elements
        void initialize(const double timeDelay, const double Ts, const double initValue)
        {
            //mFracStep = timeDelay/Ts;
            //Calculate stepdelay, round double to int
            //! @todo mayby need to behave differently if we want to use fractions, (rount to ceeling before), however we cant do that allways as we get one extra step every time
            this->initialize( size_t(floor(timeDelay/Ts+0.5)), initValue);
        }

        //! @brief Initialize Delay size based on known number of delay steps
        //! @param [in] delaySteps The number of delay steps
        //! @param [in] initValue The initial value of all buffer elements
        void initialize(const size_t delaySteps, const double initValue)
        {
            if (mpArray != 0)
            {
                delete  mpArray;
            }
            assert(delaySteps > 0);
            mSize = delaySteps+1;

            mpArray = new double[mSize];
            std::cout << "DelayBuffer, size: " << delaySteps << ", mSize: " << mSize << ", init_value: " << initValue << std::endl;

            //! @todo maybe use c++ vector to init, or some smarter init, does not really matter I think
            for (size_t i=0; i<mSize; ++i)
            {
                mpArray[i] = initValue;
            }

            newest = 0;
            oldest = 1;
        }

        //! @brief updates delay with a new value
        //! @param [in] newValue The new value to insert into delay buffer
        //! @return The odlest value of the delay buffer (After update this value will have been overwriten in the buffer)
        double update(const double newValue)
        {
            // First get the oldes value
            double oldestValue = mpArray[oldest];

            // Increment the pointers, newets will after incrementaion overwrite previous oldest
            ++newest;
            ++oldest;

            if (oldest >= mSize)
            {
                oldest = 0;
            }
            if (newest >= mSize)
            {
                newest = 0;
            }

            // Overwrite previous oldest with the new_value
            mpArray[newest] = newValue;

            return oldestValue;
        }

        //! @brief Get the oldest value inte the buffer
        //! @return The oldest value in the buffer
        double getOldest()
        {
            return mpArray[oldest];
        }

        //! @brief Get the newest value inte the buffer
        //! @return The newest value in the buffer
        double getNewest()
        {
            return mpArray[newest];
        }

        //! @brief Returns a specific value, 0=newest, 1=nextnewest, 2=nextnextnewest and so on, no range check is performed
        //! @param [in] i Index of value to return
        //! @return Value of specified index
        double getIdx(const size_t i)
        {
            if ( (int(newest)-int(i)) < 0 )
            {
                return mpArray[mSize+newest-i];
            }
            else
            {
                return mpArray[newest-i];
            }
        }

        //! @brief Returns a specific value, 0=oldest, 1=nextoldest, 2=nextnextoldest and so on, no range check is performed
        //! @param [in] i Index of value to return
        //! @return Value of specified index
        double getOldIdx(const size_t i)
        {
            if (oldest+i >= mSize)
            {
                return mpArray[oldest+i-mSize];
            }
            else
            {
                return mpArray[oldest+i];
            }
        }

        //! @brief Get the size of the ring buffer (the number of elements)
        //! @return The size of the buffer (the number of elements)
        size_t getSize() const
        {
            return mSize-1;
        }

    private:
        size_t mSize;
        double *mpArray;
        size_t newest, oldest;

    };
}

#endif
<commit_msg>Made the Delay class a template class for use with any copyable type. Renamed to Delay_ made typedef Delay for compatibility, might change in the future<commit_after>/*-----------------------------------------------------------------------------
 This source file is part of Hopsan NG

 Copyright (c) 2011
    Mikael Axin, Robert Braun, Alessandro Dell'Amico, Björn Eriksson,
    Peter Nordin, Karl Pettersson, Petter Krus, Ingo Staack

 This file is provided "as is", with no guarantee or warranty for the
 functionality or reliability of the contents. All contents in this file is
 the original work of the copyright holders at the Division of Fluid and
 Mechatronic Systems (Flumes) at Linköping University. Modifying, using or
 redistributing any part of this file is prohibited without explicit
 permission from the copyright holders.
-----------------------------------------------------------------------------*/

//!
//! @file   Delay.hpp
//! @author Peter Nordin <peter.nordin@liu.se>
//! @date   2010-12-01
//!
//! @brief Contains the Component Utility Delay circle buffer class
//!
//$Id$

#ifndef DELAY_HPP_INCLUDED
#define DELAY_HPP_INCLUDED

#include "assert.h"
#include <cmath>
#include <iostream>

namespace hopsan {

template<typename T>
class Delay_
{
public:
    Delay_()
    {
        mpArray = 0;
    }

    ~Delay_()
    {
        if (mpArray != 0)
        {
            delete mpArray;
        }
    }

    //! @brief Initialize Delay size based on timeDelay at a given timestep
    //! @param [in] timeDelay The total time delay for a value to come out on the other side of the circle buffer
    //! @param [in] Ts The timestep between each call
    //! @param [in] initValue The initial value of all buffer elements
    void initialize(const double timeDelay, const double Ts, const T initValue)
    {
        //mFracStep = timeDelay/Ts;
        //Calculate stepdelay, round double to int
        //! @todo mayby need to behave differently if we want to use fractions, (rount to ceeling before), however we cant do that allways as we get one extra step every time
        this->initialize( size_t(floor(timeDelay/Ts+0.5)), initValue);
    }

    //! @brief Initialize Delay size based on known number of delay steps
    //! @param [in] delaySteps The number of delay steps
    //! @param [in] initValue The initial value of all buffer elements
    void initialize(const size_t delaySteps, const T initValue)
    {
        if (mpArray != 0)
        {
            delete  mpArray;
        }
        assert(delaySteps > 0);
        mSize = delaySteps+1;

        mpArray = new double[mSize];
        std::cout << "DelayBuffer, size: " << delaySteps << ", mSize: " << mSize << std::endl;

        //! @todo maybe use c++ vector to init, or some smarter init, does not really matter I think
        for (size_t i=0; i<mSize; ++i)
        {
            mpArray[i] = initValue;
        }

        newest = 0;
        oldest = 1;
    }

    //! @brief updates delay with a new value
    //! @param [in] newValue The new value to insert into delay buffer
    //! @return The odlest value of the delay buffer (After update this value will have been overwriten in the buffer)
    T update(const T newValue)
    {
        // First get the oldes value
        T oldestValue = mpArray[oldest];

        // Increment the pointers, newets will after incrementaion overwrite previous oldest
        ++newest;
        ++oldest;

        if (oldest >= mSize)
        {
            oldest = 0;
        }
        if (newest >= mSize)
        {
            newest = 0;
        }

        // Overwrite previous oldest with the new_value
        mpArray[newest] = newValue;

        return oldestValue;
    }

    //! @brief Get the oldest value inte the buffer
    //! @return The oldest value in the buffer
    T getOldest()
    {
        return mpArray[oldest];
    }

    //! @brief Get the newest value inte the buffer
    //! @return The newest value in the buffer
    T getNewest()
    {
        return mpArray[newest];
    }

    //! @brief Returns a specific value, 0=newest, 1=nextnewest, 2=nextnextnewest and so on, no range check is performed
    //! @param [in] i Index of value to return
    //! @return Value of specified index
    T getIdx(const size_t i)
    {
        if ( (int(newest)-int(i)) < 0 )
        {
            return mpArray[mSize+newest-i];
        }
        else
        {
            return mpArray[newest-i];
        }
    }

    //! @brief Returns a specific value, 0=oldest, 1=nextoldest, 2=nextnextoldest and so on, no range check is performed
    //! @param [in] i Index of value to return
    //! @return Value of specified index
    T getOldIdx(const size_t i)
    {
        if (oldest+i >= mSize)
        {
            return mpArray[oldest+i-mSize];
        }
        else
        {
            return mpArray[oldest+i];
        }
    }

    //! @brief Get the size of the ring buffer (the number of elements)
    //! @return The size of the buffer (the number of elements)
    size_t getSize() const
    {
        return mSize-1;
    }

private:
    size_t mSize, newest, oldest;
    T *mpArray;
};

typedef Delay_<double> Delay; //!< @todo My we should only have template class and let users choose data type themselfs

}

#endif
<|endoftext|>


<commit_before>/* RTcmix  - Copyright (C) 2000  The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include <globals.h>
#include <prototypes.h>
#include <maxdispargs.h>
#include <pthread.h>
#include <Instrument.h>
#include <PField.h>
#include <PFieldSet.h>
#include "../rtstuff/rt.h"
#include "../rtstuff/rtdefs.h"
#include "../sys/mixerr.h"
#include <rtcmix_types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

extern heap rtHeap;     // intraverse.C

//#define DEBUG

#ifdef PFIELD_CLASS

static void
_printargs(const char *instname, const Arg arglist[], const int nargs)
{
   int i;
   Arg arg;

   if (print_is_on) {
      printf("========<rt-queueing>=======\n");
      printf("%s:  ", instname);
      for (i = 0; i < nargs; i++) {
         arglist[i].printInline(stdout);
      }
      putchar('\n');
      fflush(stdout);
   }
}

double
checkInsts(const char *instname, const Arg arglist[], const int nargs, Arg *retval)
{
// FIXME: set this up as in addcheckfuncs, so that the guts of the
// instrument name list are not exposed here.  -JGG
   rt_item *rt_p;       // rt_item defined in rt.h
   rt_item *rt_temp;
   Instrument *Iptr;

#ifdef DEBUG
   printf("ENTERING checkInsts() FUNCTION -----\n");
#endif

   mixerr = MX_FNAME;
   rt_temp = rt_list;
   rt_p = rt_list;

   while (rt_p) {

      if (strcmp(rt_p->rt_name, instname) == 0) {

         _printargs(instname, arglist, nargs);

         /* set up the Instrument */

         Iptr = (*(rt_p->rt_ptr))();

         Iptr->ref();   // We do this to assure one reference

		// Load PFieldSet with ConstPField instances for each
		// valid p field.
		PFieldSet *pfieldset = new PFieldSet(nargs);
		for (int arg = 0; arg < nargs; ++arg) {
		  const Arg &theArg = arglist[arg];
		  if (theArg.isType(DoubleType))
			pfieldset->load(new ConstPField((double) theArg), arg);
		  else if (theArg.isType(StringType))
			pfieldset->load(new StringPField(theArg.string()), arg);
		  else if (theArg.isType(HandleType)) {
			Handle handle = (Handle) theArg;
			if (handle->type == PFieldType) {
				assert(handle->ptr != NULL);
				pfieldset->load((PField *) handle->ptr, arg);
			}
		  }
		  else {
			// For now, default to using a zero PField.
			pfieldset->load(new ConstPField(0.0), arg);
			break;
		  }
		}
        double rv = (double) Iptr->setup(pfieldset);

        if (rv == (double) DONT_SCHEDULE) { // only schedule if no init() error
			mixerr = MX_FAIL;
            return rv;
		}

        // For non-interactive case, configure() is delayed until just
        // before instrument run time.
		if (rtInteractive) {
		   if (Iptr->configure(RTBUFSAMPS) != 0) {
			   mixerr = MX_FAIL;
			   return -1;	// Configuration error!
		   }
		}

        /* schedule instrument */
        Iptr->schedule(&rtHeap);

        mixerr = MX_NOERR;
        rt_list = rt_temp;

		// Create Handle for Iptr on return
		Handle rethandle = (Handle) malloc(sizeof(struct _handle));
		if (rethandle) {
			rethandle->type = InstrumentPtrType;
			rethandle->ptr = (void *) Iptr;
			*retval = rethandle;
		}
#ifdef DEBUG
         printf("EXITING checkInsts() FUNCTION -----\n");
#endif
         return rv;
      }
      rt_p = rt_p->rt_next;
   }
   rt_list = rt_temp;

   return 0.0;
}

#else /* !PFIELD_CLASS */

double checkInsts(const char *fname, double *pp, int n_args, void **inst)
{
   int i;
   rt_item *rt_p;
   rt_item *rt_temp;
   Instrument *Iptr;
   double rv;

#ifdef DEBUG
   printf("ENTERING checkInsts() FUNCTION -----\n");
#endif

   /* zero out the unused p fields */
   for (i = n_args; i < MAXDISPARGS; i++) pp[i] = 0.0;

   mixerr = MX_FNAME;
   rt_temp = rt_list;
   rt_p = rt_list;

   while (rt_p) {

      if (strcmp(rt_p->rt_name, fname) == 0) {
         if (print_is_on) {
            printf("========<rt-queueing>=======\n");
            printf("%s:  ",fname);
            for (i = 0; i < n_args; i++)
               printf("%f ", pp[i]);
            printf("\n");
         }

         /* set up the Instrument */

         Iptr = (*(rt_p->rt_ptr))();

         Iptr->ref();   // We do this to assure one reference

         rv = (double) Iptr->init(pp, n_args);

         if (rv != DONT_SCHEDULE) { // only schedule if no init() error
            // For non-interactive case, configure() is delayed until just
            // before instrument run time.
            if (rtInteractive) {
               if (Iptr->configure(RTBUFSAMPS) != 0) {
			       mixerr = MX_FAIL;
				   return -1;	// Configuration error!
			   }
			}

            /* schedule instrument */
            Iptr->schedule(&rtHeap);

            mixerr = MX_NOERR;
            rt_list = rt_temp;
         } else {
		    mixerr = MX_FAIL;
            return rv;
         }

#ifdef DEBUG
         printf("EXITING checkInsts() FUNCTION -----\n");
#endif

         if (inst != NULL)
            *inst = (void *) Iptr;
         return rv;
      }
      rt_p = rt_p->rt_next;
   }
   rt_list = rt_temp;

#ifdef DEBUG
   printf("EXITING checkInsts() FUNCTION (function not found) -----\n");
#endif

   return 0.0;
}

#endif /* !PFIELD_CLASS */
<commit_msg>Use global Option object.<commit_after>/* RTcmix  - Copyright (C) 2000  The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include <globals.h>
#include <prototypes.h>
#include <maxdispargs.h>
#include <pthread.h>
#include <Instrument.h>
#include <PField.h>
#include <PFieldSet.h>
#include "../rtstuff/rt.h"
#include "../rtstuff/rtdefs.h"
#include "../sys/mixerr.h"
#include <rtcmix_types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

extern heap rtHeap;     // intraverse.C

//#define DEBUG

#ifdef PFIELD_CLASS

static void
_printargs(const char *instname, const Arg arglist[], const int nargs)
{
   int i;
   Arg arg;

   if (options.print()) {
      printf("========<rt-queueing>=======\n");
      printf("%s:  ", instname);
      for (i = 0; i < nargs; i++) {
         arglist[i].printInline(stdout);
      }
      putchar('\n');
      fflush(stdout);
   }
}

double
checkInsts(const char *instname, const Arg arglist[], const int nargs, Arg *retval)
{
// FIXME: set this up as in addcheckfuncs, so that the guts of the
// instrument name list are not exposed here.  -JGG
   rt_item *rt_p;       // rt_item defined in rt.h
   rt_item *rt_temp;
   Instrument *Iptr;

#ifdef DEBUG
   printf("ENTERING checkInsts() FUNCTION -----\n");
#endif

   mixerr = MX_FNAME;
   rt_temp = rt_list;
   rt_p = rt_list;

   while (rt_p) {

      if (strcmp(rt_p->rt_name, instname) == 0) {

         _printargs(instname, arglist, nargs);

         /* set up the Instrument */

         Iptr = (*(rt_p->rt_ptr))();

         Iptr->ref();   // We do this to assure one reference

		// Load PFieldSet with ConstPField instances for each
		// valid p field.
		PFieldSet *pfieldset = new PFieldSet(nargs);
		for (int arg = 0; arg < nargs; ++arg) {
		  const Arg &theArg = arglist[arg];
		  if (theArg.isType(DoubleType))
			pfieldset->load(new ConstPField((double) theArg), arg);
		  else if (theArg.isType(StringType))
			pfieldset->load(new StringPField(theArg.string()), arg);
		  else if (theArg.isType(HandleType)) {
			Handle handle = (Handle) theArg;
			if (handle->type == PFieldType) {
				assert(handle->ptr != NULL);
				pfieldset->load((PField *) handle->ptr, arg);
			}
		  }
		  else {
			// For now, default to using a zero PField.
			pfieldset->load(new ConstPField(0.0), arg);
			break;
		  }
		}
        double rv = (double) Iptr->setup(pfieldset);

        if (rv == (double) DONT_SCHEDULE) { // only schedule if no init() error
			mixerr = MX_FAIL;
            return rv;
		}

        // For non-interactive case, configure() is delayed until just
        // before instrument run time.
		if (rtInteractive) {
		   if (Iptr->configure(RTBUFSAMPS) != 0) {
			   mixerr = MX_FAIL;
			   return -1;	// Configuration error!
		   }
		}

        /* schedule instrument */
        Iptr->schedule(&rtHeap);

        mixerr = MX_NOERR;
        rt_list = rt_temp;

		// Create Handle for Iptr on return
		Handle rethandle = (Handle) malloc(sizeof(struct _handle));
		if (rethandle) {
			rethandle->type = InstrumentPtrType;
			rethandle->ptr = (void *) Iptr;
			*retval = rethandle;
		}
#ifdef DEBUG
         printf("EXITING checkInsts() FUNCTION -----\n");
#endif
         return rv;
      }
      rt_p = rt_p->rt_next;
   }
   rt_list = rt_temp;

   return 0.0;
}

#else /* !PFIELD_CLASS */

double checkInsts(const char *fname, double *pp, int n_args, void **inst)
{
   int i;
   rt_item *rt_p;
   rt_item *rt_temp;
   Instrument *Iptr;
   double rv;

#ifdef DEBUG
   printf("ENTERING checkInsts() FUNCTION -----\n");
#endif

   /* zero out the unused p fields */
   for (i = n_args; i < MAXDISPARGS; i++) pp[i] = 0.0;

   mixerr = MX_FNAME;
   rt_temp = rt_list;
   rt_p = rt_list;

   while (rt_p) {

      if (strcmp(rt_p->rt_name, fname) == 0) {
         if (options.print()) {
            printf("========<rt-queueing>=======\n");
            printf("%s:  ",fname);
            for (i = 0; i < n_args; i++)
               printf("%f ", pp[i]);
            printf("\n");
         }

         /* set up the Instrument */

         Iptr = (*(rt_p->rt_ptr))();

         Iptr->ref();   // We do this to assure one reference

         rv = (double) Iptr->init(pp, n_args);

         if (rv != DONT_SCHEDULE) { // only schedule if no init() error
            // For non-interactive case, configure() is delayed until just
            // before instrument run time.
            if (rtInteractive) {
               if (Iptr->configure(RTBUFSAMPS) != 0) {
			       mixerr = MX_FAIL;
				   return -1;	// Configuration error!
			   }
			}

            /* schedule instrument */
            Iptr->schedule(&rtHeap);

            mixerr = MX_NOERR;
            rt_list = rt_temp;
         } else {
		    mixerr = MX_FAIL;
            return rv;
         }

#ifdef DEBUG
         printf("EXITING checkInsts() FUNCTION -----\n");
#endif

         if (inst != NULL)
            *inst = (void *) Iptr;
         return rv;
      }
      rt_p = rt_p->rt_next;
   }
   rt_list = rt_temp;

#ifdef DEBUG
   printf("EXITING checkInsts() FUNCTION (function not found) -----\n");
#endif

   return 0.0;
}

#endif /* !PFIELD_CLASS */
<|endoftext|>


<commit_before>// Copyright 2019 The Marl Authors.
//
// 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
//
//     https://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.

#include "marl/thread.h"

#include "marl/debug.h"
#include "marl/defer.h"
#include "marl/trace.h"

#include <algorithm>  // std::sort

#include <cstdarg>
#include <cstdio>

#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <array>
#include <cstdlib>  // mbstowcs
#include <limits>   // std::numeric_limits
#undef max
#elif defined(__APPLE__)
#include <mach/thread_act.h>
#include <pthread.h>
#include <unistd.h>
#include <thread>
#elif defined(__FreeBSD__)
#include <pthread.h>
#include <pthread_np.h>
#include <unistd.h>
#include <thread>
#else
#include <pthread.h>
#include <unistd.h>
#include <thread>
#endif

namespace {

struct CoreHasher {
  inline uint64_t operator()(const marl::Thread::Core& core) const {
    return core.pthread.index;
  }
};

}  // anonymous namespace

namespace marl {

#if defined(_WIN32)
static constexpr size_t MaxCoreCount =
    std::numeric_limits<decltype(Thread::Core::windows.index)>::max() + 1ULL;
static constexpr size_t MaxGroupCount =
    std::numeric_limits<decltype(Thread::Core::windows.group)>::max() + 1ULL;
static_assert(sizeof(KAFFINITY) * 8ULL <= MaxCoreCount,
              "Thread::Core::windows.index is too small");

namespace {
#define CHECK_WIN32(expr)                                    \
  do {                                                       \
    auto res = expr;                                         \
    (void)res;                                               \
    MARL_ASSERT(res == TRUE, #expr " failed with error: %d", \
                (int)GetLastError());                        \
  } while (false)

struct ProcessorGroup {
  unsigned int count;  // number of logical processors in this group.
  KAFFINITY affinity;  // affinity mask.
};

struct ProcessorGroups {
  std::array<ProcessorGroup, MaxGroupCount> groups;
  size_t count;
};

const ProcessorGroups& getProcessorGroups() {
  static ProcessorGroups groups = [] {
    ProcessorGroups out = {};
    SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info[32] = {};
    DWORD size = sizeof(info);
    CHECK_WIN32(GetLogicalProcessorInformationEx(RelationGroup, info, &size));
    DWORD count = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX);
    for (DWORD i = 0; i < count; i++) {
      if (info[i].Relationship == RelationGroup) {
        auto groupCount = info[i].Group.ActiveGroupCount;
        for (WORD groupIdx = 0; groupIdx < groupCount; groupIdx++) {
          auto const& groupInfo = info[i].Group.GroupInfo[groupIdx];
          out.groups[out.count++] = ProcessorGroup{
              groupInfo.ActiveProcessorCount, groupInfo.ActiveProcessorMask};
          MARL_ASSERT(out.count <= MaxGroupCount, "Group index overflow");
        }
      }
    }
    return out;
  }();
  return groups;
}
}  // namespace
#endif  // defined(_WIN32)

////////////////////////////////////////////////////////////////////////////////
// Thread::Affinty
////////////////////////////////////////////////////////////////////////////////

Thread::Affinity::Affinity(Allocator* allocator) : cores(allocator) {}
Thread::Affinity::Affinity(Affinity&& other) : cores(std::move(other.cores)) {}
Thread::Affinity::Affinity(const Affinity& other, Allocator* allocator)
    : cores(other.cores, allocator) {}

Thread::Affinity::Affinity(std::initializer_list<Core> list,
                           Allocator* allocator)
    : cores(allocator) {
  cores.reserve(list.size());
  for (auto core : list) {
    cores.push_back(core);
  }
}

Thread::Affinity Thread::Affinity::all(
    Allocator* allocator /* = Allocator::Default */) {
  Thread::Affinity affinity(allocator);

#if defined(_WIN32)
  const auto& groups = getProcessorGroups();
  for (size_t groupIdx = 0; groupIdx < groups.count; groupIdx++) {
    const auto& group = groups.groups[groupIdx];
    Core core;
    core.windows.group = static_cast<decltype(Core::windows.group)>(groupIdx);
    for (unsigned int coreIdx = 0; coreIdx < group.count; coreIdx++) {
      if ((group.affinity >> coreIdx) & 1) {
        core.windows.index = static_cast<decltype(core.windows.index)>(coreIdx);
        affinity.cores.emplace_back(std::move(core));
      }
    }
  }
#elif defined(__linux__) && !defined(__ANDROID__)
  auto thread = pthread_self();
  cpu_set_t cpuset;
  CPU_ZERO(&cpuset);
  if (pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset) == 0) {
    int count = CPU_COUNT(&cpuset);
    for (int i = 0; i < count; i++) {
      Core core;
      core.pthread.index = static_cast<uint16_t>(i);
      affinity.cores.emplace_back(std::move(core));
    }
  }
#elif defined(__FreeBSD__)
  auto thread = pthread_self();
  cpuset_t cpuset;
  CPU_ZERO(&cpuset);
  if (pthread_getaffinity_np(thread, sizeof(cpuset_t), &cpuset) == 0) {
    int count = CPU_COUNT(&cpuset);
    for (int i = 0; i < count; i++) {
      Core core;
      core.pthread.index = static_cast<uint16_t>(i);
      affinity.cores.emplace_back(std::move(core));
    }
  }
#else
  static_assert(!supported,
                "marl::Thread::Affinity::supported is true, but "
                "Thread::Affinity::all() is not implemented for this platform");
#endif

  return affinity;
}

std::shared_ptr<Thread::Affinity::Policy> Thread::Affinity::Policy::anyOf(
    Affinity&& affinity,
    Allocator* allocator /* = Allocator::Default */) {
  struct Policy : public Thread::Affinity::Policy {
    Affinity affinity;
    Policy(Affinity&& affinity) : affinity(std::move(affinity)) {}

    Affinity get(uint32_t threadId, Allocator* allocator) const override {
#if defined(_WIN32)
      auto count = affinity.count();
      if (count == 0) {
        return Affinity(affinity, allocator);
      }
      auto group = affinity[threadId % affinity.count()].windows.group;
      Affinity out(allocator);
      out.cores.reserve(count);
      for (auto core : affinity.cores) {
        if (core.windows.group == group) {
          out.cores.push_back(core);
        }
      }
      return out;
#else
      return Affinity(affinity, allocator);
#endif
    }
  };

  return allocator->make_shared<Policy>(std::move(affinity));
}

std::shared_ptr<Thread::Affinity::Policy> Thread::Affinity::Policy::oneOf(
    Affinity&& affinity,
    Allocator* allocator /* = Allocator::Default */) {
  struct Policy : public Thread::Affinity::Policy {
    Affinity affinity;
    Policy(Affinity&& affinity) : affinity(std::move(affinity)) {}

    Affinity get(uint32_t threadId, Allocator* allocator) const override {
      auto count = affinity.count();
      if (count == 0) {
        return Affinity(affinity, allocator);
      }
      return Affinity({affinity[threadId % affinity.count()]}, allocator);
    }
  };

  return allocator->make_shared<Policy>(std::move(affinity));
}

size_t Thread::Affinity::count() const {
  return cores.size();
}

Thread::Core Thread::Affinity::operator[](size_t index) const {
  return cores[index];
}

Thread::Affinity& Thread::Affinity::add(const Thread::Affinity& other) {
  containers::unordered_set<Core, CoreHasher> set(cores.allocator);
  for (auto core : cores) {
    set.emplace(core);
  }
  for (auto core : other.cores) {
    if (set.count(core) == 0) {
      cores.push_back(core);
    }
  }
  std::sort(cores.begin(), cores.end());
  return *this;
}

Thread::Affinity& Thread::Affinity::remove(const Thread::Affinity& other) {
  containers::unordered_set<Core, CoreHasher> set(cores.allocator);
  for (auto core : other.cores) {
    set.emplace(core);
  }
  for (size_t i = 0; i < cores.size(); i++) {
    if (set.count(cores[i]) != 0) {
      cores[i] = cores.back();
      cores.resize(cores.size() - 1);
    }
  }
  std::sort(cores.begin(), cores.end());
  return *this;
}

#if defined(_WIN32)

class Thread::Impl {
 public:
  Impl(Func&& func) : func(std::move(func)) {}
  static DWORD WINAPI run(void* self) {
    reinterpret_cast<Impl*>(self)->func();
    return 0;
  }

  Func func;
  HANDLE handle;
};

Thread::Thread(Affinity&& affinity, Func&& func) {
  SIZE_T size = 0;
  InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
  MARL_ASSERT(size > 0,
              "InitializeProcThreadAttributeList() did not give a size");

  std::vector<uint8_t> buffer(size);
  LPPROC_THREAD_ATTRIBUTE_LIST attributes =
      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buffer.data());
  CHECK_WIN32(InitializeProcThreadAttributeList(attributes, 1, 0, &size));
  defer(DeleteProcThreadAttributeList(attributes));

  GROUP_AFFINITY groupAffinity = {};

  auto count = affinity.count();
  if (count > 0) {
    groupAffinity.Group = affinity[0].windows.group;
    for (size_t i = 0; i < count; i++) {
      auto core = affinity[i];
      MARL_ASSERT(groupAffinity.Group == core.windows.group,
                  "Cannot create thread that uses multiple affinity groups");
      groupAffinity.Mask |= (1ULL << core.windows.index);
    }
    CHECK_WIN32(UpdateProcThreadAttribute(
        attributes, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, &groupAffinity,
        sizeof(groupAffinity), nullptr, nullptr));
  }

  impl = new Impl(std::move(func));
  impl->handle = CreateRemoteThreadEx(GetCurrentProcess(), nullptr, 0,
                                      &Impl::run, impl, 0, attributes, nullptr);
}

Thread::~Thread() {
  if (impl) {
    CloseHandle(impl->handle);
    delete impl;
  }
}

void Thread::join() {
  MARL_ASSERT(impl != nullptr, "join() called on unjoinable thread");
  WaitForSingleObject(impl->handle, INFINITE);
}

void Thread::setName(const char* fmt, ...) {
  static auto setThreadDescription =
      reinterpret_cast<HRESULT(WINAPI*)(HANDLE, PCWSTR)>(GetProcAddress(
          GetModuleHandle("kernelbase.dll"), "SetThreadDescription"));
  if (setThreadDescription == nullptr) {
    return;
  }

  char name[1024];
  va_list vararg;
  va_start(vararg, fmt);
  vsnprintf(name, sizeof(name), fmt, vararg);
  va_end(vararg);

  wchar_t wname[1024];
  mbstowcs(wname, name, 1024);
  setThreadDescription(GetCurrentThread(), wname);
  MARL_NAME_THREAD("%s", name);
}

unsigned int Thread::numLogicalCPUs() {
  unsigned int count = 0;
  const auto& groups = getProcessorGroups();
  for (size_t groupIdx = 0; groupIdx < groups.count; groupIdx++) {
    const auto& group = groups.groups[groupIdx];
    count += group.count;
  }
  return count;
}

#else

class Thread::Impl {
 public:
  Impl(Affinity&& affinity, Thread::Func&& f)
      : affinity(std::move(affinity)), func(std::move(f)), thread([this] {
          setAffinity();
          func();
        }) {}

  Affinity affinity;
  Func func;
  std::thread thread;

  void setAffinity() {
    auto count = affinity.count();
    if (count == 0) {
      return;
    }

#if defined(__linux__) && !defined(__ANDROID__)
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    for (size_t i = 0; i < count; i++) {
      CPU_SET(affinity[i].pthread.index, &cpuset);
    }
    auto thread = pthread_self();
    pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
#elif defined(__FreeBSD__)
    cpuset_t cpuset;
    CPU_ZERO(&cpuset);
    for (size_t i = 0; i < count; i++) {
      CPU_SET(affinity[i].pthread.index, &cpuset);
    }
    auto thread = pthread_self();
    pthread_setaffinity_np(thread, sizeof(cpuset_t), &cpuset);
#else
    MARL_ASSERT(!marl::Thread::Affinity::supported,
                "Attempting to use thread affinity on a unsupported platform");
#endif
  }
};

Thread::Thread(Affinity&& affinity, Func&& func)
    : impl(new Thread::Impl(std::move(affinity), std::move(func))) {}

Thread::~Thread() {
  delete impl;
}

void Thread::join() {
  impl->thread.join();
}

void Thread::setName(const char* fmt, ...) {
  char name[1024];
  va_list vararg;
  va_start(vararg, fmt);
  vsnprintf(name, sizeof(name), fmt, vararg);
  va_end(vararg);

#if defined(__APPLE__)
  pthread_setname_np(name);
#elif defined(__FreeBSD__)
  pthread_set_name_np(pthread_self(), name);
#elif !defined(__Fuchsia__)
  pthread_setname_np(pthread_self(), name);
#endif

  MARL_NAME_THREAD("%s", name);
}

unsigned int Thread::numLogicalCPUs() {
  return static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_ONLN));
}

#endif  // OS

Thread::Thread(Thread&& rhs) : impl(rhs.impl) {
  rhs.impl = nullptr;
}

Thread& Thread::operator=(Thread&& rhs) {
  if (impl) {
    delete impl;
    impl = nullptr;
  }
  impl = rhs.impl;
  rhs.impl = nullptr;
  return *this;
}

}  // namespace marl
<commit_msg>Add missing <vector> include<commit_after>// Copyright 2019 The Marl Authors.
//
// 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
//
//     https://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.

#include "marl/thread.h"

#include "marl/debug.h"
#include "marl/defer.h"
#include "marl/trace.h"

#include <algorithm>  // std::sort

#include <cstdarg>
#include <cstdio>

#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <array>
#include <cstdlib>  // mbstowcs
#include <limits>   // std::numeric_limits
#include <vector>
#undef max
#elif defined(__APPLE__)
#include <mach/thread_act.h>
#include <pthread.h>
#include <unistd.h>
#include <thread>
#elif defined(__FreeBSD__)
#include <pthread.h>
#include <pthread_np.h>
#include <unistd.h>
#include <thread>
#else
#include <pthread.h>
#include <unistd.h>
#include <thread>
#endif

namespace {

struct CoreHasher {
  inline uint64_t operator()(const marl::Thread::Core& core) const {
    return core.pthread.index;
  }
};

}  // anonymous namespace

namespace marl {

#if defined(_WIN32)
static constexpr size_t MaxCoreCount =
    std::numeric_limits<decltype(Thread::Core::windows.index)>::max() + 1ULL;
static constexpr size_t MaxGroupCount =
    std::numeric_limits<decltype(Thread::Core::windows.group)>::max() + 1ULL;
static_assert(sizeof(KAFFINITY) * 8ULL <= MaxCoreCount,
              "Thread::Core::windows.index is too small");

namespace {
#define CHECK_WIN32(expr)                                    \
  do {                                                       \
    auto res = expr;                                         \
    (void)res;                                               \
    MARL_ASSERT(res == TRUE, #expr " failed with error: %d", \
                (int)GetLastError());                        \
  } while (false)

struct ProcessorGroup {
  unsigned int count;  // number of logical processors in this group.
  KAFFINITY affinity;  // affinity mask.
};

struct ProcessorGroups {
  std::array<ProcessorGroup, MaxGroupCount> groups;
  size_t count;
};

const ProcessorGroups& getProcessorGroups() {
  static ProcessorGroups groups = [] {
    ProcessorGroups out = {};
    SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info[32] = {};
    DWORD size = sizeof(info);
    CHECK_WIN32(GetLogicalProcessorInformationEx(RelationGroup, info, &size));
    DWORD count = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX);
    for (DWORD i = 0; i < count; i++) {
      if (info[i].Relationship == RelationGroup) {
        auto groupCount = info[i].Group.ActiveGroupCount;
        for (WORD groupIdx = 0; groupIdx < groupCount; groupIdx++) {
          auto const& groupInfo = info[i].Group.GroupInfo[groupIdx];
          out.groups[out.count++] = ProcessorGroup{
              groupInfo.ActiveProcessorCount, groupInfo.ActiveProcessorMask};
          MARL_ASSERT(out.count <= MaxGroupCount, "Group index overflow");
        }
      }
    }
    return out;
  }();
  return groups;
}
}  // namespace
#endif  // defined(_WIN32)

////////////////////////////////////////////////////////////////////////////////
// Thread::Affinty
////////////////////////////////////////////////////////////////////////////////

Thread::Affinity::Affinity(Allocator* allocator) : cores(allocator) {}
Thread::Affinity::Affinity(Affinity&& other) : cores(std::move(other.cores)) {}
Thread::Affinity::Affinity(const Affinity& other, Allocator* allocator)
    : cores(other.cores, allocator) {}

Thread::Affinity::Affinity(std::initializer_list<Core> list,
                           Allocator* allocator)
    : cores(allocator) {
  cores.reserve(list.size());
  for (auto core : list) {
    cores.push_back(core);
  }
}

Thread::Affinity Thread::Affinity::all(
    Allocator* allocator /* = Allocator::Default */) {
  Thread::Affinity affinity(allocator);

#if defined(_WIN32)
  const auto& groups = getProcessorGroups();
  for (size_t groupIdx = 0; groupIdx < groups.count; groupIdx++) {
    const auto& group = groups.groups[groupIdx];
    Core core;
    core.windows.group = static_cast<decltype(Core::windows.group)>(groupIdx);
    for (unsigned int coreIdx = 0; coreIdx < group.count; coreIdx++) {
      if ((group.affinity >> coreIdx) & 1) {
        core.windows.index = static_cast<decltype(core.windows.index)>(coreIdx);
        affinity.cores.emplace_back(std::move(core));
      }
    }
  }
#elif defined(__linux__) && !defined(__ANDROID__)
  auto thread = pthread_self();
  cpu_set_t cpuset;
  CPU_ZERO(&cpuset);
  if (pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset) == 0) {
    int count = CPU_COUNT(&cpuset);
    for (int i = 0; i < count; i++) {
      Core core;
      core.pthread.index = static_cast<uint16_t>(i);
      affinity.cores.emplace_back(std::move(core));
    }
  }
#elif defined(__FreeBSD__)
  auto thread = pthread_self();
  cpuset_t cpuset;
  CPU_ZERO(&cpuset);
  if (pthread_getaffinity_np(thread, sizeof(cpuset_t), &cpuset) == 0) {
    int count = CPU_COUNT(&cpuset);
    for (int i = 0; i < count; i++) {
      Core core;
      core.pthread.index = static_cast<uint16_t>(i);
      affinity.cores.emplace_back(std::move(core));
    }
  }
#else
  static_assert(!supported,
                "marl::Thread::Affinity::supported is true, but "
                "Thread::Affinity::all() is not implemented for this platform");
#endif

  return affinity;
}

std::shared_ptr<Thread::Affinity::Policy> Thread::Affinity::Policy::anyOf(
    Affinity&& affinity,
    Allocator* allocator /* = Allocator::Default */) {
  struct Policy : public Thread::Affinity::Policy {
    Affinity affinity;
    Policy(Affinity&& affinity) : affinity(std::move(affinity)) {}

    Affinity get(uint32_t threadId, Allocator* allocator) const override {
#if defined(_WIN32)
      auto count = affinity.count();
      if (count == 0) {
        return Affinity(affinity, allocator);
      }
      auto group = affinity[threadId % affinity.count()].windows.group;
      Affinity out(allocator);
      out.cores.reserve(count);
      for (auto core : affinity.cores) {
        if (core.windows.group == group) {
          out.cores.push_back(core);
        }
      }
      return out;
#else
      return Affinity(affinity, allocator);
#endif
    }
  };

  return allocator->make_shared<Policy>(std::move(affinity));
}

std::shared_ptr<Thread::Affinity::Policy> Thread::Affinity::Policy::oneOf(
    Affinity&& affinity,
    Allocator* allocator /* = Allocator::Default */) {
  struct Policy : public Thread::Affinity::Policy {
    Affinity affinity;
    Policy(Affinity&& affinity) : affinity(std::move(affinity)) {}

    Affinity get(uint32_t threadId, Allocator* allocator) const override {
      auto count = affinity.count();
      if (count == 0) {
        return Affinity(affinity, allocator);
      }
      return Affinity({affinity[threadId % affinity.count()]}, allocator);
    }
  };

  return allocator->make_shared<Policy>(std::move(affinity));
}

size_t Thread::Affinity::count() const {
  return cores.size();
}

Thread::Core Thread::Affinity::operator[](size_t index) const {
  return cores[index];
}

Thread::Affinity& Thread::Affinity::add(const Thread::Affinity& other) {
  containers::unordered_set<Core, CoreHasher> set(cores.allocator);
  for (auto core : cores) {
    set.emplace(core);
  }
  for (auto core : other.cores) {
    if (set.count(core) == 0) {
      cores.push_back(core);
    }
  }
  std::sort(cores.begin(), cores.end());
  return *this;
}

Thread::Affinity& Thread::Affinity::remove(const Thread::Affinity& other) {
  containers::unordered_set<Core, CoreHasher> set(cores.allocator);
  for (auto core : other.cores) {
    set.emplace(core);
  }
  for (size_t i = 0; i < cores.size(); i++) {
    if (set.count(cores[i]) != 0) {
      cores[i] = cores.back();
      cores.resize(cores.size() - 1);
    }
  }
  std::sort(cores.begin(), cores.end());
  return *this;
}

#if defined(_WIN32)

class Thread::Impl {
 public:
  Impl(Func&& func) : func(std::move(func)) {}
  static DWORD WINAPI run(void* self) {
    reinterpret_cast<Impl*>(self)->func();
    return 0;
  }

  Func func;
  HANDLE handle;
};

Thread::Thread(Affinity&& affinity, Func&& func) {
  SIZE_T size = 0;
  InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
  MARL_ASSERT(size > 0,
              "InitializeProcThreadAttributeList() did not give a size");

  std::vector<uint8_t> buffer(size);
  LPPROC_THREAD_ATTRIBUTE_LIST attributes =
      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buffer.data());
  CHECK_WIN32(InitializeProcThreadAttributeList(attributes, 1, 0, &size));
  defer(DeleteProcThreadAttributeList(attributes));

  GROUP_AFFINITY groupAffinity = {};

  auto count = affinity.count();
  if (count > 0) {
    groupAffinity.Group = affinity[0].windows.group;
    for (size_t i = 0; i < count; i++) {
      auto core = affinity[i];
      MARL_ASSERT(groupAffinity.Group == core.windows.group,
                  "Cannot create thread that uses multiple affinity groups");
      groupAffinity.Mask |= (1ULL << core.windows.index);
    }
    CHECK_WIN32(UpdateProcThreadAttribute(
        attributes, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, &groupAffinity,
        sizeof(groupAffinity), nullptr, nullptr));
  }

  impl = new Impl(std::move(func));
  impl->handle = CreateRemoteThreadEx(GetCurrentProcess(), nullptr, 0,
                                      &Impl::run, impl, 0, attributes, nullptr);
}

Thread::~Thread() {
  if (impl) {
    CloseHandle(impl->handle);
    delete impl;
  }
}

void Thread::join() {
  MARL_ASSERT(impl != nullptr, "join() called on unjoinable thread");
  WaitForSingleObject(impl->handle, INFINITE);
}

void Thread::setName(const char* fmt, ...) {
  static auto setThreadDescription =
      reinterpret_cast<HRESULT(WINAPI*)(HANDLE, PCWSTR)>(GetProcAddress(
          GetModuleHandle("kernelbase.dll"), "SetThreadDescription"));
  if (setThreadDescription == nullptr) {
    return;
  }

  char name[1024];
  va_list vararg;
  va_start(vararg, fmt);
  vsnprintf(name, sizeof(name), fmt, vararg);
  va_end(vararg);

  wchar_t wname[1024];
  mbstowcs(wname, name, 1024);
  setThreadDescription(GetCurrentThread(), wname);
  MARL_NAME_THREAD("%s", name);
}

unsigned int Thread::numLogicalCPUs() {
  unsigned int count = 0;
  const auto& groups = getProcessorGroups();
  for (size_t groupIdx = 0; groupIdx < groups.count; groupIdx++) {
    const auto& group = groups.groups[groupIdx];
    count += group.count;
  }
  return count;
}

#else

class Thread::Impl {
 public:
  Impl(Affinity&& affinity, Thread::Func&& f)
      : affinity(std::move(affinity)), func(std::move(f)), thread([this] {
          setAffinity();
          func();
        }) {}

  Affinity affinity;
  Func func;
  std::thread thread;

  void setAffinity() {
    auto count = affinity.count();
    if (count == 0) {
      return;
    }

#if defined(__linux__) && !defined(__ANDROID__)
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    for (size_t i = 0; i < count; i++) {
      CPU_SET(affinity[i].pthread.index, &cpuset);
    }
    auto thread = pthread_self();
    pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
#elif defined(__FreeBSD__)
    cpuset_t cpuset;
    CPU_ZERO(&cpuset);
    for (size_t i = 0; i < count; i++) {
      CPU_SET(affinity[i].pthread.index, &cpuset);
    }
    auto thread = pthread_self();
    pthread_setaffinity_np(thread, sizeof(cpuset_t), &cpuset);
#else
    MARL_ASSERT(!marl::Thread::Affinity::supported,
                "Attempting to use thread affinity on a unsupported platform");
#endif
  }
};

Thread::Thread(Affinity&& affinity, Func&& func)
    : impl(new Thread::Impl(std::move(affinity), std::move(func))) {}

Thread::~Thread() {
  delete impl;
}

void Thread::join() {
  impl->thread.join();
}

void Thread::setName(const char* fmt, ...) {
  char name[1024];
  va_list vararg;
  va_start(vararg, fmt);
  vsnprintf(name, sizeof(name), fmt, vararg);
  va_end(vararg);

#if defined(__APPLE__)
  pthread_setname_np(name);
#elif defined(__FreeBSD__)
  pthread_set_name_np(pthread_self(), name);
#elif !defined(__Fuchsia__)
  pthread_setname_np(pthread_self(), name);
#endif

  MARL_NAME_THREAD("%s", name);
}

unsigned int Thread::numLogicalCPUs() {
  return static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_ONLN));
}

#endif  // OS

Thread::Thread(Thread&& rhs) : impl(rhs.impl) {
  rhs.impl = nullptr;
}

Thread& Thread::operator=(Thread&& rhs) {
  if (impl) {
    delete impl;
    impl = nullptr;
  }
  impl = rhs.impl;
  rhs.impl = nullptr;
  return *this;
}

}  // namespace marl
<|endoftext|>


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

#include <cmath>

#include <GL/glew.h>

#include "camera.h"

#include "ui/gl_canvas.h"
#include "visualization/game_object.h"
#include "visualization/stage.h"

Camera& Camera::operator=(const Camera& cam)
{
    zoom_power_    = cam.zoom_power_;
    camera_pos_x_  = cam.camera_pos_x_;
    camera_pos_y_  = cam.camera_pos_y_;
    canvas_width_  = cam.canvas_width_;
    canvas_height_ = cam.canvas_height_;
    scale_         = cam.scale_;

    update_object_pose();

    return *this;
}

void Camera::window_resized(int w, int h)
{
    projection.set_ortho_projection(w / 2.0, h / 2.0, -1.0f, 1.0f);
    canvas_width_  = w;
    canvas_height_ = h;
}

void Camera::scroll_callback(float delta)
{
    float mouse_x = gl_canvas->mouse_x();
    float mouse_y = gl_canvas->mouse_y();
    float win_w   = gl_canvas->width();
    float win_h   = gl_canvas->height();

    vec4 mouse_pos_ndc(2.0 * (mouse_x - win_w / 2) / win_w,
                       -2.0 * (mouse_y - win_h / 2) / win_h,
                       0,
                       1);
    mat4 vp_inv = game_object->get_pose() * projection.inv();

    float delta_zoom = std::pow(zoom_factor, -delta);

    vec4 mouse_pos = scale_.inv() * vp_inv * mouse_pos_ndc;

    // Since the view matrix of the camera is inverted before being applied to
    // the world coordinates, the order in which the operations below are
    // applied to world coordinates during rendering will also be reversed

    // clang-format off
    scale_ = scale_ *
             mat4::translation(mouse_pos) *
             mat4::scale(vec4(delta_zoom, delta_zoom, 1.0, 1.0)) *
             mat4::translation(-mouse_pos);
    // clang-format on

    // Calls to compute_zoom will require the zoom_power_ parameter to be on par
    // with the accumulated delta_zooms
    zoom_power_ += delta;

    update_object_pose();
}

void Camera::update()
{
}

void Camera::update_object_pose()
{
    if (game_object != nullptr) {
        vec4 position{-camera_pos_x_, -camera_pos_y_, 0, 1.0f};

        // Since the view matrix of the camera is inverted before being applied
        // to the world coordinates, the order in which the operations below are
        // applied to world coordinates during rendering will also be reversed

        // clang-format off
        mat4 pose =  scale_ *
                     mat4::translation(position);
        // clang-format on

        game_object->set_pose(pose);
    }
}

bool Camera::post_initialize()
{
    window_resized(gl_canvas->width(), gl_canvas->height());
    set_initial_zoom();
    update_object_pose();

    return true;
}

void Camera::set_initial_zoom()
{
    GameObject* buffer_obj = game_object->stage->get_game_object("buffer");
    Buffer* buff = buffer_obj->get_component<Buffer>("buffer_component");

    vec4 buf_dim = buffer_obj->get_pose() *
                   vec4(buff->buffer_width_f, buff->buffer_height_f, 0, 1);

    zoom_power_ = 0.0;

    if (canvas_width_ > buf_dim.x() && canvas_height_ > buf_dim.y()) {
        // Zoom in
        zoom_power_ += 1.0;
        float new_zoom = compute_zoom();

        // Iterate until buffer can fit inside the canvas
        while (canvas_width_ > new_zoom * buf_dim.x() &&
               canvas_height_ > new_zoom * buf_dim.y()) {
            zoom_power_ += 1.0;
            new_zoom = compute_zoom();
        }

        zoom_power_ -= 1.0;
    } else if (canvas_width_ < buf_dim.x() || canvas_height_ < buf_dim.y()) {
        // Zoom out
        zoom_power_ -= 1.0;
        float new_zoom = compute_zoom();

        // Iterate until buffer can fit inside the canvas
        while (canvas_width_ < new_zoom * buf_dim.x() ||
               canvas_height_ < new_zoom * buf_dim.y()) {
            zoom_power_ -= 1.0;
            new_zoom = compute_zoom();
        }
    }

    float zoom = 1.f / compute_zoom();
    scale_     = mat4::scale(vec4(zoom, zoom, 1.0, 1.0));
}

float Camera::compute_zoom()
{
    return std::pow(zoom_factor, zoom_power_);
}

void Camera::recenter_camera()
{
    camera_pos_x_ = camera_pos_y_ = 0.0f;

    set_initial_zoom();
    update_object_pose();
}

void Camera::mouse_drag_event(int mouse_x, int mouse_y)
{
    // Mouse is down. Update camera_pos_x_/camera_pos_y_
    camera_pos_x_ += mouse_x;
    camera_pos_y_ += mouse_y;

    update_object_pose();
}

bool Camera::post_buffer_update()
{
    return true;
}
<commit_msg>Fix computation of buffer size<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2017 GDB ImageWatch contributors
 * (github.com/csantosbh/gdb-imagewatch/)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <cmath>

#include <GL/glew.h>

#include "camera.h"

#include "ui/gl_canvas.h"
#include "visualization/game_object.h"
#include "visualization/stage.h"

Camera& Camera::operator=(const Camera& cam)
{
    zoom_power_    = cam.zoom_power_;
    camera_pos_x_  = cam.camera_pos_x_;
    camera_pos_y_  = cam.camera_pos_y_;
    canvas_width_  = cam.canvas_width_;
    canvas_height_ = cam.canvas_height_;
    scale_         = cam.scale_;

    update_object_pose();

    return *this;
}

void Camera::window_resized(int w, int h)
{
    projection.set_ortho_projection(w / 2.0, h / 2.0, -1.0f, 1.0f);
    canvas_width_  = w;
    canvas_height_ = h;
}

void Camera::scroll_callback(float delta)
{
    float mouse_x = gl_canvas->mouse_x();
    float mouse_y = gl_canvas->mouse_y();
    float win_w   = gl_canvas->width();
    float win_h   = gl_canvas->height();

    vec4 mouse_pos_ndc(2.0 * (mouse_x - win_w / 2) / win_w,
                       -2.0 * (mouse_y - win_h / 2) / win_h,
                       0,
                       1);
    mat4 vp_inv = game_object->get_pose() * projection.inv();

    float delta_zoom = std::pow(zoom_factor, -delta);

    vec4 mouse_pos = scale_.inv() * vp_inv * mouse_pos_ndc;

    // Since the view matrix of the camera is inverted before being applied to
    // the world coordinates, the order in which the operations below are
    // applied to world coordinates during rendering will also be reversed

    // clang-format off
    scale_ = scale_ *
             mat4::translation(mouse_pos) *
             mat4::scale(vec4(delta_zoom, delta_zoom, 1.0, 1.0)) *
             mat4::translation(-mouse_pos);
    // clang-format on

    // Calls to compute_zoom will require the zoom_power_ parameter to be on par
    // with the accumulated delta_zooms
    zoom_power_ += delta;

    update_object_pose();
}

void Camera::update()
{
}

void Camera::update_object_pose()
{
    if (game_object != nullptr) {
        vec4 position{-camera_pos_x_, -camera_pos_y_, 0, 1.0f};

        // Since the view matrix of the camera is inverted before being applied
        // to the world coordinates, the order in which the operations below are
        // applied to world coordinates during rendering will also be reversed

        // clang-format off
        mat4 pose =  scale_ *
                     mat4::translation(position);
        // clang-format on

        game_object->set_pose(pose);
    }
}

bool Camera::post_initialize()
{
    window_resized(gl_canvas->width(), gl_canvas->height());
    set_initial_zoom();
    update_object_pose();

    return true;
}

void Camera::set_initial_zoom()
{
    GameObject* buffer_obj = game_object->stage->get_game_object("buffer");
    Buffer* buff = buffer_obj->get_component<Buffer>("buffer_component");

    vec4 buf_dim = buffer_obj->get_pose() *
                   vec4(buff->buffer_width_f, buff->buffer_height_f, 0, 1);

    buf_dim.x() = std::abs(buf_dim.x());
    buf_dim.y() = std::abs(buf_dim.y());

    zoom_power_ = 0.0;

    if (canvas_width_ > buf_dim.x() && canvas_height_ > buf_dim.y()) {
        // Zoom in
        zoom_power_ += 1.0;
        float new_zoom = compute_zoom();

        // Iterate until buffer can fit inside the canvas
        while (canvas_width_ > new_zoom * buf_dim.x() &&
               canvas_height_ > new_zoom * buf_dim.y()) {
            zoom_power_ += 1.0;
            new_zoom = compute_zoom();
        }

        zoom_power_ -= 1.0;
    } else if (canvas_width_ < buf_dim.x() || canvas_height_ < buf_dim.y()) {
        // Zoom out
        zoom_power_ -= 1.0;
        float new_zoom = compute_zoom();

        // Iterate until buffer can fit inside the canvas
        while (canvas_width_ < new_zoom * buf_dim.x() ||
               canvas_height_ < new_zoom * buf_dim.y()) {
            zoom_power_ -= 1.0;
            new_zoom = compute_zoom();
        }
    }

    float zoom = 1.f / compute_zoom();
    scale_     = mat4::scale(vec4(zoom, zoom, 1.0, 1.0));
}

float Camera::compute_zoom()
{
    return std::pow(zoom_factor, zoom_power_);
}

void Camera::recenter_camera()
{
    camera_pos_x_ = camera_pos_y_ = 0.0f;

    set_initial_zoom();
    update_object_pose();
}

void Camera::mouse_drag_event(int mouse_x, int mouse_y)
{
    // Mouse is down. Update camera_pos_x_/camera_pos_y_
    camera_pos_x_ += mouse_x;
    camera_pos_y_ += mouse_y;

    update_object_pose();
}

bool Camera::post_buffer_update()
{
    return true;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include "AST.h"
#include "Error.h"
#include "BuiltinFunctions.h"
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Transforms/IPO.h>

namespace po = boost::program_options;
namespace fs = boost::filesystem;
using namespace Helen;
using namespace std;

vector<string> includePaths;

int yyparse(AST*& ast);
extern FILE* yyin;

int main(int argc, char** argv)
{
    // Boost Program Options setup
    po::options_description desc("Compiler options");
    desc.add_options()
    ("help,h", "display this help")
    ("version,v", "display version")
    ("input-file", po::value<string>(), "input file")
    ("include-path,I", po::value<vector<string> >(), "path to include files")
    ;
    po::positional_options_description p;
    p.add("input-file", -1);
    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
    po::notify(vm);
    if (vm.count("help") || vm.empty()) {
        cout << desc << "\n";
        return 1;
    }
    if(vm.count("include-path"))
        includePaths = vm["include-path"].as<vector<string> >();
    includePaths.push_back(".");
    //TODO: add stdlib dirs to include paths
    AST::module = llvm::make_unique<Module>("__helenmodule__", getGlobalContext());
    AST::fpm = llvm::make_unique<legacy::FunctionPassManager>(AST::module.get());
    AST::dataLayout = llvm::make_unique<DataLayout>(AST::module.get());
    AST::fpm->add(createBasicAliasAnalysisPass());
    AST::fpm->add(createPromoteMemoryToRegisterPass());
    AST::fpm->add(createInstructionCombiningPass());
    AST::fpm->add(createReassociatePass());
    AST::fpm->add(createGVNPass());
    AST::fpm->add(createCFGSimplificationPass());
    //AST::fpm->add(createFunctionInliningPass());
    legacy::PassManager pm;
    pm.add(createAlwaysInlinerPass());
    AST::fpm->doInitialization();
    yyin = fopen(vm["input-file"].as<string>().c_str(), "r");
    AST::isMainModule = false;
    AST* result;
    yyparse(result);
    if (Error::errorFlag) { // don't even try to compile if syntax errors present
        fprintf(stderr, "Fatal errors detected: translation terminated\n");
        return 1;
    }
    BuiltinFunctions::createMainFunction(AST::isMainModule);
    result->codegen();
    if (Error::errorFlag) {
        fprintf(stderr, "Fatal errors detected: translation terminated\n");
        return 1;
    }
    AST::builder.CreateRet(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0));
    pm.run(*(AST::module.get()));
    AST::module->dump();
    //TODO: Add output file to program options
    string filename = (argc >= 3) ? argv[2] : (argv[1] + string(".bc"));
    std::error_code ec;
    raw_fd_ostream fdos(filename, ec, sys::fs::OpenFlags::F_None);
    WriteBitcodeToFile(AST::module.get(), fdos);
    return 0;
}
<commit_msg>from Linux<commit_after>#include <stdio.h>
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include "AST.h"
#include "Error.h"
#include "BuiltinFunctions.h"
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Transforms/IPO.h>

namespace po = boost::program_options;
namespace fs = boost::filesystem;
using namespace Helen;
using namespace std;

vector<string> includePaths;

int yyparse(AST*& ast);
extern FILE* yyin;

string checkPath()
{

}

int main(int argc, char** argv)
{
    // Boost Program Options setup
    po::options_description desc("Compiler options");
    desc.add_options()
    ("help,h", "display this help")
    ("version,v", "display version")
    ("input-file", po::value<string>(), "input file")
    ("include-path,I", po::value<vector<string> >(), "path to include files")
    ;
    po::positional_options_description p;
    p.add("input-file", -1);
    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
    po::notify(vm);
    if (vm.count("help") || vm.empty()) {
        cout << desc << "\n";
        return 1;
    }
    if(vm.count("include-path"))
        includePaths = vm["include-path"].as<vector<string> >();
    includePaths.push_back(".");
    //TODO: add stdlib dirs to include paths
    AST::module = llvm::make_unique<Module>("__helenmodule__", getGlobalContext());
    AST::fpm = llvm::make_unique<legacy::FunctionPassManager>(AST::module.get());
    AST::dataLayout = llvm::make_unique<DataLayout>(AST::module.get());
    AST::fpm->add(createBasicAliasAnalysisPass());
    AST::fpm->add(createPromoteMemoryToRegisterPass());
    AST::fpm->add(createInstructionCombiningPass());
    AST::fpm->add(createReassociatePass());
    AST::fpm->add(createGVNPass());
    AST::fpm->add(createCFGSimplificationPass());
    //AST::fpm->add(createFunctionInliningPass());
    legacy::PassManager pm;
    pm.add(createAlwaysInlinerPass());
    AST::fpm->doInitialization();
    yyin = fopen(vm["input-file"].as<string>().c_str(), "r");
    AST::isMainModule = false;
    AST* result;
    yyparse(result);
    if (Error::errorFlag) { // don't even try to compile if syntax errors present
        fprintf(stderr, "Fatal errors detected: translation terminated\n");
        return 1;
    }
    BuiltinFunctions::createMainFunction(AST::isMainModule);
    result->codegen();
    if (Error::errorFlag) {
        fprintf(stderr, "Fatal errors detected: translation terminated\n");
        return 1;
    }
    AST::builder.CreateRet(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0));
    pm.run(*(AST::module.get()));
    AST::module->dump();
    //TODO: Add output file to program options
    string filename = (argc >= 3) ? argv[2] : (argv[1] + string(".bc"));
    std::error_code ec;
    raw_fd_ostream fdos(filename, ec, sys::fs::OpenFlags::F_None);
    WriteBitcodeToFile(AST::module.get(), fdos);
    return 0;
}
<|endoftext|>