<commit_before>#include "urtupdater.h"
#include "ui_urtupdater.h"

UrTUpdater::UrTUpdater(QWidget *parent) : QMainWindow(parent), ui(new Ui::UrTUpdater)
{
    ui->setupUi(this);

    updaterVersion = "4.0.1";

    QMenu *menuFile = menuBar()->addMenu("&File");
    QMenu *menuHelp = menuBar()->addMenu("&Help");

    QAction *actionDlServer = menuFile->addAction("&Download Server Selection");
    connect(actionDlServer, SIGNAL(triggered()), this, SLOT(serverSelection()));

    QAction *actionChangelog = menuFile->addAction("&Changelog");
    //connect(actionChangelog, SIGNAL(triggered()), this, SLOT(openChangelogPage()));

    QAction *actionAbout = menuHelp->addAction("&About");
    //connect(actionAbout, SIGNAL(triggered()), this, SLOT(openAboutPage()));

    QAction *actionHelp = menuHelp->addAction("&Get help");
    //connect(actionHelp, SIGNAL(triggered()), this, SLOT(openHelpPage()));
    actionHelp->setShortcut(QKeySequence("Ctrl+H"));

    QAction *actionQuitter = menuFile->addAction("&Quit");
    connect(actionQuitter, SIGNAL(triggered()), this, SLOT(quit()));
    actionQuitter->setShortcut(QKeySequence("Ctrl+Q"));

    init();
}

UrTUpdater::~UrTUpdater()
{
    delete ui;
}

void UrTUpdater::init(){

    updaterPath = getCurrentPath();

    // Check if this is the first launch of the updater
    if(!QFile::exists(updaterPath + URT_GAME_SUBDIR)){
        QMessageBox msg;
        int result;

        msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
        msg.setIcon(QMessageBox::Information);
        msg.setText("The game " URT_GAME_NAME " will be installed in this path:\n" + updaterPath + "\n"
                    + "To change the installation path, please click on \"Cancel\" and copy this Updater to where you want it to be installed.");
        result = msg.exec();

        // If we want to quit
        if(result == QMessageBox::Cancel){
            quit();
        }

        // Create the game folder
        else {
            if(!QDir().mkdir(updaterPath + URT_GAME_SUBDIR)){
                QMessageBox::critical(this, QString(URT_GAME_SUBDIR) + " folder", "Could not create the game folder (" + updaterPath + URT_GAME_SUBDIR + ").\n"
                                      + "Please move the updater to a folder where it has sufficient permissions.");
                quit();
            }
        }
    }

    getManifest("versionInfo");
}

QString UrTUpdater::getPlatform()
{
    #ifdef Q_OS_MAC
    return "Mac";
    #endif

    #ifdef Q_OS_LINUX
    return "Linux";
    #endif

    #ifdef Q_OS_WIN32
    return "Windows";
    #endif

    return "Linux";
}

QString UrTUpdater::getCurrentPath(){
    QDir dir = QDir(QCoreApplication::applicationDirPath());

    // If we're on Mac, 'dir' will contain the path to the executable
    // inside of the Updater's bundle which isn't what we want.
    // We need to cd ../../..
    if(getPlatform() == "Mac"){
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
    }

    return dir.absolutePath() + "/";
}

void UrTUpdater::getManifest(QString query){
    QUrl APIUrl(URT_API_LINK);
    QUrlQuery url;
    QNetworkRequest apiRequest(APIUrl);
    QNetworkAccessManager *apiManager = new QNetworkAccessManager(this);

    qDebug() << "query: " << query << endl;

    apiRequest.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded;charset=utf-8");
    url.addQueryItem("platform", getPlatform());
    url.addQueryItem("query", query);

    apiAnswer = apiManager->post(apiRequest, url.query(QUrl::FullyEncoded).toUtf8());
    connect(apiAnswer, SIGNAL(finished()), this, SLOT(parseAPIAnswer()));
    connect(apiAnswer, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError)));
}

void UrTUpdater::parseAPIAnswer(){
    QByteArray  apiByteAnswer = apiAnswer->readAll();
    QString     apiData = QString(apiByteAnswer);

    qDebug() << "apiData: " << apiData << endl;

    parseDOM(apiData);
}

void UrTUpdater::parseDOM(QString data){
    QDomDocument* dom = new QDomDocument();
    dom->setContent(data);

    QDomNode node = dom->firstChild();

    while(!node.isNull()){
        if(node.toElement().nodeName() == "Updater"){
            QDomNode updater = node.firstChild();

            while(!updater.isNull()){

                if(updater.toElement().nodeName() == "VersionInfo"){
                    QDomNode versionInfo = updater.firstChild();

                    while(!versionInfo.isNull()){
                        if(versionInfo.nodeName() == "VersionNumber"){
                            versionNumber = versionInfo.toElement().text();
                        }
                        if(versionInfo.nodeName() == "ReleaseDate"){
                            releaseDate = versionInfo.toElement().text();
                        }
                        versionInfo = versionInfo.nextSibling();
                    }
                }

                else if(updater.toElement().nodeName() == "ServerList"){
                    QDomNode serverListNode = updater.firstChild();

                    while(!serverListNode.isNull()){
                        if(serverListNode.nodeName() == "Server"){
                            QDomNode serverNode = serverListNode.firstChild();
                            QString serverURL;
                            QString serverName;
                            QString serverLocation;
                            serverInfo_s si;

                            while(!serverNode.isNull()){
                                if(serverNode.nodeName() == "ServerName"){
                                    serverName = serverNode.toElement().text();
                                }
                                if(serverNode.nodeName() == "ServerURL"){
                                    serverURL = serverNode.toElement().text();
                                }
                                if(serverNode.nodeName() == "ServerLocation"){
                                    serverLocation = serverNode.toElement().text();
                                }
                                serverNode = serverNode.nextSibling();
                            }

                            si.serverName = serverName;
                            si.serverURL = serverURL;
                            si.serverLocation = serverLocation;
                            downloadServers.append(si);
                        }
                        serverListNode = serverListNode.nextSibling();
                    }
                }

                else if(updater.toElement().nodeName() == "Files"){
                    QDomNode files = updater.firstChild();

                    while(!files.isNull()){
                        if(files.nodeName() == "File"){
                            QDomNode fileInfo = files.firstChild();
                            QString fileDir;
                            QString fileName;
                            QString fileMd5;
                            QString fileSize;
                            bool mustDownload = false;

                            while(!fileInfo.isNull()){
                                if(fileInfo.nodeName() == "FileDir"){
                                    fileDir = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileName"){
                                    fileName = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileMD5"){
                                    fileMd5 = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileSize"){
                                    fileSize = fileInfo.toElement().text();
                                }
                                fileInfo = fileInfo.nextSibling();
                            }

                            QString filePath(updaterPath + fileDir + fileName);
                            QFile* f = new QFile(filePath);

                            // If the file does not exist, it must be downloaded.
                            if(!f->exists()){
                                mustDownload = true;
                            }

                            // If the md5 string is empty, it means that the API wants
                            // us to delete this file if needed
                            else if(!fileName.isEmpty() && fileMd5.isEmpty()){
                                QFile::remove(filePath);
                            }

                            // Check the file's md5sum to see if it needs to be updated.
                            else if(!fileName.isEmpty() && !fileMd5.isEmpty()){
                                if(getMd5Sum(f) != fileMd5){
                                    mustDownload = true;
                                    qDebug() << "md5 file: " << getMd5Sum(f) << ", " << fileMd5 << endl;
                                }
                            }

                            qDebug() << "fileDir:" << fileDir << endl;
                            qDebug() << "fileName: " << fileName << endl;
                            qDebug() << "fileMd5: " << fileMd5 << endl;

                            if(mustDownload){
                                fileInfo_s fi;
                                fi.fileName = fileName;
                                fi.filePath = fileDir;
                                fi.fileMd5 = fileMd5;
                                fi.fileSize = fileSize;
                                filesToDownload.append(fi);
                            }

                            delete f;
                        }
                        files = files.nextSibling();
                    }
                }
                updater = updater.nextSibling();
            }
        }
        node = node.nextSibling();
    }

    delete dom;
}

QString UrTUpdater::getMd5Sum(QFile* file)
{
    if (file->exists() && file->open(QIODevice::ReadOnly))
    {
        QByteArray content = file->readAll();
        QByteArray hashed = QCryptographicHash::hash(content, QCryptographicHash::Md5);
        file->close();

        return hashed.toHex().data();
    }

    return "";
}

void UrTUpdater::networkError(QNetworkReply::NetworkError code){
    QString error = "";
    bool critical = false;

    switch(code){
        case QNetworkReply::ConnectionRefusedError:
            error = "Error: the remote server refused the connection. Please try again later.";
            critical = true;
            break;

        case QNetworkReply::RemoteHostClosedError:
            error = "Error: the remote server closed the connection prematurely. Please try again later.";
            break;

        case QNetworkReply::HostNotFoundError:
            error = "Error: the remote server could not be found. Please check your internet connection!";
            critical = true;
            break;

        case QNetworkReply::TimeoutError:
            error = "Error: the connection to the remote server timed out. Please try again.";
            break;

        case QNetworkReply::TemporaryNetworkFailureError:
            error = "Error: the connection to the remote server was broken due to disconnection from the network. Please try again.";
            break;

        case QNetworkReply::ContentNotFoundError:
            error = "Error: the remote content could not be found. Please report this issue on our website: http://www.urbanterror.info";
            break;

        case QNetworkReply::UnknownNetworkError:
            error = "Error: an unknown network-related error was encountered. Please try again";
            break;

        case QNetworkReply::UnknownContentError:
            error = "Error: an unknown content-related error was encountered. Please try again";
            break;

        default:
        case QNetworkReply::NoError:
            break;
    }

    if(!error.isEmpty()){
        if(critical == true){
            QMessageBox::critical(0, "Download error", error);
            quit();
        }
        else {
            QMessageBox::information(0, "Download error", error);
        }
    }
}

void UrTUpdater::serverSelection(){
    ServerSelection *serverSel = new ServerSelection(this);

    serverSel->downloadServers = downloadServers;
    serverSel->init();
    serverSel->exec();
}

void UrTUpdater::engineSelection(){
    EngineSelection* engineSel = new EngineSelection(this);
    engineSel->exec();
}

void UrTUpdater::quit(){
    exit(0);
}
<commit_msg>When closing the app, force the usage of our quit() function<commit_after>#include "urtupdater.h"
#include "ui_urtupdater.h"

UrTUpdater::UrTUpdater(QWidget *parent) : QMainWindow(parent), ui(new Ui::UrTUpdater)
{
    ui->setupUi(this);

    updaterVersion = "4.0.1";

    QMenu *menuFile = menuBar()->addMenu("&File");
    QMenu *menuHelp = menuBar()->addMenu("&Help");

    QAction *actionDlServer = menuFile->addAction("&Download Server Selection");
    connect(actionDlServer, SIGNAL(triggered()), this, SLOT(serverSelection()));

    QAction *actionChangelog = menuFile->addAction("&Changelog");
    //connect(actionChangelog, SIGNAL(triggered()), this, SLOT(openChangelogPage()));

    QAction *actionAbout = menuHelp->addAction("&About");
    //connect(actionAbout, SIGNAL(triggered()), this, SLOT(openAboutPage()));

    QAction *actionHelp = menuHelp->addAction("&Get help");
    //connect(actionHelp, SIGNAL(triggered()), this, SLOT(openHelpPage()));
    actionHelp->setShortcut(QKeySequence("Ctrl+H"));

    QAction *actionQuitter = menuFile->addAction("&Quit");
    connect(actionQuitter, SIGNAL(triggered()), this, SLOT(quit()));
    actionQuitter->setShortcut(QKeySequence("Ctrl+Q"));

    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit()));

    init();
}

UrTUpdater::~UrTUpdater()
{
    delete ui;
}

void UrTUpdater::init(){

    updaterPath = getCurrentPath();

    // Check if this is the first launch of the updater
    if(!QFile::exists(updaterPath + URT_GAME_SUBDIR)){
        QMessageBox msg;
        int result;

        msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
        msg.setIcon(QMessageBox::Information);
        msg.setText("The game " URT_GAME_NAME " will be installed in this path:\n" + updaterPath + "\n"
                    + "To change the installation path, please click on \"Cancel\" and copy this Updater to where you want it to be installed.");
        result = msg.exec();

        // If we want to quit
        if(result == QMessageBox::Cancel){
            quit();
        }

        // Create the game folder
        else {
            if(!QDir().mkdir(updaterPath + URT_GAME_SUBDIR)){
                QMessageBox::critical(this, QString(URT_GAME_SUBDIR) + " folder", "Could not create the game folder (" + updaterPath + URT_GAME_SUBDIR + ").\n"
                                      + "Please move the updater to a folder where it has sufficient permissions.");
                quit();
            }
        }
    }

    getManifest("versionInfo");
}

QString UrTUpdater::getPlatform()
{
    #ifdef Q_OS_MAC
    return "Mac";
    #endif

    #ifdef Q_OS_LINUX
    return "Linux";
    #endif

    #ifdef Q_OS_WIN32
    return "Windows";
    #endif

    return "Linux";
}

QString UrTUpdater::getCurrentPath(){
    QDir dir = QDir(QCoreApplication::applicationDirPath());

    // If we're on Mac, 'dir' will contain the path to the executable
    // inside of the Updater's bundle which isn't what we want.
    // We need to cd ../../..
    if(getPlatform() == "Mac"){
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
    }

    return dir.absolutePath() + "/";
}

void UrTUpdater::getManifest(QString query){
    QUrl APIUrl(URT_API_LINK);
    QUrlQuery url;
    QNetworkRequest apiRequest(APIUrl);
    QNetworkAccessManager *apiManager = new QNetworkAccessManager(this);

    qDebug() << "query: " << query << endl;

    apiRequest.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded;charset=utf-8");
    url.addQueryItem("platform", getPlatform());
    url.addQueryItem("query", query);

    apiAnswer = apiManager->post(apiRequest, url.query(QUrl::FullyEncoded).toUtf8());
    connect(apiAnswer, SIGNAL(finished()), this, SLOT(parseAPIAnswer()));
    connect(apiAnswer, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError)));
}

void UrTUpdater::parseAPIAnswer(){
    QByteArray  apiByteAnswer = apiAnswer->readAll();
    QString     apiData = QString(apiByteAnswer);

    qDebug() << "apiData: " << apiData << endl;

    parseDOM(apiData);
}

void UrTUpdater::parseDOM(QString data){
    QDomDocument* dom = new QDomDocument();
    dom->setContent(data);

    QDomNode node = dom->firstChild();

    while(!node.isNull()){
        if(node.toElement().nodeName() == "Updater"){
            QDomNode updater = node.firstChild();

            while(!updater.isNull()){

                if(updater.toElement().nodeName() == "VersionInfo"){
                    QDomNode versionInfo = updater.firstChild();

                    while(!versionInfo.isNull()){
                        if(versionInfo.nodeName() == "VersionNumber"){
                            versionNumber = versionInfo.toElement().text();
                        }
                        if(versionInfo.nodeName() == "ReleaseDate"){
                            releaseDate = versionInfo.toElement().text();
                        }
                        versionInfo = versionInfo.nextSibling();
                    }
                }

                else if(updater.toElement().nodeName() == "ServerList"){
                    QDomNode serverListNode = updater.firstChild();

                    while(!serverListNode.isNull()){
                        if(serverListNode.nodeName() == "Server"){
                            QDomNode serverNode = serverListNode.firstChild();
                            QString serverURL;
                            QString serverName;
                            QString serverLocation;
                            serverInfo_s si;

                            while(!serverNode.isNull()){
                                if(serverNode.nodeName() == "ServerName"){
                                    serverName = serverNode.toElement().text();
                                }
                                if(serverNode.nodeName() == "ServerURL"){
                                    serverURL = serverNode.toElement().text();
                                }
                                if(serverNode.nodeName() == "ServerLocation"){
                                    serverLocation = serverNode.toElement().text();
                                }
                                serverNode = serverNode.nextSibling();
                            }

                            si.serverName = serverName;
                            si.serverURL = serverURL;
                            si.serverLocation = serverLocation;
                            downloadServers.append(si);
                        }
                        serverListNode = serverListNode.nextSibling();
                    }
                }

                else if(updater.toElement().nodeName() == "Files"){
                    QDomNode files = updater.firstChild();

                    while(!files.isNull()){
                        if(files.nodeName() == "File"){
                            QDomNode fileInfo = files.firstChild();
                            QString fileDir;
                            QString fileName;
                            QString fileMd5;
                            QString fileSize;
                            bool mustDownload = false;

                            while(!fileInfo.isNull()){
                                if(fileInfo.nodeName() == "FileDir"){
                                    fileDir = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileName"){
                                    fileName = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileMD5"){
                                    fileMd5 = fileInfo.toElement().text();
                                }
                                if(fileInfo.nodeName() == "FileSize"){
                                    fileSize = fileInfo.toElement().text();
                                }
                                fileInfo = fileInfo.nextSibling();
                            }

                            QString filePath(updaterPath + fileDir + fileName);
                            QFile* f = new QFile(filePath);

                            // If the file does not exist, it must be downloaded.
                            if(!f->exists()){
                                mustDownload = true;
                            }

                            // If the md5 string is empty, it means that the API wants
                            // us to delete this file if needed
                            else if(!fileName.isEmpty() && fileMd5.isEmpty()){
                                QFile::remove(filePath);
                            }

                            // Check the file's md5sum to see if it needs to be updated.
                            else if(!fileName.isEmpty() && !fileMd5.isEmpty()){
                                if(getMd5Sum(f) != fileMd5){
                                    mustDownload = true;
                                    qDebug() << "md5 file: " << getMd5Sum(f) << ", " << fileMd5 << endl;
                                }
                            }

                            qDebug() << "fileDir:" << fileDir << endl;
                            qDebug() << "fileName: " << fileName << endl;
                            qDebug() << "fileMd5: " << fileMd5 << endl;

                            if(mustDownload){
                                fileInfo_s fi;
                                fi.fileName = fileName;
                                fi.filePath = fileDir;
                                fi.fileMd5 = fileMd5;
                                fi.fileSize = fileSize;
                                filesToDownload.append(fi);
                            }

                            delete f;
                        }
                        files = files.nextSibling();
                    }
                }
                updater = updater.nextSibling();
            }
        }
        node = node.nextSibling();
    }

    delete dom;
}

QString UrTUpdater::getMd5Sum(QFile* file)
{
    if (file->exists() && file->open(QIODevice::ReadOnly))
    {
        QByteArray content = file->readAll();
        QByteArray hashed = QCryptographicHash::hash(content, QCryptographicHash::Md5);
        file->close();

        return hashed.toHex().data();
    }

    return "";
}

void UrTUpdater::networkError(QNetworkReply::NetworkError code){
    QString error = "";
    bool critical = false;

    switch(code){
        case QNetworkReply::ConnectionRefusedError:
            error = "Error: the remote server refused the connection. Please try again later.";
            critical = true;
            break;

        case QNetworkReply::RemoteHostClosedError:
            error = "Error: the remote server closed the connection prematurely. Please try again later.";
            break;

        case QNetworkReply::HostNotFoundError:
            error = "Error: the remote server could not be found. Please check your internet connection!";
            critical = true;
            break;

        case QNetworkReply::TimeoutError:
            error = "Error: the connection to the remote server timed out. Please try again.";
            break;

        case QNetworkReply::TemporaryNetworkFailureError:
            error = "Error: the connection to the remote server was broken due to disconnection from the network. Please try again.";
            break;

        case QNetworkReply::ContentNotFoundError:
            error = "Error: the remote content could not be found. Please report this issue on our website: http://www.urbanterror.info";
            break;

        case QNetworkReply::UnknownNetworkError:
            error = "Error: an unknown network-related error was encountered. Please try again";
            break;

        case QNetworkReply::UnknownContentError:
            error = "Error: an unknown content-related error was encountered. Please try again";
            break;

        default:
        case QNetworkReply::NoError:
            break;
    }

    if(!error.isEmpty()){
        if(critical == true){
            QMessageBox::critical(0, "Download error", error);
            quit();
        }
        else {
            QMessageBox::information(0, "Download error", error);
        }
    }
}

void UrTUpdater::serverSelection(){
    ServerSelection *serverSel = new ServerSelection(this);

    serverSel->downloadServers = downloadServers;
    serverSel->init();
    serverSel->exec();
}

void UrTUpdater::engineSelection(){
    EngineSelection* engineSel = new EngineSelection(this);
    engineSel->exec();
}

void UrTUpdater::quit(){
    exit(0);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
 *
 * 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 "ModelImporter.hpp"
#include "toposort.hpp"
#include "onnx_utils.hpp"
#include "onnx2trt_utils.hpp"

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>

#include <limits>

namespace onnx2trt {

//Status const& ModelImporter::setInput(const char* name, nvinfer1::ITensor* input) {
//  _importer_ctx.setUserInput(name, input);
//  _last_error = Status::success();
//  return _last_error;
//}
//
//Status const& ModelImporter::setOutput(const char* name, nvinfer1::ITensor** output) {
//  _importer_ctx.setUserOutput(name, output);
//  _last_error = Status::success();
//  return _last_error;
//}

Status importInput(ImporterContext* importer_ctx,
                   ::ONNX_NAMESPACE::ValueInfoProto const& input,
                   nvinfer1::ITensor** tensor) {
  auto const& onnx_tensor_type = input.type().tensor_type();
  nvinfer1::DataType trt_dtype;
  ASSERT(convert_dtype(onnx_tensor_type.elem_type(), &trt_dtype),
         ErrorCode::kUNSUPPORTED_NODE);
  ASSERT(onnx_tensor_type.shape().dim().size() > 0,
         ErrorCode::kUNSUPPORTED_NODE);
  auto trt_dims = convert_dims(onnx_tensor_type.shape().dim());
  nvinfer1::ITensor* user_input = importer_ctx->getUserInput(input.name().c_str());
  if( user_input ) {
    ASSERT(user_input, ErrorCode::kINVALID_VALUE);
    // Note: We intentionally don't check dimensions/dtype here so that users
    //       can change the input shape/type if they want to.
    //ASSERT(trt_dims  == user_input->getDimensions(), ErrorCode::kINVALID_VALUE);
    //ASSERT(trt_dtype == user_input->getType(),       ErrorCode::kINVALID_VALUE);
    *tensor = user_input;
    return Status::success();
  }
  // WAR for TRT not supporting < 3 input dims
  for( int i=trt_dims.nbDims; i<3; ++i ) {
    // Pad with unitary dims
    ++trt_dims.nbDims;
    trt_dims.d[i] = 1;
    trt_dims.type[i] = (i == 0 ?
                        nvinfer1::DimensionType::kCHANNEL :
                        nvinfer1::DimensionType::kSPATIAL);
  }
  ASSERT(trt_dims.nbDims <= 3, ErrorCode::kUNSUPPORTED_NODE);
  ASSERT(*tensor = importer_ctx->network()->addInput(
           input.name().c_str(), trt_dtype, trt_dims),
         ErrorCode::kUNSUPPORTED_NODE);
  return Status::success();
}

Status importInputs(ImporterContext* importer_ctx,
                    ::ONNX_NAMESPACE::GraphProto const& graph,
                    string_map<TensorOrWeights>* tensors) {
  string_map<::ONNX_NAMESPACE::TensorProto const*> initializer_map;
  for( ::ONNX_NAMESPACE::TensorProto const& initializer : graph.initializer() ) {
    ASSERT(!initializer_map.count(initializer.name()), ErrorCode::kINVALID_GRAPH);
    initializer_map.insert({initializer.name(), &initializer});
  }
  for( ::ONNX_NAMESPACE::ValueInfoProto const& input : graph.input() ) {
    TensorOrWeights tensor;
    if( initializer_map.count(input.name()) ) {
      ::ONNX_NAMESPACE::TensorProto const& initializer = *initializer_map.at(input.name());
      ShapedWeights weights;
      ASSERT(convert_onnx_weights(initializer, &weights),
             ErrorCode::kUNSUPPORTED_NODE);
      tensor = weights;
    } else {
      nvinfer1::ITensor* tensor_ptr;
      TRT_CHECK(importInput(importer_ctx, input, &tensor_ptr));
      tensor = tensor_ptr;
    }
    ASSERT(!tensors->count(input.name()), ErrorCode::kINVALID_GRAPH);
    tensors->insert({input.name(), tensor});

  }
  return Status::success();
}

NodeImportResult ModelImporter::importNode(::ONNX_NAMESPACE::NodeProto const& node,
                                           std::vector<TensorOrWeights>& inputs) {
  if( !_op_importers.count(node.op_type()) ) {
    return MAKE_ERROR("No importer registered for op: " + node.op_type(),
                      ErrorCode::kUNSUPPORTED_NODE);
  }
  NodeImporter const& node_importer = _op_importers.at(node.op_type());

  std::vector<TensorOrWeights> outputs;
  GET_VALUE(node_importer(&_importer_ctx, node, inputs), &outputs);
  ASSERT(outputs.size() <= (size_t)node.output().size(), ErrorCode::kINTERNAL_ERROR);
  for( size_t i=0; i<outputs.size(); ++i ) {
    std::string node_output_name = node.output(i);
    TensorOrWeights& output = outputs.at(i);
    if( output ) {
      if( output.is_tensor() ) {
        output.tensor().setName(node_output_name.c_str());
      }
      //// TODO: Remove when done testing
      //cout << "Imported " << node.op_type()
      //     << " output tensor '" << node_output_name;
      //if( output.is_tensor() ) {
      //  cout << "' (" << output.tensor().getType()  << ")";
      //}
      //cout << endl;
    }
  }
  return outputs;
}

Status deserialize_onnx_model(void const* serialized_onnx_model,
                              size_t      serialized_onnx_model_size,
                              bool is_serialized_as_text,
                              ::ONNX_NAMESPACE::ModelProto* model) {
  google::protobuf::io::ArrayInputStream raw_input(serialized_onnx_model,
                                                   serialized_onnx_model_size);
  if( is_serialized_as_text ) {
    ASSERT(google::protobuf::TextFormat::Parse(&raw_input, model),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  } else {
    google::protobuf::io::CodedInputStream coded_input(&raw_input);
    // Note: This WARs the very low default size limit (64MB)
    coded_input.SetTotalBytesLimit(std::numeric_limits<int>::max(),
                                   std::numeric_limits<int>::max() / 4);
    ASSERT(model->ParseFromCodedStream(&coded_input),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  }
  return Status::success();
}

Status deserialize_onnx_model(int fd,
                              bool is_serialized_as_text,
                              ::ONNX_NAMESPACE::ModelProto* model) {
  google::protobuf::io::FileInputStream raw_input(fd);
  if( is_serialized_as_text ) {
    ASSERT(google::protobuf::TextFormat::Parse(&raw_input, model),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  } else {
    google::protobuf::io::CodedInputStream coded_input(&raw_input);
    // Note: This WARs the very low default size limit (64MB)
    coded_input.SetTotalBytesLimit(std::numeric_limits<int>::max(),
                                   std::numeric_limits<int>::max()/4);
    ASSERT(model->ParseFromCodedStream(&coded_input),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  }
  return Status::success();
}

bool ModelImporter::supportsOperator(const char* op_name) const {
  return _op_importers.count(op_name);
}

bool ModelImporter::parse(void const *serialized_onnx_model,
                          size_t serialized_onnx_model_size) {
  _current_node = -1;
  // TODO: This function (and its overload below) could do with some cleaning,
  //       particularly wrt error handling.
  // Note: We store a copy of the model so that weight arrays will persist
  _onnx_models.emplace_back();
  ::ONNX_NAMESPACE::ModelProto& model = _onnx_models.back();
  bool is_serialized_as_text = false;
  Status status = deserialize_onnx_model(
      serialized_onnx_model, serialized_onnx_model_size,
      is_serialized_as_text, &model);
  if( status.is_error() ) {
    _errors.push_back(status);
    return false;
  }
  status = this->importModel(model);
  if( status.is_error() ) {
    status.setNode(_current_node);
    _errors.push_back(status);
    return false;
  }
  return true;
}

Status ModelImporter::importModel(::ONNX_NAMESPACE::ModelProto const& model) {
  // TODO: Remove when done testing
  //cout << "------Begin model " << model.graph().name() << "-------" << endl;
  //cout << model << endl;
  //cout << "------End model-------" << endl;
  _importer_ctx.clearOpsets();
  for( int i=0; i<model.opset_import().size(); ++i ) {
    std::string domain  = model.opset_import(i).domain();
    int64_t     version = model.opset_import(i).version();
    _importer_ctx.addOpset(domain, version);
  }
  ::ONNX_NAMESPACE::GraphProto const& graph = model.graph();
  string_map<TensorOrWeights> tensors;
  TRT_CHECK(importInputs(&_importer_ctx, graph, &tensors));
  std::vector<size_t> topological_order;
  ASSERT(toposort(graph.node(), &topological_order), ErrorCode::kINVALID_GRAPH);
  for( size_t node_idx : topological_order ) {
    _current_node = node_idx;
    ::ONNX_NAMESPACE::NodeProto const& node = graph.node(node_idx);
    std::vector<TensorOrWeights> inputs;
    for( auto const& input_name : node.input() ) {
      ASSERT(tensors.count(input_name), ErrorCode::kINVALID_GRAPH);
      inputs.push_back(tensors.at(input_name));
    }
    std::vector<TensorOrWeights> outputs;
    GET_VALUE(this->importNode(node, inputs), &outputs);
    for( size_t i=0; i<outputs.size(); ++i ) {
      std::string node_output_name = node.output(i);
      TensorOrWeights& output = outputs.at(i);
      // Note: This condition is to allow ONNX outputs to be ignored
      if( output ) {
        ASSERT(!tensors.count(node_output_name), ErrorCode::kINVALID_GRAPH);

        tensors.insert({node_output_name, output});
      }
    }
    if( node.output().size() > 0 ) {
      std::stringstream ss;
      ss << node.output(0) << ":"
         << node.op_type() << " -> "
         << outputs.at(0).shape();
      _importer_ctx.logger().log(
          nvinfer1::ILogger::Severity::kINFO, ss.str().c_str());
    }
  }
  _current_node = -1;
  // Mark outputs defined in the ONNX model (unless tensors are user-requested)
  for( ::ONNX_NAMESPACE::ValueInfoProto const& output : graph.output() ) {
    ASSERT(tensors.count(output.name()), ErrorCode::kINVALID_GRAPH);
    ASSERT(tensors.at(output.name()).is_tensor(), ErrorCode::kUNSUPPORTED_GRAPH);
    nvinfer1::ITensor* output_tensor_ptr = &tensors.at(output.name()).tensor();
    if( output_tensor_ptr->isNetworkInput() ) {
      // HACK WAR for TRT not allowing input == output
      // TODO: Does this break things by changing the name of the input tensor?
      output_tensor_ptr->setName(("__" + output.name()).c_str());
      output_tensor_ptr = &identity(&_importer_ctx, output_tensor_ptr).tensor();
      ASSERT(output_tensor_ptr, ErrorCode::kUNSUPPORTED_NODE);
      output_tensor_ptr->setName(output.name().c_str());
    }
    nvinfer1::ITensor** user_output = _importer_ctx.getUserOutput(output.name().c_str());
    if( !user_output ) {
      _importer_ctx.network()->markOutput(*output_tensor_ptr);
      nvinfer1::DataType trt_dtype;
      ASSERT(convert_dtype(output.type().tensor_type().elem_type(), &trt_dtype),
             ErrorCode::kUNSUPPORTED_NODE);
      // Note: Without this, output type is always float32
      output_tensor_ptr->setType(trt_dtype);
    }
  }
  // Return user-requested output tensors
  for( auto user_output_entry : _importer_ctx.getUserOutputs() ) {
    std::string         user_output_name = user_output_entry.first;
    nvinfer1::ITensor** user_output_ptr  = user_output_entry.second;
    ASSERT(tensors.count(user_output_name), ErrorCode::kINVALID_VALUE);
    TensorOrWeights user_output = tensors.at(user_output_name);
    ASSERT(user_output.is_tensor(), ErrorCode::kINVALID_VALUE);
    *user_output_ptr = &user_output.tensor();
  }
  return Status::success();
}

} // namespace onnx2trt
<commit_msg>TRT4: Assert that output type matches INT32 tensor<commit_after>/*
 * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
 *
 * 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 "ModelImporter.hpp"
#include "toposort.hpp"
#include "onnx_utils.hpp"
#include "onnx2trt_utils.hpp"

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>

#include <limits>

namespace onnx2trt {

//Status const& ModelImporter::setInput(const char* name, nvinfer1::ITensor* input) {
//  _importer_ctx.setUserInput(name, input);
//  _last_error = Status::success();
//  return _last_error;
//}
//
//Status const& ModelImporter::setOutput(const char* name, nvinfer1::ITensor** output) {
//  _importer_ctx.setUserOutput(name, output);
//  _last_error = Status::success();
//  return _last_error;
//}

Status importInput(ImporterContext* importer_ctx,
                   ::ONNX_NAMESPACE::ValueInfoProto const& input,
                   nvinfer1::ITensor** tensor) {
  auto const& onnx_tensor_type = input.type().tensor_type();
  nvinfer1::DataType trt_dtype;
  ASSERT(convert_dtype(onnx_tensor_type.elem_type(), &trt_dtype),
         ErrorCode::kUNSUPPORTED_NODE);
  ASSERT(onnx_tensor_type.shape().dim().size() > 0,
         ErrorCode::kUNSUPPORTED_NODE);
  auto trt_dims = convert_dims(onnx_tensor_type.shape().dim());
  nvinfer1::ITensor* user_input = importer_ctx->getUserInput(input.name().c_str());
  if( user_input ) {
    ASSERT(user_input, ErrorCode::kINVALID_VALUE);
    // Note: We intentionally don't check dimensions/dtype here so that users
    //       can change the input shape/type if they want to.
    //ASSERT(trt_dims  == user_input->getDimensions(), ErrorCode::kINVALID_VALUE);
    //ASSERT(trt_dtype == user_input->getType(),       ErrorCode::kINVALID_VALUE);
    *tensor = user_input;
    return Status::success();
  }
  // WAR for TRT not supporting < 3 input dims
  for( int i=trt_dims.nbDims; i<3; ++i ) {
    // Pad with unitary dims
    ++trt_dims.nbDims;
    trt_dims.d[i] = 1;
    trt_dims.type[i] = (i == 0 ?
                        nvinfer1::DimensionType::kCHANNEL :
                        nvinfer1::DimensionType::kSPATIAL);
  }
  ASSERT(trt_dims.nbDims <= 3, ErrorCode::kUNSUPPORTED_NODE);
  ASSERT(*tensor = importer_ctx->network()->addInput(
           input.name().c_str(), trt_dtype, trt_dims),
         ErrorCode::kUNSUPPORTED_NODE);
  return Status::success();
}

Status importInputs(ImporterContext* importer_ctx,
                    ::ONNX_NAMESPACE::GraphProto const& graph,
                    string_map<TensorOrWeights>* tensors) {
  string_map<::ONNX_NAMESPACE::TensorProto const*> initializer_map;
  for( ::ONNX_NAMESPACE::TensorProto const& initializer : graph.initializer() ) {
    ASSERT(!initializer_map.count(initializer.name()), ErrorCode::kINVALID_GRAPH);
    initializer_map.insert({initializer.name(), &initializer});
  }
  for( ::ONNX_NAMESPACE::ValueInfoProto const& input : graph.input() ) {
    TensorOrWeights tensor;
    if( initializer_map.count(input.name()) ) {
      ::ONNX_NAMESPACE::TensorProto const& initializer = *initializer_map.at(input.name());
      ShapedWeights weights;
      ASSERT(convert_onnx_weights(initializer, &weights),
             ErrorCode::kUNSUPPORTED_NODE);
      tensor = weights;
    } else {
      nvinfer1::ITensor* tensor_ptr;
      TRT_CHECK(importInput(importer_ctx, input, &tensor_ptr));
      tensor = tensor_ptr;
    }
    ASSERT(!tensors->count(input.name()), ErrorCode::kINVALID_GRAPH);
    tensors->insert({input.name(), tensor});

  }
  return Status::success();
}

NodeImportResult ModelImporter::importNode(::ONNX_NAMESPACE::NodeProto const& node,
                                           std::vector<TensorOrWeights>& inputs) {
  if( !_op_importers.count(node.op_type()) ) {
    return MAKE_ERROR("No importer registered for op: " + node.op_type(),
                      ErrorCode::kUNSUPPORTED_NODE);
  }
  NodeImporter const& node_importer = _op_importers.at(node.op_type());

  std::vector<TensorOrWeights> outputs;
  GET_VALUE(node_importer(&_importer_ctx, node, inputs), &outputs);
  ASSERT(outputs.size() <= (size_t)node.output().size(), ErrorCode::kINTERNAL_ERROR);
  for( size_t i=0; i<outputs.size(); ++i ) {
    std::string node_output_name = node.output(i);
    TensorOrWeights& output = outputs.at(i);
    if( output ) {
      if( output.is_tensor() ) {
        output.tensor().setName(node_output_name.c_str());
      }
      //// TODO: Remove when done testing
      //cout << "Imported " << node.op_type()
      //     << " output tensor '" << node_output_name;
      //if( output.is_tensor() ) {
      //  cout << "' (" << output.tensor().getType()  << ")";
      //}
      //cout << endl;
    }
  }
  return outputs;
}

Status deserialize_onnx_model(void const* serialized_onnx_model,
                              size_t      serialized_onnx_model_size,
                              bool is_serialized_as_text,
                              ::ONNX_NAMESPACE::ModelProto* model) {
  google::protobuf::io::ArrayInputStream raw_input(serialized_onnx_model,
                                                   serialized_onnx_model_size);
  if( is_serialized_as_text ) {
    ASSERT(google::protobuf::TextFormat::Parse(&raw_input, model),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  } else {
    google::protobuf::io::CodedInputStream coded_input(&raw_input);
    // Note: This WARs the very low default size limit (64MB)
    coded_input.SetTotalBytesLimit(std::numeric_limits<int>::max(),
                                   std::numeric_limits<int>::max() / 4);
    ASSERT(model->ParseFromCodedStream(&coded_input),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  }
  return Status::success();
}

Status deserialize_onnx_model(int fd,
                              bool is_serialized_as_text,
                              ::ONNX_NAMESPACE::ModelProto* model) {
  google::protobuf::io::FileInputStream raw_input(fd);
  if( is_serialized_as_text ) {
    ASSERT(google::protobuf::TextFormat::Parse(&raw_input, model),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  } else {
    google::protobuf::io::CodedInputStream coded_input(&raw_input);
    // Note: This WARs the very low default size limit (64MB)
    coded_input.SetTotalBytesLimit(std::numeric_limits<int>::max(),
                                   std::numeric_limits<int>::max()/4);
    ASSERT(model->ParseFromCodedStream(&coded_input),
           ErrorCode::kMODEL_DESERIALIZE_FAILED);
  }
  return Status::success();
}

bool ModelImporter::supportsOperator(const char* op_name) const {
  return _op_importers.count(op_name);
}

bool ModelImporter::parse(void const *serialized_onnx_model,
                          size_t serialized_onnx_model_size) {
  _current_node = -1;
  // TODO: This function (and its overload below) could do with some cleaning,
  //       particularly wrt error handling.
  // Note: We store a copy of the model so that weight arrays will persist
  _onnx_models.emplace_back();
  ::ONNX_NAMESPACE::ModelProto& model = _onnx_models.back();
  bool is_serialized_as_text = false;
  Status status = deserialize_onnx_model(
      serialized_onnx_model, serialized_onnx_model_size,
      is_serialized_as_text, &model);
  if( status.is_error() ) {
    _errors.push_back(status);
    return false;
  }
  status = this->importModel(model);
  if( status.is_error() ) {
    status.setNode(_current_node);
    _errors.push_back(status);
    return false;
  }
  return true;
}

Status ModelImporter::importModel(::ONNX_NAMESPACE::ModelProto const& model) {
  // TODO: Remove when done testing
  //cout << "------Begin model " << model.graph().name() << "-------" << endl;
  //cout << model << endl;
  //cout << "------End model-------" << endl;
  _importer_ctx.clearOpsets();
  for( int i=0; i<model.opset_import().size(); ++i ) {
    std::string domain  = model.opset_import(i).domain();
    int64_t     version = model.opset_import(i).version();
    _importer_ctx.addOpset(domain, version);
  }
  ::ONNX_NAMESPACE::GraphProto const& graph = model.graph();
  string_map<TensorOrWeights> tensors;
  TRT_CHECK(importInputs(&_importer_ctx, graph, &tensors));
  std::vector<size_t> topological_order;
  ASSERT(toposort(graph.node(), &topological_order), ErrorCode::kINVALID_GRAPH);
  for( size_t node_idx : topological_order ) {
    _current_node = node_idx;
    ::ONNX_NAMESPACE::NodeProto const& node = graph.node(node_idx);
    std::vector<TensorOrWeights> inputs;
    for( auto const& input_name : node.input() ) {
      ASSERT(tensors.count(input_name), ErrorCode::kINVALID_GRAPH);
      inputs.push_back(tensors.at(input_name));
    }
    std::vector<TensorOrWeights> outputs;
    GET_VALUE(this->importNode(node, inputs), &outputs);
    for( size_t i=0; i<outputs.size(); ++i ) {
      std::string node_output_name = node.output(i);
      TensorOrWeights& output = outputs.at(i);
      // Note: This condition is to allow ONNX outputs to be ignored
      if( output ) {
        ASSERT(!tensors.count(node_output_name), ErrorCode::kINVALID_GRAPH);

        tensors.insert({node_output_name, output});
      }
    }
    if( node.output().size() > 0 ) {
      std::stringstream ss;
      ss << node.output(0) << ":"
         << node.op_type() << " -> "
         << outputs.at(0).shape();
      _importer_ctx.logger().log(
          nvinfer1::ILogger::Severity::kINFO, ss.str().c_str());
    }
  }
  _current_node = -1;
  // Mark outputs defined in the ONNX model (unless tensors are user-requested)
  for( ::ONNX_NAMESPACE::ValueInfoProto const& output : graph.output() ) {
    ASSERT(tensors.count(output.name()), ErrorCode::kINVALID_GRAPH);
    ASSERT(tensors.at(output.name()).is_tensor(), ErrorCode::kUNSUPPORTED_GRAPH);
    nvinfer1::ITensor* output_tensor_ptr = &tensors.at(output.name()).tensor();
    if( output_tensor_ptr->isNetworkInput() ) {
      // HACK WAR for TRT not allowing input == output
      // TODO: Does this break things by changing the name of the input tensor?
      output_tensor_ptr->setName(("__" + output.name()).c_str());
      output_tensor_ptr = &identity(&_importer_ctx, output_tensor_ptr).tensor();
      ASSERT(output_tensor_ptr, ErrorCode::kUNSUPPORTED_NODE);
      output_tensor_ptr->setName(output.name().c_str());
    }
    nvinfer1::ITensor** user_output = _importer_ctx.getUserOutput(output.name().c_str());
    if( !user_output ) {
      _importer_ctx.network()->markOutput(*output_tensor_ptr);
      nvinfer1::DataType output_trt_dtype;
      ASSERT(convert_dtype(
                 output.type().tensor_type().elem_type(), &output_trt_dtype),
             ErrorCode::kUNSUPPORTED_NODE);
#if NV_TENSORRT_MAJOR >= 4
      // For INT32 data type, output type must match tensor type
      ASSERT(output_tensor_ptr->getType() != nvinfer1::DataType::kINT32 ||
             output_trt_dtype == nvinfer1::DataType::kINT32,
             ErrorCode::kUNSUPPORTED_NODE);
#endif // NV_TENSORRT_MAJOR >= 4
      // Note: Without this, output type is always float32
      output_tensor_ptr->setType(output_trt_dtype);
    }
  }
  // Return user-requested output tensors
  for( auto user_output_entry : _importer_ctx.getUserOutputs() ) {
    std::string         user_output_name = user_output_entry.first;
    nvinfer1::ITensor** user_output_ptr  = user_output_entry.second;
    ASSERT(tensors.count(user_output_name), ErrorCode::kINVALID_VALUE);
    TensorOrWeights user_output = tensors.at(user_output_name);
    ASSERT(user_output.is_tensor(), ErrorCode::kINVALID_VALUE);
    *user_output_ptr = &user_output.tensor();
  }
  return Status::success();
}

} // namespace onnx2trt
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "caf/actor.hpp"
#include "caf/actor_cast.hpp"
#include "caf/downstream_manager.hpp"
#include "caf/downstream_msg.hpp"
#include "caf/fwd.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/make_message.hpp"
#include "caf/message_builder.hpp"
#include "caf/ref_counted.hpp"
#include "caf/stream.hpp"
#include "caf/stream_slot.hpp"
#include "caf/upstream_msg.hpp"

namespace caf {

/// Manages a single stream with any number of in- and outbound paths.
class stream_manager : public ref_counted {
public:
  // -- constants --------------------------------------------------------------

  /// Configures whether this stream shall remain open even if no in- or
  /// outbound paths exist.
  static constexpr int is_continuous_flag = 0x0001;

  /// Denotes whether the stream is about to stop, only sending already
  /// buffered elements.
  static constexpr int is_shutting_down_flag = 0x0002;

  // -- member types -----------------------------------------------------------

  using inbound_paths_list = std::vector<inbound_path*>;

  stream_manager(scheduled_actor* selfptr,
                 stream_priority prio = stream_priority::normal);

  ~stream_manager() override;

  virtual void handle(inbound_path* from, downstream_msg::batch& x);

  virtual void handle(inbound_path* from, downstream_msg::close& x);

  virtual void handle(inbound_path* from, downstream_msg::forced_close& x);

  virtual bool handle(stream_slots, upstream_msg::ack_open& x);

  virtual void handle(stream_slots slots, upstream_msg::ack_batch& x);

  virtual void handle(stream_slots slots, upstream_msg::drop& x);

  virtual void handle(stream_slots slots, upstream_msg::forced_drop& x);

  /// Closes all output and input paths and sends the final result to the
  /// client.
  virtual void stop(error reason = none);

  /// Mark this stream as shutting down, only allowing flushing all related
  /// buffers of in- and outbound paths.
  virtual void shutdown();

  /// Tries to advance the stream by generating more credit or by sending
  /// batches.
  void advance();

  /// Pushes new data to downstream actors by sending batches. The amount of
  /// pushed data is limited by the available credit.
  virtual void push();

  /// Returns true if the handler is not able to process any further batches
  /// since it is unable to make progress sending on its own.
  virtual bool congested() const noexcept;

  /// Sends a handshake to `dest`.
  /// @pre `dest != nullptr`
  virtual void deliver_handshake(response_promise& rp, stream_slot slot,
                                 message handshake);

  // -- implementation hooks for sources ---------------------------------------

  /// Tries to generate new messages for the stream. This member function does
  /// nothing on stages and sinks, but can trigger a source to produce more
  /// messages.
  virtual bool generate_messages();

  // -- pure virtual member functions ------------------------------------------

  /// Returns the manager for downstream communication.
  virtual downstream_manager& out() = 0;

  /// Returns the manager for downstream communication.
  const downstream_manager& out() const;

  /// Returns whether the manager has reached the end and can be discarded
  /// safely.
  virtual bool done() const = 0;

  /// Returns whether the manager cannot make any progress on its own at the
  /// moment. For example, a source is idle if it has filled its output buffer
  /// and there isn't any credit left.
  virtual bool idle() const noexcept = 0;

  /// Advances time.
  virtual void cycle_timeout(size_t cycle_nr);

  // -- input path management --------------------------------------------------

  /// Informs the manager that a new input path opens.
  /// @note The lifetime of inbound paths is managed by the downstream queue.
  ///       This function is called from the constructor of `inbound_path`.
  virtual void register_input_path(inbound_path* x);

  /// Informs the manager that an input path closes.
  /// @note The lifetime of inbound paths is managed by the downstream queue.
  ///       This function is called from the destructor of `inbound_path`.
  virtual void deregister_input_path(inbound_path* x) noexcept;

  /// Removes an input path
  virtual void remove_input_path(stream_slot slot, error reason, bool silent);

  // -- properties -------------------------------------------------------------

  /// Returns whether this stream is shutting down.
  bool shutting_down() const noexcept {
    return getf(is_shutting_down_flag);
  }

  /// Returns whether this stream remains open even if no in- or outbound paths
  /// exist. The default is `false`. Does not keep a source alive past the
  /// point where its driver returns `done() == true`.
  inline bool continuous() const noexcept {
    return getf(is_continuous_flag);
  }

  /// Sets whether this stream remains open even if no in- or outbound paths
  /// exist.
  inline void continuous(bool x) noexcept {
    if (!shutting_down()) {
      if (x)
        setf(is_continuous_flag);
      else
        unsetf(is_continuous_flag);
    }
  }

  /// Returns the list of inbound paths.
  inline const inbound_paths_list& inbound_paths() const  noexcept{
    return inbound_paths_;
  }

  /// Returns the inbound paths at slot `x`.
  inbound_path* get_inbound_path(stream_slot x) const noexcept;

  /// Queries whether all inbound paths are up-to-date and have non-zero
  /// credit. A sink is idle if this function returns `true`.
  bool inbound_paths_idle() const noexcept;

  /// Returns the parent actor.
  inline scheduled_actor* self() {
    return self_;
  }

  /// Acquires credit on an inbound path. The calculated credit to fill our
  /// queue fro two cycles is `desired`, but the manager is allowed to return
  /// any non-negative value.
  virtual int32_t acquire_credit(inbound_path* path, int32_t desired);

  /// Creates an outbound path to the current sender without any type checking.
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out>
  outbound_stream_slot<Out> add_unchecked_outbound_path() {
    auto handshake = make_message(stream<Out>{});
    return add_unchecked_outbound_path_impl(std::move(handshake));
  }

  /// Creates an outbound path to the current sender without any type checking.
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class... Ts>
  outbound_stream_slot<Out, detail::strip_and_convert_t<Ts>...>
  add_unchecked_outbound_path(std::tuple<Ts...> xs) {
    auto tk = std::make_tuple(stream<Out>{});
    auto handshake = make_message_from_tuple(std::tuple_cat(tk, std::move(xs)));
    return add_unchecked_outbound_path_impl(std::move(handshake));
  }

  /// Creates an outbound path to `next`, only checking whether the interface
  /// of `next` allows handshakes of type `Out`.
  /// @pre `next != nullptr`
  /// @pre `self()->pending_stream_managers_[slot] == this`
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class Handle>
  outbound_stream_slot<Out> add_unchecked_outbound_path(Handle next) {
    // TODO: type-check whether `next` accepts our handshake
    auto handshake = make_message(stream<Out>{});
    auto hdl = actor_cast<strong_actor_ptr>(std::move(next));
    return add_unchecked_outbound_path_impl(std::move(hdl),
                                            std::move(handshake));
  }

  /// Creates an outbound path to `next`, only checking whether the interface
  /// of `next` allows handshakes of type `Out` with arguments `Ts...`.
  /// @pre `next != nullptr`
  /// @pre `self()->pending_stream_managers_[slot] == this`
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class Handle, class... Ts>
  outbound_stream_slot<Out, detail::strip_and_convert_t<Ts>...>
  add_unchecked_outbound_path(const Handle& next, std::tuple<Ts...> xs) {
    // TODO: type-check whether `next` accepts our handshake
    auto tk = std::make_tuple(stream<Out>{});
    auto handshake = make_message_from_tuple(std::tuple_cat(tk, std::move(xs)));
    auto hdl = actor_cast<strong_actor_ptr>(std::move(next));
    return add_unchecked_outbound_path_impl(std::move(hdl),
                                            std::move(handshake));
  }

  /// Creates an inbound path to the current sender without any type checking.
  /// @pre `current_sender() != nullptr`
  /// @pre `out().terminal() == false`
  /// @private
  template <class In>
  stream_slot add_unchecked_inbound_path(const stream<In>&) {
    return add_unchecked_inbound_path_impl(make_rtti_pair<In>());
  }

  /// Adds a new outbound path to `rp.next()`.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(response_promise& rp,
                                               message handshake);

  /// Adds a new outbound path to `next`.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(strong_actor_ptr next,
                                               message handshake);

  /// Adds a new outbound path to the current sender.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(message handshake);

  /// Adds the current sender as an inbound path.
  /// @pre Current message is an `open_stream_msg`.
stream_slot add_unchecked_inbound_path_impl(rtti_pair rtti);

protected:
  // -- modifiers for self -----------------------------------------------------

  stream_slot assign_next_slot();

  stream_slot assign_next_pending_slot();

  // -- implementation hooks ---------------------------------------------------

  virtual void finalize(const error& reason);

  // -- implementation hooks for sinks -----------------------------------------

  /// Called when `in().closed()` changes to `true`. The default
  /// implementation does nothing.
  virtual void input_closed(error reason);

  // -- implementation hooks for sources ---------------------------------------

  /// Called whenever new credit becomes available. The default implementation
  /// logs an error (sources are expected to override this hook).
  virtual void downstream_demand(outbound_path* ptr, long demand);

  /// Called when `out().closed()` changes to `true`. The default
  /// implementation does nothing.
  virtual void output_closed(error reason);

  // -- member variables -------------------------------------------------------

  /// Points to the parent actor.
  scheduled_actor* self_;

  /// Stores non-owning pointers to all input paths.
  inbound_paths_list inbound_paths_;

  /// Keeps track of pending handshakes.
  long pending_handshakes_;

  /// Configures the importance of outgoing traffic.
  stream_priority priority_;

  /// Stores individual flags, for continuous streaming or when shutting down.
  int flags_;

private:
  void setf(int flag) noexcept {
    auto x = flags_;
    flags_ = x | flag;
  }

  void unsetf(int flag) noexcept {
    auto x = flags_;
    flags_ = x & ~flag;
  }

   bool getf(int flag) const noexcept {
     return (flags_ & flag) != 0;
   }
};

/// A reference counting pointer to a `stream_manager`.
/// @relates stream_manager
using stream_manager_ptr = intrusive_ptr<stream_manager>;

} // namespace caf

<commit_msg>Add is_stopped_flag and fix formatting<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "caf/actor.hpp"
#include "caf/actor_cast.hpp"
#include "caf/downstream_manager.hpp"
#include "caf/downstream_msg.hpp"
#include "caf/fwd.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/make_message.hpp"
#include "caf/message_builder.hpp"
#include "caf/ref_counted.hpp"
#include "caf/stream.hpp"
#include "caf/stream_slot.hpp"
#include "caf/upstream_msg.hpp"

namespace caf {

/// Manages a single stream with any number of in- and outbound paths.
class stream_manager : public ref_counted {
public:
  // -- constants --------------------------------------------------------------

  /// Configures whether this stream shall remain open even if no in- or
  /// outbound paths exist.
  static constexpr int is_continuous_flag = 0x0001;

  /// Denotes whether the stream is about to stop, only sending already
  /// buffered elements.
  static constexpr int is_shutting_down_flag = 0x0002;

  /// Denotes whether the manager has already stopped. Calling member functions
  /// such as stop() or abort() on it no longer has any effect.
  static constexpr int is_stopped_flag = 0x0004;

  // -- member types -----------------------------------------------------------

  using inbound_paths_list = std::vector<inbound_path*>;

  stream_manager(scheduled_actor* selfptr,
                 stream_priority prio = stream_priority::normal);

  ~stream_manager() override;

  virtual void handle(inbound_path* from, downstream_msg::batch& x);

  virtual void handle(inbound_path* from, downstream_msg::close& x);

  virtual void handle(inbound_path* from, downstream_msg::forced_close& x);

  virtual bool handle(stream_slots, upstream_msg::ack_open& x);

  virtual void handle(stream_slots slots, upstream_msg::ack_batch& x);

  virtual void handle(stream_slots slots, upstream_msg::drop& x);

  virtual void handle(stream_slots slots, upstream_msg::forced_drop& x);

  /// Closes all output and input paths and sends the final result to the
  /// client.
  virtual void stop(error reason = none);

  /// Mark this stream as shutting down, only allowing flushing all related
  /// buffers of in- and outbound paths.
  virtual void shutdown();

  /// Tries to advance the stream by generating more credit or by sending
  /// batches.
  void advance();

  /// Pushes new data to downstream actors by sending batches. The amount of
  /// pushed data is limited by the available credit.
  virtual void push();

  /// Returns true if the handler is not able to process any further batches
  /// since it is unable to make progress sending on its own.
  virtual bool congested() const noexcept;

  /// Sends a handshake to `dest`.
  /// @pre `dest != nullptr`
  virtual void deliver_handshake(response_promise& rp, stream_slot slot,
                                 message handshake);

  // -- implementation hooks for sources ---------------------------------------

  /// Tries to generate new messages for the stream. This member function does
  /// nothing on stages and sinks, but can trigger a source to produce more
  /// messages.
  virtual bool generate_messages();

  // -- pure virtual member functions ------------------------------------------

  /// Returns the manager for downstream communication.
  virtual downstream_manager& out() = 0;

  /// Returns the manager for downstream communication.
  const downstream_manager& out() const;

  /// Returns whether the manager has reached the end and can be discarded
  /// safely.
  virtual bool done() const = 0;

  /// Returns whether the manager cannot make any progress on its own at the
  /// moment. For example, a source is idle if it has filled its output buffer
  /// and there isn't any credit left.
  virtual bool idle() const noexcept = 0;

  /// Advances time.
  virtual void cycle_timeout(size_t cycle_nr);

  // -- input path management --------------------------------------------------

  /// Informs the manager that a new input path opens.
  /// @note The lifetime of inbound paths is managed by the downstream queue.
  ///       This function is called from the constructor of `inbound_path`.
  virtual void register_input_path(inbound_path* x);

  /// Informs the manager that an input path closes.
  /// @note The lifetime of inbound paths is managed by the downstream queue.
  ///       This function is called from the destructor of `inbound_path`.
  virtual void deregister_input_path(inbound_path* x) noexcept;

  /// Removes an input path
  virtual void remove_input_path(stream_slot slot, error reason, bool silent);

  // -- properties -------------------------------------------------------------

  /// Returns whether this stream is shutting down.
  bool shutting_down() const noexcept {
    return getf(is_shutting_down_flag);
  }

  /// Returns whether this manager has already stopped.
  bool stopped() const noexcept {
    return getf(is_stopped_flag);
  }

  /// Returns whether this stream remains open even if no in- or outbound paths
  /// exist. The default is `false`. Does not keep a source alive past the
  /// point where its driver returns `done() == true`.
  bool continuous() const noexcept {
    return getf(is_continuous_flag);
  }

  /// Sets whether this stream remains open even if no in- or outbound paths
  /// exist.
  void continuous(bool x) noexcept {
    if (!shutting_down()) {
      if (x)
        setf(is_continuous_flag);
      else
        unsetf(is_continuous_flag);
    }
  }

  /// Returns the list of inbound paths.
  const inbound_paths_list& inbound_paths() const noexcept {
    return inbound_paths_;
  }

  /// Returns the inbound paths at slot `x`.
  inbound_path* get_inbound_path(stream_slot x) const noexcept;

  /// Queries whether all inbound paths are up-to-date and have non-zero
  /// credit. A sink is idle if this function returns `true`.
  bool inbound_paths_idle() const noexcept;

  /// Returns the parent actor.
  scheduled_actor* self() {
    return self_;
  }

  /// Acquires credit on an inbound path. The calculated credit to fill our
  /// queue fro two cycles is `desired`, but the manager is allowed to return
  /// any non-negative value.
  virtual int32_t acquire_credit(inbound_path* path, int32_t desired);

  /// Creates an outbound path to the current sender without any type checking.
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out>
  outbound_stream_slot<Out> add_unchecked_outbound_path() {
    auto handshake = make_message(stream<Out>{});
    return add_unchecked_outbound_path_impl(std::move(handshake));
  }

  /// Creates an outbound path to the current sender without any type checking.
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class... Ts>
  outbound_stream_slot<Out, detail::strip_and_convert_t<Ts>...>
  add_unchecked_outbound_path(std::tuple<Ts...> xs) {
    auto tk = std::make_tuple(stream<Out>{});
    auto handshake = make_message_from_tuple(std::tuple_cat(tk, std::move(xs)));
    return add_unchecked_outbound_path_impl(std::move(handshake));
  }

  /// Creates an outbound path to `next`, only checking whether the interface
  /// of `next` allows handshakes of type `Out`.
  /// @pre `next != nullptr`
  /// @pre `self()->pending_stream_managers_[slot] == this`
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class Handle>
  outbound_stream_slot<Out> add_unchecked_outbound_path(Handle next) {
    // TODO: type-check whether `next` accepts our handshake
    auto handshake = make_message(stream<Out>{});
    auto hdl = actor_cast<strong_actor_ptr>(std::move(next));
    return add_unchecked_outbound_path_impl(std::move(hdl),
                                            std::move(handshake));
  }

  /// Creates an outbound path to `next`, only checking whether the interface
  /// of `next` allows handshakes of type `Out` with arguments `Ts...`.
  /// @pre `next != nullptr`
  /// @pre `self()->pending_stream_managers_[slot] == this`
  /// @pre `out().terminal() == false`
  /// @private
  template <class Out, class Handle, class... Ts>
  outbound_stream_slot<Out, detail::strip_and_convert_t<Ts>...>
  add_unchecked_outbound_path(const Handle& next, std::tuple<Ts...> xs) {
    // TODO: type-check whether `next` accepts our handshake
    auto tk = std::make_tuple(stream<Out>{});
    auto handshake = make_message_from_tuple(std::tuple_cat(tk, std::move(xs)));
    auto hdl = actor_cast<strong_actor_ptr>(std::move(next));
    return add_unchecked_outbound_path_impl(std::move(hdl),
                                            std::move(handshake));
  }

  /// Creates an inbound path to the current sender without any type checking.
  /// @pre `current_sender() != nullptr`
  /// @pre `out().terminal() == false`
  /// @private
  template <class In>
  stream_slot add_unchecked_inbound_path(const stream<In>&) {
    return add_unchecked_inbound_path_impl(make_rtti_pair<In>());
  }

  /// Adds a new outbound path to `rp.next()`.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(response_promise& rp,
                                               message handshake);

  /// Adds a new outbound path to `next`.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(strong_actor_ptr next,
                                               message handshake);

  /// Adds a new outbound path to the current sender.
  /// @private
  stream_slot add_unchecked_outbound_path_impl(message handshake);

  /// Adds the current sender as an inbound path.
  /// @pre Current message is an `open_stream_msg`.
  stream_slot add_unchecked_inbound_path_impl(rtti_pair rtti);

protected:
  // -- modifiers for self -----------------------------------------------------

  stream_slot assign_next_slot();

  stream_slot assign_next_pending_slot();

  // -- implementation hooks ---------------------------------------------------

  virtual void finalize(const error& reason);

  // -- implementation hooks for sinks -----------------------------------------

  /// Called when `in().closed()` changes to `true`. The default
  /// implementation does nothing.
  virtual void input_closed(error reason);

  // -- implementation hooks for sources ---------------------------------------

  /// Called whenever new credit becomes available. The default implementation
  /// logs an error (sources are expected to override this hook).
  virtual void downstream_demand(outbound_path* ptr, long demand);

  /// Called when `out().closed()` changes to `true`. The default
  /// implementation does nothing.
  virtual void output_closed(error reason);

  // -- member variables -------------------------------------------------------

  /// Points to the parent actor.
  scheduled_actor* self_;

  /// Stores non-owning pointers to all input paths.
  inbound_paths_list inbound_paths_;

  /// Keeps track of pending handshakes.
  long pending_handshakes_;

  /// Configures the importance of outgoing traffic.
  stream_priority priority_;

  /// Stores individual flags, for continuous streaming or when shutting down.
  int flags_;

private:
  void setf(int flag) noexcept {
    auto x = flags_;
    flags_ = x | flag;
  }

  void unsetf(int flag) noexcept {
    auto x = flags_;
    flags_ = x & ~flag;
  }

  bool getf(int flag) const noexcept {
    return (flags_ & flag) != 0;
  }
};

/// A reference counting pointer to a `stream_manager`.
/// @relates stream_manager
using stream_manager_ptr = intrusive_ptr<stream_manager>;

} // namespace caf

<|endoftext|>


<commit_before>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_MATH_MATHUTILS_HPP
#define OUZEL_MATH_MATHUTILS_HPP

#include <cstdint>
#include <cmath>
#include <limits>
#include <type_traits>
#if defined(__ANDROID__)
#  include <cpu-features.h>
#endif

namespace ouzel
{
#if defined(__ARM_NEON__)
#  if defined(__ANDROID__) && defined(__arm__)
    // NEON support must be checked at runtime on 32-bit Android
    class AnrdoidNeonChecker final
    {
    public:
        AnrdoidNeonChecker() noexcept:
            neonAvailable(android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
                          (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0)
        {
        }

        operator bool() const noexcept { return neonAvailable; }

    private:
        bool neonAvailable;
    };

    extern const AnrdoidNeonChecker isSimdAvailable;
#  else
    constexpr auto isSimdAvailable = true;
#  endif
#elif defined(__SSE__)
    constexpr auto isSimdAvailable = true;
#else
    constexpr auto isSimdAvailable = false;
#endif

    template <typename T> constexpr T lerp(const T v0, const T v1, const T t) noexcept
    {
        return (T(1) - t) * v0 + t * v1;
    }

    template <typename T> constexpr T smoothStep(const T a, const T b, const T t) noexcept
    {
        return lerp(a, b, t * t * (T(3) - T(2) * t));
    }

    template <typename T, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
    constexpr auto isPowerOfTwo(const T x) noexcept
    {
        return (x != T(0)) && (((x - T(1)) & x) == 0);
    }

    template <typename T, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
    inline T nextPowerOfTwo(const T x) noexcept
    {
        if (x != 0)
        {
            --x;
            for (uint32_t shift = 1; shift < sizeof(T) * 8; shift *= 2)
                x |= (x >> shift);
        }
        return ++x;
    }

    template <typename T> constexpr T degToRad(const T x) noexcept
    {
        return x * T(0.01745329251994329576);
    }

    template <typename T> constexpr T radToDeg(const T x) noexcept
    {
        return x * T(57.2957795130823208767);
    }

    template <typename T> constexpr T clamp(const T x, const T lo, const T hi) noexcept
    {
        return (x < lo) ? lo : ((x > hi) ? hi : x);
    }

    template <typename T>
    constexpr auto isNearlyEqual(const T a, const T b, const T tolerance = std::numeric_limits<T>::min()) noexcept
    {
        return (a - b) <= tolerance && (a - b) >= -tolerance;
    }
}

#endif // OUZEL_MATH_MATHUTILS_HPP
<commit_msg>Split math function declarations into multiple lines<commit_after>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_MATH_MATHUTILS_HPP
#define OUZEL_MATH_MATHUTILS_HPP

#include <cstdint>
#include <cmath>
#include <limits>
#include <type_traits>
#if defined(__ANDROID__)
#  include <cpu-features.h>
#endif

namespace ouzel
{
#if defined(__ARM_NEON__)
#  if defined(__ANDROID__) && defined(__arm__)
    // NEON support must be checked at runtime on 32-bit Android
    class AnrdoidNeonChecker final
    {
    public:
        AnrdoidNeonChecker() noexcept:
            neonAvailable(android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
                          (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0)
        {
        }

        operator bool() const noexcept { return neonAvailable; }

    private:
        bool neonAvailable;
    };

    extern const AnrdoidNeonChecker isSimdAvailable;
#  else
    constexpr auto isSimdAvailable = true;
#  endif
#elif defined(__SSE__)
    constexpr auto isSimdAvailable = true;
#else
    constexpr auto isSimdAvailable = false;
#endif

    template <typename T>
    constexpr T lerp(const T v0, const T v1, const T t) noexcept
    {
        return (T(1) - t) * v0 + t * v1;
    }

    template <typename T>
    constexpr T smoothStep(const T a, const T b, const T t) noexcept
    {
        return lerp(a, b, t * t * (T(3) - T(2) * t));
    }

    template <typename T, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
    constexpr auto isPowerOfTwo(const T x) noexcept
    {
        return (x != T(0)) && (((x - T(1)) & x) == 0);
    }

    template <typename T, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
    inline T nextPowerOfTwo(const T x) noexcept
    {
        if (x != 0)
        {
            --x;
            for (uint32_t shift = 1; shift < sizeof(T) * 8; shift *= 2)
                x |= (x >> shift);
        }
        return ++x;
    }

    template <typename T> constexpr T degToRad(const T x) noexcept
    {
        return x * T(0.01745329251994329576);
    }

    template <typename T> constexpr T radToDeg(const T x) noexcept
    {
        return x * T(57.2957795130823208767);
    }

    template <typename T>
    constexpr T clamp(const T x, const T lo, const T hi) noexcept
    {
        return (x < lo) ? lo : ((x > hi) ? hi : x);
    }

    template <typename T>
    constexpr auto isNearlyEqual(const T a, const T b,
                                 const T tolerance = std::numeric_limits<T>::min()) noexcept
    {
        return (a - b) <= tolerance && (a - b) >= -tolerance;
    }
}

#endif // OUZEL_MATH_MATHUTILS_HPP
<|endoftext|>


<commit_before>/*********************************************************************
 * NAN - Native Abstractions for Node.js
 *
 * Copyright (c) 2018 NAN contributors
 *
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 ********************************************************************/

#include <nan.h>
#include <cstring>

using namespace Nan;  // NOLINT(build/namespaces)

class NamedInterceptor : public ObjectWrap {
  char buf[256];

 public:
  NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof (this->buf)); }
  static NAN_MODULE_INIT(Init);
  static v8::Local<v8::Value> NewInstance ();
  static NAN_METHOD(New);

  static NAN_PROPERTY_GETTER(PropertyGetter);
  static NAN_PROPERTY_SETTER(PropertySetter);
  static NAN_PROPERTY_ENUMERATOR(PropertyEnumerator);
  static NAN_PROPERTY_DELETER(PropertyDeleter);
  static NAN_PROPERTY_QUERY(PropertyQuery);
};

static Persistent<v8::FunctionTemplate> namedinterceptors_constructor;

NAN_METHOD(CreateNew) {
  info.GetReturnValue().Set(NamedInterceptor::NewInstance());
}

NAN_MODULE_INIT(NamedInterceptor::Init) {
  v8::Local<v8::FunctionTemplate> tpl =
    Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);
  namedinterceptors_constructor.Reset(tpl);
  tpl->SetClassName(Nan::New("NamedInterceptor").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  v8::Local<v8::ObjectTemplate> inst = tpl->InstanceTemplate();

  SetNamedPropertyHandler(
      inst
    , NamedInterceptor::PropertyGetter
    , NamedInterceptor::PropertySetter
    , NamedInterceptor::PropertyQuery
    , NamedInterceptor::PropertyDeleter
    , NamedInterceptor::PropertyEnumerator);

  v8::Local<v8::Function> createnew =
    Nan::New<v8::FunctionTemplate>(CreateNew)->GetFunction();
  Set(target, Nan::New("create").ToLocalChecked(), createnew);
}

v8::Local<v8::Value> NamedInterceptor::NewInstance () {
  EscapableHandleScope scope;
  v8::Local<v8::FunctionTemplate> constructorHandle =
      Nan::New(namedinterceptors_constructor);
  v8::Local<v8::Object> instance =
    Nan::NewInstance(constructorHandle->GetFunction()).ToLocalChecked();
  return scope.Escape(instance);
}

NAN_METHOD(NamedInterceptor::New) {
  NamedInterceptor* interceptor = new NamedInterceptor();
  interceptor->Wrap(info.This());
  info.GetReturnValue().Set(info.This());
}

NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
    info.GetReturnValue().Set(Nan::New(interceptor->buf).ToLocalChecked());
  } else {
    info.GetReturnValue().Set(Nan::New("bar").ToLocalChecked());
  }
}

NAN_PROPERTY_SETTER(NamedInterceptor::PropertySetter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
    std::strncpy(
        interceptor->buf
      , *Nan::Utf8String(value)
      , sizeof (interceptor->buf));
    info.GetReturnValue().Set(info.This());
  } else {
    info.GetReturnValue().Set(info.This());
  }
}

NAN_PROPERTY_ENUMERATOR(NamedInterceptor::PropertyEnumerator) {
  v8::Local<v8::Array> arr = Nan::New<v8::Array>();
  Set(arr, 0, Nan::New("value").ToLocalChecked());
  info.GetReturnValue().Set(arr);
}

NAN_PROPERTY_DELETER(NamedInterceptor::PropertyDeleter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  std::strncpy(interceptor->buf, "goober", sizeof (interceptor->buf));
  info.GetReturnValue().Set(True());
}

NAN_PROPERTY_QUERY(NamedInterceptor::PropertyQuery) {
  if (!std::strcmp(*Nan::Utf8String(property), "thing")) {
    info.GetReturnValue().Set(Nan::New<v8::Integer>(v8::DontEnum));
  }
}

NODE_MODULE(namedinterceptors, NamedInterceptor::Init)
<commit_msg>Fix bugs in named interceptor test.<commit_after>/*********************************************************************
 * NAN - Native Abstractions for Node.js
 *
 * Copyright (c) 2018 NAN contributors
 *
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 ********************************************************************/

#include <nan.h>
#include <cstring>

using namespace Nan;  // NOLINT(build/namespaces)

class NamedInterceptor : public ObjectWrap {
  char buf[256];

 public:
  NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof (this->buf)); }
  static NAN_MODULE_INIT(Init);
  static v8::Local<v8::Value> NewInstance ();
  static NAN_METHOD(New);

  static NAN_PROPERTY_GETTER(PropertyGetter);
  static NAN_PROPERTY_SETTER(PropertySetter);
  static NAN_PROPERTY_ENUMERATOR(PropertyEnumerator);
  static NAN_PROPERTY_DELETER(PropertyDeleter);
  static NAN_PROPERTY_QUERY(PropertyQuery);
};

static Persistent<v8::FunctionTemplate> namedinterceptors_constructor;

NAN_METHOD(CreateNew) {
  info.GetReturnValue().Set(NamedInterceptor::NewInstance());
}

NAN_MODULE_INIT(NamedInterceptor::Init) {
  v8::Local<v8::FunctionTemplate> tpl =
    Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);
  namedinterceptors_constructor.Reset(tpl);
  tpl->SetClassName(Nan::New("NamedInterceptor").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  v8::Local<v8::ObjectTemplate> inst = tpl->InstanceTemplate();

  SetNamedPropertyHandler(
      inst
    , NamedInterceptor::PropertyGetter
    , NamedInterceptor::PropertySetter
    , NamedInterceptor::PropertyQuery
    , NamedInterceptor::PropertyDeleter
    , NamedInterceptor::PropertyEnumerator);

  v8::Local<v8::Function> createnew =
    Nan::New<v8::FunctionTemplate>(CreateNew)->GetFunction();
  Set(target, Nan::New("create").ToLocalChecked(), createnew);
}

v8::Local<v8::Value> NamedInterceptor::NewInstance () {
  EscapableHandleScope scope;
  v8::Local<v8::FunctionTemplate> constructorHandle =
      Nan::New(namedinterceptors_constructor);
  v8::Local<v8::Object> instance =
    Nan::NewInstance(constructorHandle->GetFunction()).ToLocalChecked();
  return scope.Escape(instance);
}

NAN_METHOD(NamedInterceptor::New) {
  NamedInterceptor* interceptor = new NamedInterceptor();
  interceptor->Wrap(info.This());
  info.GetReturnValue().Set(info.This());
}

NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
    info.GetReturnValue().Set(Nan::New(interceptor->buf).ToLocalChecked());
  } else {
    info.GetReturnValue().Set(Nan::New("bar").ToLocalChecked());
  }
}

NAN_PROPERTY_SETTER(NamedInterceptor::PropertySetter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
    std::strncpy(
        interceptor->buf
      , *Nan::Utf8String(value)
      , sizeof (interceptor->buf));
    info.GetReturnValue().Set(info.This());
  } else {
    info.GetReturnValue().Set(info.This());
  }
}

NAN_PROPERTY_ENUMERATOR(NamedInterceptor::PropertyEnumerator) {
  v8::Local<v8::Array> arr = Nan::New<v8::Array>();
  Set(arr, 0, Nan::New("value").ToLocalChecked());
  info.GetReturnValue().Set(arr);
}

NAN_PROPERTY_DELETER(NamedInterceptor::PropertyDeleter) {
  NamedInterceptor* interceptor =
    ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
  std::strncpy(interceptor->buf, "goober", sizeof (interceptor->buf));
  info.GetReturnValue().Set(True());
}

NAN_PROPERTY_QUERY(NamedInterceptor::PropertyQuery) {
  Nan::Utf8String s(property);
  if (!std::strcmp(*s, "thing")) {
    return info.GetReturnValue().Set(Nan::New<v8::Integer>(v8::DontEnum));
  }
  if (!std::strcmp(*s, "value")) {
    return info.GetReturnValue().Set(Nan::New(0));
  }
}

NODE_MODULE(namedinterceptors, NamedInterceptor::Init)
<|endoftext|>