<commit_before>/*
   This file is part of the clazy static checker.

  Copyright (C) 2015-2016 Sergio Martins <smartins@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "clazy_stl.h"
#include "QtUtils.h"
#include "Utils.h"
#include "TypeUtils.h"
#include "StmtBodyRange.h"
#include "MacroUtils.h"
#include "HierarchyUtils.h"
#include "StringUtils.h"
#include "ContextUtils.h"

#include <clang/AST/AST.h>

using namespace std;
using namespace clang;

bool QtUtils::isQtIterableClass(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtIterableClass(record->getQualifiedNameAsString());
}

const vector<string> & QtUtils::qtContainers()
{
    static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap",
                                            "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
                                            "QByteArray", "QSequentialIterable", "QAssociativeIterable", "QJsonArray", "QLinkedList" };
    return classes;
}

const vector<string> & QtUtils::qtCOWContainers()
{
    static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap",
                                            "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
                                            "QByteArray", "QJsonArray", "QLinkedList" };
    return classes;
}

bool QtUtils::isQtCOWIterableClass(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtCOWIterableClass(record->getQualifiedNameAsString());
}

bool QtUtils::isQtCOWIterableClass(const string &className)
{
    const auto &classes = qtCOWContainers();
    return clazy_std::contains(classes, className);
}

bool QtUtils::isQtIterableClass(const string &className)
{
    const auto &classes = qtContainers();
    return clazy_std::contains(classes, className);
}

bool QtUtils::isQtAssociativeContainer(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtAssociativeContainer(record->getNameAsString());
}

bool QtUtils::isQtAssociativeContainer(const string &className)
{
    static const vector<string> classes = { "QSet", "QMap", "QHash" };
    return clazy_std::contains(classes, className);
}

bool QtUtils::isBootstrapping(const clang::CompilerInstance &ci)
{
    return MacroUtils::isPredefined(ci, "QT_BOOTSTRAPPED");
}

bool QtUtils::isQObject(CXXRecordDecl *decl)
{
    return TypeUtils::derivesFrom(decl, "QObject");
}

bool QtUtils::isQObject(clang::QualType qt)
{
    qt = TypeUtils::pointeeQualType(qt);
    const auto t = qt.getTypePtrOrNull();
    return t ? isQObject(t->getAsCXXRecordDecl()) : false;
}

bool QtUtils::isConvertibleTo(const Type *source, const Type *target)
{
    if (!source || !target)
        return false;

    if (source->isPointerType() ^ target->isPointerType())
        return false;

    if (source == target)
        return true;

    if (source->getPointeeCXXRecordDecl() && source->getPointeeCXXRecordDecl() == target->getPointeeCXXRecordDecl())
        return true;

    if (source->isIntegerType() && target->isIntegerType())
        return true;

    if (source->isFloatingType() && target->isFloatingType())
        return true;

    return false;
}

bool QtUtils::isInForeach(const clang::CompilerInstance &ci, clang::SourceLocation loc)
{
    return MacroUtils::isInAnyMacro(ci, loc, { "Q_FOREACH", "foreach" });
}

bool QtUtils::isJavaIterator(CXXRecordDecl *record)
{
    if (!record)
        return false;

    static const vector<string> names = { "QHashIterator", "QMapIterator", "QSetIterator", "QListIterator",
                                         "QVectorIterator", "QLinkedListIterator", "QStringListIterator" };

    return clazy_std::contains(names, record->getNameAsString());
}

bool QtUtils::isJavaIterator(CXXMemberCallExpr *call)
{
    if (!call)
        return false;

    return isJavaIterator(call->getRecordDecl());
}

clang::ValueDecl *QtUtils::signalSenderForConnect(clang::CallExpr *call)
{
    if (!call || call->getNumArgs() < 1)
        return nullptr;

    Expr *firstArg = call->getArg(0);
    auto declRef = isa<DeclRefExpr>(firstArg) ? cast<DeclRefExpr>(firstArg) : HierarchyUtils::getFirstChildOfType2<DeclRefExpr>(firstArg);
    if (!declRef)
        return nullptr;

    return declRef->getDecl();
}

bool QtUtils::isTooBigForQList(clang::QualType qt, const clang::CompilerInstance &ci)
{
    return (int)ci.getASTContext().getTypeSize(qt) <= TypeUtils::sizeOfPointer(ci, qt);
}

bool QtUtils::isQtContainer(QualType t, const LangOptions &lo)
{
    const string typeName = StringUtils::simpleTypeName(t, lo);
    return clazy_std::any_of(QtUtils::qtContainers(), [typeName] (const string &container) {
        return container == typeName;
    });
}

bool QtUtils::containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRange bodyRange) // clazy:exclude=function-args-by-value
{
    if (!valDecl)
        return false;

    const FunctionDecl *context = dyn_cast<FunctionDecl>(valDecl->getDeclContext());
    if (!context)
        return false;

    bodyRange.body = context->getBody();
    if (!bodyRange.body)
        return false;

    // TODO1: Being passed to a function as const should be OK
    if (Utils::isPassedToFunction(bodyRange, valDecl, false))
        return false;

    return true;
}

bool QtUtils::isAReserveClass(CXXRecordDecl *recordDecl)
{
    if (!recordDecl)
        return false;

    static const std::vector<std::string> classes = {"QVector", "std::vector", "QList", "QSet"};

    return clazy_std::any_of(classes, [recordDecl](const string &className) {
        return TypeUtils::derivesFrom(recordDecl, className);
    });
}

clang::CXXRecordDecl *QtUtils::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl)
{
    if (!recordDecl)
        return nullptr;

    for (auto baseClass : recordDecl->bases()) {
        CXXRecordDecl *record = TypeUtils::recordFromBaseSpecifier(baseClass);
        if (isQObject(record))
            return record;
    }

    return nullptr;
}

bool QtUtils::isConnect(FunctionDecl *func)
{
    return func && func->getQualifiedNameAsString() == "QObject::connect";
}

bool QtUtils::connectHasPMFStyle(FunctionDecl *func)
{
    // Look for char* arguments
    for (auto parm : Utils::functionParameters(func)) {
        QualType qt = parm->getType();
        const Type *t = qt.getTypePtrOrNull();
        if (!t || !t->isPointerType())
            continue;

        const Type *ptt = t->getPointeeType().getTypePtrOrNull();
        if (ptt && ptt->isCharType())
            return false;
    }

    return true;
}

CXXMethodDecl *QtUtils::pmfFromConnect(CallExpr *funcCall, int argIndex)
{
    if (!funcCall)
        return nullptr;

    const int numArgs = funcCall->getNumArgs();
    if (numArgs < 3) {
        llvm::errs() << "error, connect call has less than 3 arguments\n";
        return nullptr;
    }

    if (argIndex >= numArgs) {
        llvm::errs() << "error, invalid argIndex " << argIndex << " " << numArgs;
        return nullptr;
    }

    Expr *expr = funcCall->getArg(argIndex);
    return pmfFromUnary(expr);
}

CXXMethodDecl *QtUtils::pmfFromUnary(Expr *expr)
{
    if (auto uo = dyn_cast<UnaryOperator>(expr)) {
        return pmfFromUnary(uo);
    } else if (auto call = dyn_cast<CXXOperatorCallExpr>(expr)) {

        if (call->getNumArgs() <= 1)
            return nullptr;

        FunctionDecl *func = call->getDirectCallee();
        if (!func)
            return nullptr;

        auto context = func->getParent();
        if (!context)
            return nullptr;

        CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(context);
        if (!record)
            return nullptr;

        const std::string className = record->getQualifiedNameAsString();
        if (className != "QNonConstOverload" && className != "QConstOverload")
            return nullptr;

        return pmfFromUnary(dyn_cast<UnaryOperator>(call->getArg(1)));
    } else if (auto staticCast = dyn_cast<CXXStaticCastExpr>(expr)) {
        return pmfFromUnary(staticCast->getSubExpr());
    }

    return nullptr;
}

CXXMethodDecl *QtUtils::pmfFromUnary(UnaryOperator *uo)
{
    if (!uo)
        return nullptr;

    Expr *subExpr = uo->getSubExpr();
    if (!subExpr)
        return nullptr;

    auto declref = dyn_cast<DeclRefExpr>(subExpr);

    if (declref)
        return dyn_cast<CXXMethodDecl>(declref->getDecl());

    return nullptr;
}

<commit_msg>QtUtils: QVarLength is not COW<commit_after>/*
   This file is part of the clazy static checker.

  Copyright (C) 2015-2016 Sergio Martins <smartins@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "clazy_stl.h"
#include "QtUtils.h"
#include "Utils.h"
#include "TypeUtils.h"
#include "StmtBodyRange.h"
#include "MacroUtils.h"
#include "HierarchyUtils.h"
#include "StringUtils.h"
#include "ContextUtils.h"

#include <clang/AST/AST.h>

using namespace std;
using namespace clang;

bool QtUtils::isQtIterableClass(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtIterableClass(record->getQualifiedNameAsString());
}

const vector<string> & QtUtils::qtContainers()
{
    static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap",
                                            "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
                                            "QByteArray", "QSequentialIterable", "QAssociativeIterable", "QJsonArray", "QLinkedList" };
    return classes;
}

const vector<string> & QtUtils::qtCOWContainers()
{
    static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QMap", "QHash",
                                            "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
                                            "QByteArray", "QJsonArray", "QLinkedList" };
    return classes;
}

bool QtUtils::isQtCOWIterableClass(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtCOWIterableClass(record->getQualifiedNameAsString());
}

bool QtUtils::isQtCOWIterableClass(const string &className)
{
    const auto &classes = qtCOWContainers();
    return clazy_std::contains(classes, className);
}

bool QtUtils::isQtIterableClass(const string &className)
{
    const auto &classes = qtContainers();
    return clazy_std::contains(classes, className);
}

bool QtUtils::isQtAssociativeContainer(clang::CXXRecordDecl *record)
{
    if (!record)
        return false;

    return isQtAssociativeContainer(record->getNameAsString());
}

bool QtUtils::isQtAssociativeContainer(const string &className)
{
    static const vector<string> classes = { "QSet", "QMap", "QHash" };
    return clazy_std::contains(classes, className);
}

bool QtUtils::isBootstrapping(const clang::CompilerInstance &ci)
{
    return MacroUtils::isPredefined(ci, "QT_BOOTSTRAPPED");
}

bool QtUtils::isQObject(CXXRecordDecl *decl)
{
    return TypeUtils::derivesFrom(decl, "QObject");
}

bool QtUtils::isQObject(clang::QualType qt)
{
    qt = TypeUtils::pointeeQualType(qt);
    const auto t = qt.getTypePtrOrNull();
    return t ? isQObject(t->getAsCXXRecordDecl()) : false;
}

bool QtUtils::isConvertibleTo(const Type *source, const Type *target)
{
    if (!source || !target)
        return false;

    if (source->isPointerType() ^ target->isPointerType())
        return false;

    if (source == target)
        return true;

    if (source->getPointeeCXXRecordDecl() && source->getPointeeCXXRecordDecl() == target->getPointeeCXXRecordDecl())
        return true;

    if (source->isIntegerType() && target->isIntegerType())
        return true;

    if (source->isFloatingType() && target->isFloatingType())
        return true;

    return false;
}

bool QtUtils::isInForeach(const clang::CompilerInstance &ci, clang::SourceLocation loc)
{
    return MacroUtils::isInAnyMacro(ci, loc, { "Q_FOREACH", "foreach" });
}

bool QtUtils::isJavaIterator(CXXRecordDecl *record)
{
    if (!record)
        return false;

    static const vector<string> names = { "QHashIterator", "QMapIterator", "QSetIterator", "QListIterator",
                                         "QVectorIterator", "QLinkedListIterator", "QStringListIterator" };

    return clazy_std::contains(names, record->getNameAsString());
}

bool QtUtils::isJavaIterator(CXXMemberCallExpr *call)
{
    if (!call)
        return false;

    return isJavaIterator(call->getRecordDecl());
}

clang::ValueDecl *QtUtils::signalSenderForConnect(clang::CallExpr *call)
{
    if (!call || call->getNumArgs() < 1)
        return nullptr;

    Expr *firstArg = call->getArg(0);
    auto declRef = isa<DeclRefExpr>(firstArg) ? cast<DeclRefExpr>(firstArg) : HierarchyUtils::getFirstChildOfType2<DeclRefExpr>(firstArg);
    if (!declRef)
        return nullptr;

    return declRef->getDecl();
}

bool QtUtils::isTooBigForQList(clang::QualType qt, const clang::CompilerInstance &ci)
{
    return (int)ci.getASTContext().getTypeSize(qt) <= TypeUtils::sizeOfPointer(ci, qt);
}

bool QtUtils::isQtContainer(QualType t, const LangOptions &lo)
{
    const string typeName = StringUtils::simpleTypeName(t, lo);
    return clazy_std::any_of(QtUtils::qtContainers(), [typeName] (const string &container) {
        return container == typeName;
    });
}

bool QtUtils::containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRange bodyRange) // clazy:exclude=function-args-by-value
{
    if (!valDecl)
        return false;

    const FunctionDecl *context = dyn_cast<FunctionDecl>(valDecl->getDeclContext());
    if (!context)
        return false;

    bodyRange.body = context->getBody();
    if (!bodyRange.body)
        return false;

    // TODO1: Being passed to a function as const should be OK
    if (Utils::isPassedToFunction(bodyRange, valDecl, false))
        return false;

    return true;
}

bool QtUtils::isAReserveClass(CXXRecordDecl *recordDecl)
{
    if (!recordDecl)
        return false;

    static const std::vector<std::string> classes = {"QVector", "std::vector", "QList", "QSet"};

    return clazy_std::any_of(classes, [recordDecl](const string &className) {
        return TypeUtils::derivesFrom(recordDecl, className);
    });
}

clang::CXXRecordDecl *QtUtils::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl)
{
    if (!recordDecl)
        return nullptr;

    for (auto baseClass : recordDecl->bases()) {
        CXXRecordDecl *record = TypeUtils::recordFromBaseSpecifier(baseClass);
        if (isQObject(record))
            return record;
    }

    return nullptr;
}

bool QtUtils::isConnect(FunctionDecl *func)
{
    return func && func->getQualifiedNameAsString() == "QObject::connect";
}

bool QtUtils::connectHasPMFStyle(FunctionDecl *func)
{
    // Look for char* arguments
    for (auto parm : Utils::functionParameters(func)) {
        QualType qt = parm->getType();
        const Type *t = qt.getTypePtrOrNull();
        if (!t || !t->isPointerType())
            continue;

        const Type *ptt = t->getPointeeType().getTypePtrOrNull();
        if (ptt && ptt->isCharType())
            return false;
    }

    return true;
}

CXXMethodDecl *QtUtils::pmfFromConnect(CallExpr *funcCall, int argIndex)
{
    if (!funcCall)
        return nullptr;

    const int numArgs = funcCall->getNumArgs();
    if (numArgs < 3) {
        llvm::errs() << "error, connect call has less than 3 arguments\n";
        return nullptr;
    }

    if (argIndex >= numArgs) {
        llvm::errs() << "error, invalid argIndex " << argIndex << " " << numArgs;
        return nullptr;
    }

    Expr *expr = funcCall->getArg(argIndex);
    return pmfFromUnary(expr);
}

CXXMethodDecl *QtUtils::pmfFromUnary(Expr *expr)
{
    if (auto uo = dyn_cast<UnaryOperator>(expr)) {
        return pmfFromUnary(uo);
    } else if (auto call = dyn_cast<CXXOperatorCallExpr>(expr)) {

        if (call->getNumArgs() <= 1)
            return nullptr;

        FunctionDecl *func = call->getDirectCallee();
        if (!func)
            return nullptr;

        auto context = func->getParent();
        if (!context)
            return nullptr;

        CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(context);
        if (!record)
            return nullptr;

        const std::string className = record->getQualifiedNameAsString();
        if (className != "QNonConstOverload" && className != "QConstOverload")
            return nullptr;

        return pmfFromUnary(dyn_cast<UnaryOperator>(call->getArg(1)));
    } else if (auto staticCast = dyn_cast<CXXStaticCastExpr>(expr)) {
        return pmfFromUnary(staticCast->getSubExpr());
    }

    return nullptr;
}

CXXMethodDecl *QtUtils::pmfFromUnary(UnaryOperator *uo)
{
    if (!uo)
        return nullptr;

    Expr *subExpr = uo->getSubExpr();
    if (!subExpr)
        return nullptr;

    auto declref = dyn_cast<DeclRefExpr>(subExpr);

    if (declref)
        return dyn_cast<CXXMethodDecl>(declref->getDecl());

    return nullptr;
}

<|endoftext|>


<commit_before>/* Mixer.cpp from QTau http://github.com/qtau-devgroup/editor by digited and HAL@ShurabaP, BSD license */

#include "audio/Mixer.h"
#include "Utils.h"
#include <math.h>
#include <qendian.h>
#include <QDebug>

#ifndef M_SQRT1_2
    #define M_SQRT1_2	0.70710678118654752440
#endif

qtauSoundMixer::qtauSoundMixer(QObject *parent) :
    qtauAudioSource(parent), replacingEffectsSmoothly(false), replacingTracksSmoothly(false)
{
    fmt.setByteOrder(QAudioFormat::LittleEndian);
    fmt.setCodec("audio/pcm");
    fmt.setChannelCount(2);
    fmt.setSampleRate(44100);
    fmt.setSampleSize(16);
    fmt.setSampleType(QAudioFormat::SignedInt);

    open(QIODevice::ReadOnly);
}

qtauSoundMixer::qtauSoundMixer(QList<qtauAudioSource*> &tracks, QObject *parent) :
    qtauAudioSource(parent), replacingEffectsSmoothly(false), replacingTracksSmoothly(false)
{
    foreach (qtauAudioSource *a, tracks)
        addTrack(a);
}

qint64 qtauSoundMixer::bytesAvailable() const
{
    qint64 result = 0;

    if (!atEnd())
    {
        foreach (qtauAudioSource *s, effects) result = qMax(s->bytesAvailable(), result);
        foreach (qtauAudioSource *s, tracks)  result = qMax(s->bytesAvailable(), result);
    }

    return result;
}

void qtauSoundMixer::addTrack(qtauAudioSource *t, bool replace, bool smoothly)
{
    if (t && t->size())
    {
        if (!t->isReadable())
            t->open(QIODevice::ReadOnly);

        if (t->isReadable())
        {
            if (replace)
            {
                if (smoothly) replacingTracksSmoothly = true;
                else          tracks.clear();
            }

            tracks.append(t);
        }
        else vsLog::e("Sound mixer could not open a track for reading, adding cancelled.");
    }
    else vsLog::e("Sound mixer can't add an empty track!");
}

void qtauSoundMixer::addEffect(qtauAudioSource *e, bool replace, bool smoothly)
{
    if (e && e->size())
    {
        if (!e->isReadable())
            e->open(QIODevice::ReadOnly);

        if (e->isReadable())
        {
            if (replace)
            {
                if (smoothly) replacingEffectsSmoothly = true;
                else          effects.clear();
            }

            effects.append(e);
        }
        else vsLog::e("Sound mixer could not open an effect for reading, adding cancelled.");
    }
    else vsLog::e("Sound mixer can't add an empty effect!");
}

template<qint8>   inline int sampleToS16(const qint8   &s) { return ((float)s - 128.0) / 127.0 * 32767.0; }
template<quint16> inline int sampleToS16(const quint16 &s) { return s;                                    }
template<float>   inline int sampleToS16(const float   &s) { return s * 32767.0;                          }

template<class T> inline qint64 mixSamplesT(const QByteArray &src, QByteArray &dst, bool stereo, qint64 n)
{
    const int sT = sizeof(T);
    const int sT2 = sT * 2;

    qint64 nBytes = stereo ? (sT2 * n) : (sT * n);
    qint64 max = qMin((qint64)src.size(), nBytes);
    n = stereo ? (max / sT2) : (max / sT);

    const char *inData  = src.data();
    char       *outData = dst.data();
    T       *srcS;
    quint16 *dstS;
    int iS, iD;

    if (stereo)
    {
        for (qint64 bS = 0, bD = 0; bS < max; bS += sT2, bD + 4)
        {
            // left channel
            srcS = (T*)     (inData  + bS);
            dstS = (qint16*)(outData + bD);
            iS = sampleToS16<T>(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);

            // right channel - a bit redundant, but twice less cycles easily
            srcS = (T*)     (inData  + bS + 2);
            dstS = (qint16*)(outData + bD + 2);
            iS = sampleToS16<T>(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);
        }
    }
    else
    {
        for (qint64 bS = 0, bD = 0; bS < max; bS += sT, bD + 4)
        {
            // left channel
            srcS = (T*)     (inData  + bS);
            dstS = (qint16*)(outData + bD);
            iS = sampleToS16<T>(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);

            // right channel - dest val may be different
            dstS = (qint16*)(outData + bD + 2);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);
        }
    }

    return n;
}

qint64 qtauSoundMixer::readData(char *data, qint64 maxlen)
{
    qint64 result = 0;
    qint64 frames = maxlen / 4;
    qint64 truncated = maxlen - frames*4;
    qint64 framesProcessed = 0;

    if (truncated > 0)
        vsLog::d(QString("Sound mixer was asked to give %1 bytes, %2 more than equeal to frame size!")
                         .arg(maxlen).arg(truncated));

    maxlen -= truncated;

    if (maxlen > 0) // mix samples of all tracks and effects
    {
        /*
         * all audios are considered to be open for reading, U8 or S16 or F32 LE, mono or stereo, 44100Hz
         * output from readData is considered to be 16LE stereo, 4 bytes per frame (2*2)
         * need to read same amount of frames from all tracks and sources, and if any one is giving less, it's ended
         * signal ended audios so that they may be released
         * */

        if (buffer().size() < maxlen)
            buffer().resize(maxlen);

        QByteArray &buf = buffer();
        memset(buf.data(), 0, maxlen);

        QList<qtauAudioSource*> endedEffects;
        QList<qtauAudioSource*> endedTracks;

        // cycle all effects and tracks and try to get required amount of frames from them
        foreach (qtauAudioSource *e, effects)
        {
            qint64 effFrames = 0;
            qint64 numCh = (e->getAudioFormat().channelCount() > 1) ? 2 : 1;

            switch (e->getAudioFormat().sampleSize())
            {
            case 8:  effFrames = mixSamplesT<qint8>  (e->read(frames     * numCh), buf, numCh > 1, frames); break;
            case 16: effFrames = mixSamplesT<quint16>(e->read(frames * 2 * numCh), buf, numCh > 1, frames); break;
            case 32: effFrames = mixSamplesT<float>  (e->read(frames * 4 * numCh), buf, numCh > 1, frames); break;
            default:
                vsLog::e("Sound mixer is processing an effect with unsupported sample size, dropping.");
                endedEffects.append(e);
            }

            if (effFrames < frames)
                endedEffects.append(e);

            framesProcessed = qMax(effFrames, framesProcessed);
        }

        foreach (qtauAudioSource *t, tracks)
        {
            qint64 trFrames = 0;
            qint64 numCh = (t->getAudioFormat().channelCount() > 1) ? 2 : 1;

            switch (t->getAudioFormat().sampleSize())
            {
            case 8:  effFrames = mixSamplesT<qint8>  (e->read(frames     * numCh), buf, numCh > 1, frames); break;
            case 16: effFrames = mixSamplesT<quint16>(e->read(frames * 2 * numCh), buf, numCh > 1, frames); break;
            case 32: effFrames = mixSamplesT<float>  (e->read(frames * 4 * numCh), buf, numCh > 1, frames); break;
            default:
                vsLog::e("Sound mixer is processing an effect with unsupported sample size, dropping.");
                endedTracks.append(t);
            }

            if (trFrames < frames)
                endedTracks.append(t);

            framesProcessed = qMax(trFrames, framesProcessed);
        }

        //-- cleanup ---------------------------------

        if (!endedEffects.isEmpty())
        {
            foreach (qtauAudioSource *e, endedEffects)
            {
                emit effectEnded(e);
                effects.removeOne(e);
            }

            if (effects.isEmpty())
            {
                emit allEffectsEnded();
                replacingEffectsSmoothly = false;
            }
        }

        if (!endedTracks.isEmpty())
        {
            foreach (qtauAudioSource *t, endedTracks)
            {
                emit trackEnded(t);
                tracks.removeOne(t);
            }

            if (tracks.isEmpty())
            {
                emit allTracksEnded();
                replacingTracksSmoothly = false;
            }
        }

        result = framesProcessed * 4; // 4 bytes per frame (16LE stereo, always)

        if (result > 0)
            memcpy(data, buf.data(), result);
    }

    return result;
}
<commit_msg>oh what the<commit_after>/* Mixer.cpp from QTau http://github.com/qtau-devgroup/editor by digited and HAL@ShurabaP, BSD license */

#include "audio/Mixer.h"
#include "Utils.h"
#include <math.h>
#include <qendian.h>
#include <QDebug>

#ifndef M_SQRT1_2
    #define M_SQRT1_2	0.70710678118654752440
#endif

qtauSoundMixer::qtauSoundMixer(QObject *parent) :
    qtauAudioSource(parent), replacingEffectsSmoothly(false), replacingTracksSmoothly(false)
{
    fmt.setByteOrder(QAudioFormat::LittleEndian);
    fmt.setCodec("audio/pcm");
    fmt.setChannelCount(2);
    fmt.setSampleRate(44100);
    fmt.setSampleSize(16);
    fmt.setSampleType(QAudioFormat::SignedInt);

    open(QIODevice::ReadOnly);
}

qtauSoundMixer::qtauSoundMixer(QList<qtauAudioSource*> &tracks, QObject *parent) :
    qtauAudioSource(parent), replacingEffectsSmoothly(false), replacingTracksSmoothly(false)
{
    foreach (qtauAudioSource *a, tracks)
        addTrack(a);
}

qint64 qtauSoundMixer::bytesAvailable() const
{
    qint64 result = 0;

    if (!atEnd())
    {
        foreach (qtauAudioSource *s, effects) result = qMax(s->bytesAvailable(), result);
        foreach (qtauAudioSource *s, tracks)  result = qMax(s->bytesAvailable(), result);
    }

    return result;
}

void qtauSoundMixer::addTrack(qtauAudioSource *t, bool replace, bool smoothly)
{
    if (t && t->size())
    {
        if (!t->isReadable())
            t->open(QIODevice::ReadOnly);

        if (t->isReadable())
        {
            if (replace)
            {
                if (smoothly) replacingTracksSmoothly = true;
                else          tracks.clear();
            }

            tracks.append(t);
        }
        else vsLog::e("Sound mixer could not open a track for reading, adding cancelled.");
    }
    else vsLog::e("Sound mixer can't add an empty track!");
}

void qtauSoundMixer::addEffect(qtauAudioSource *e, bool replace, bool smoothly)
{
    if (e && e->size())
    {
        if (!e->isReadable())
            e->open(QIODevice::ReadOnly);

        if (e->isReadable())
        {
            if (replace)
            {
                if (smoothly) replacingEffectsSmoothly = true;
                else          effects.clear();
            }

            effects.append(e);
        }
        else vsLog::e("Sound mixer could not open an effect for reading, adding cancelled.");
    }
    else vsLog::e("Sound mixer can't add an empty effect!");
}

template<typename T> int sampleTS16(const T &s) { return s; }

template<typename T = quint8> int sampleToS16(const quint8 &s) { return ((float)s - 128.0) / 127.0 * 32767.0;  }
template<typename T = qint16> int sampleToS16(const qint16 &s) { return s;                                     }
template<typename T = qint32> int sampleToS16(const qint32 &s) { float *fS = (float*)&s; return *fS * 32767.0; }

template<class T> inline qint64 mixSamplesT(const QByteArray &src, QByteArray &dst, bool stereo, qint64 n)
{
    const int sT = sizeof(T);
    const int sT2 = sT * 2;

    qint64 nBytes = stereo ? (sT2 * n) : (sT * n);
    qint64 max = qMin((qint64)src.size(), nBytes);
    n = stereo ? (max / sT2) : (max / sT);

    const char *inData  = src.data();
    char       *outData = dst.data();
    T       *srcS;
    qint16 *dstS;
    int iS, iD;

    if (stereo)
    {
        for (qint64 bS = 0, bD = 0; bS < max; bS += sT2, bD += 4)
        {
            // left channel
            srcS = (T*)     (inData  + bS);
            dstS = (qint16*)(outData + bD);
            iS = sampleToS16(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);

            // right channel - a bit redundant, but twice less cycles easily
            srcS = (T*)     (inData  + bS + 2);
            dstS = (qint16*)(outData + bD + 2);
            iS = sampleToS16<T>(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);
        }
    }
    else
    {
        for (qint64 bS = 0, bD = 0; bS < max; bS += sT, bD += 4)
        {
            // left channel
            srcS = (T*)     (inData  + bS);
            dstS = (qint16*)(outData + bD);
            iS = sampleToS16<T>(*srcS);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);

            // right channel - dest val may be different
            dstS = (qint16*)(outData + bD + 2);
            iD = *dstS;

            iD = M_SQRT1_2 * (iS + iD);
            *dstS = qMax(qMin(iD, SHRT_MAX), SHRT_MIN);
        }
    }

    return n;
}

qint64 qtauSoundMixer::readData(char *data, qint64 maxlen)
{
    qint64 result = 0;
    qint64 frames = maxlen / 4;
    qint64 truncated = maxlen - frames*4;
    qint64 framesProcessed = 0;

    if (truncated > 0)
        vsLog::d(QString("Sound mixer was asked to give %1 bytes, %2 more than equeal to frame size!")
                         .arg(maxlen).arg(truncated));

    maxlen -= truncated;

    if (maxlen > 0) // mix samples of all tracks and effects
    {
        /*
         * all audios are considered to be open for reading, U8 or S16 or F32 LE, mono or stereo, 44100Hz
         * output from readData is considered to be 16LE stereo, 4 bytes per frame (2*2)
         * need to read same amount of frames from all tracks and sources, and if any one is giving less, it's ended
         * signal ended audios so that they may be released
         * */

        if (buffer().size() < maxlen)
            buffer().resize(maxlen);

        QByteArray &buf = buffer();
        memset(buf.data(), 0, maxlen);

        QList<qtauAudioSource*> endedEffects;
        QList<qtauAudioSource*> endedTracks;

        // cycle all effects and tracks and try to get required amount of frames from them
        foreach (qtauAudioSource *e, effects)
        {
            qint64 effFrames = 0;
            qint64 numCh = (e->getAudioFormat().channelCount() > 1) ? 2 : 1;

            switch (e->getAudioFormat().sampleSize())
            {
            case 8:  effFrames = mixSamplesT<quint8>(e->read(frames     * numCh), buf, numCh > 1, frames); break;
            case 16: effFrames = mixSamplesT<qint16>(e->read(frames * 2 * numCh), buf, numCh > 1, frames); break;
            case 32: effFrames = mixSamplesT<qint32>(e->read(frames * 4 * numCh), buf, numCh > 1, frames); break;
            default:
                vsLog::e("Sound mixer is processing an effect with unsupported sample size, dropping.");
                endedEffects.append(e);
            }

            if (effFrames < frames)
                endedEffects.append(e);

            framesProcessed = qMax(effFrames, framesProcessed);
        }

        foreach (qtauAudioSource *t, tracks)
        {
            qint64 trFrames = 0;
            qint64 numCh = (t->getAudioFormat().channelCount() > 1) ? 2 : 1;

            switch (t->getAudioFormat().sampleSize())
            {
            case 8:  trFrames = mixSamplesT<quint8>(t->read(frames     * numCh), buf, numCh > 1, frames); break;
            case 16: trFrames = mixSamplesT<qint16>(t->read(frames * 2 * numCh), buf, numCh > 1, frames); break;
            case 32: trFrames = mixSamplesT<qint32>(t->read(frames * 4 * numCh), buf, numCh > 1, frames); break;
            default:
                vsLog::e("Sound mixer is processing an effect with unsupported sample size, dropping.");
                endedTracks.append(t);
            }

            if (trFrames < frames)
                endedTracks.append(t);

            framesProcessed = qMax(trFrames, framesProcessed);
        }

        //-- cleanup ---------------------------------

        if (!endedEffects.isEmpty())
        {
            foreach (qtauAudioSource *e, endedEffects)
            {
                emit effectEnded(e);
                effects.removeOne(e);
            }

            if (effects.isEmpty())
            {
                emit allEffectsEnded();
                replacingEffectsSmoothly = false;
            }
        }

        if (!endedTracks.isEmpty())
        {
            foreach (qtauAudioSource *t, endedTracks)
            {
                emit trackEnded(t);
                tracks.removeOne(t);
            }

            if (tracks.isEmpty())
            {
                emit allTracksEnded();
                replacingTracksSmoothly = false;
            }
        }

        result = framesProcessed * 4; // 4 bytes per frame (16LE stereo, always)

        if (result > 0)
            memcpy(data, buf.data(), result);
    }

    return result;
}
<|endoftext|>


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

#include <configuration.hpp>
#include <CommandLine.hpp>
#include <Kernels.hpp>
#include <Memory.hpp>
#include <Pipeline.hpp>

int main(int argc, char *argv[])
{
    // Command line options
    Options options{};
    DeviceOptions deviceOptions{};
    DataOptions dataOptions{};
    GeneratorOptions generatorOptions{};
    // Memory
    HostMemory hostMemory{};
    DeviceMemory deviceMemory{};
    HostMemoryDumpFiles hostMemoryDumpFiles{};
    // OpenCL kernels
    OpenCLRunTime openclRunTime{};
    KernelConfigurations kernelConfigurations{};
    Kernels kernels{};
    KernelRunTimeConfigurations kernelRunTimeConfigurations{};
    // Timers
    Timers timers{};
    // Observation
    AstroData::Observation observation;

    // Process command line arguments
    isa::utils::ArgumentList args(argc, argv);
    try
    {
        processCommandLineOptions(args, options, deviceOptions, dataOptions, hostMemoryDumpFiles, kernelConfigurations, generatorOptions, observation);
    }
    catch (std::exception &err)
    {
        return 1;
    }

    // Load or generate input data
    try
    {
        hostMemory.input.resize(observation.getNrBeams());
        if (dataOptions.dataLOFAR || dataOptions.dataSIGPROC || dataOptions.dataPSRDADA)
        {
            loadInput(observation, deviceOptions, dataOptions, hostMemory, timers);
        }
        else
        {
            for (unsigned int beam = 0; beam < observation.getNrBeams(); beam++)
            {
                // TODO: if there are multiple synthesized beams, the generated data should take this into account
                hostMemory.input.at(beam) = new std::vector<std::vector<inputDataType> *>(observation.getNrBatches());
                AstroData::generateSinglePulse(generatorOptions.width, generatorOptions.DM, observation, deviceOptions.padding.at(deviceOptions.deviceName), *(hostMemory.input.at(beam)), inputBits, generatorOptions.random);
            }
        }
        try
        {
            hostMemory.zappedChannels.resize(observation.getNrChannels(deviceOptions.padding.at(deviceOptions.deviceName) / sizeof(unsigned int)));
        }
        catch (std::out_of_range &err)
        {
            std::cerr << "No padding specified for " << deviceOptions.deviceName << "." << std::endl;
            return 1;
        }
        try
        {
            AstroData::readZappedChannels(observation, dataOptions.channelsFile, hostMemory.zappedChannels);
            AstroData::readIntegrationSteps(observation, dataOptions.integrationFile, hostMemory.integrationSteps);
        }
        catch (AstroData::FileError &err)
        {
            std::cerr << err.what() << std::endl;
            return 1;
        }
    }
    catch (std::exception &err)
    {
        std::cerr << err.what() << std::endl;
        return 1;
    }

    // Print message with observation and search information
    if (options.print)
    {
        std::cout << "Device: " << deviceOptions.deviceName << " (" + std::to_string(deviceOptions.platformID) + ", ";
        std::cout << std::to_string(deviceOptions.deviceID) + ")" << std::endl;
        std::cout << "Padding: " << deviceOptions.padding[deviceOptions.deviceName] << " bytes" << std::endl;
        std::cout << std::endl;
        std::cout << "Beams: " << observation.getNrBeams() << std::endl;
        std::cout << "Synthesized Beams: " << observation.getNrSynthesizedBeams() << std::endl;
        std::cout << "Batches: " << observation.getNrBatches() << std::endl;
        std::cout << "Samples: " << observation.getNrSamplesPerBatch() << std::endl;
        std::cout << "Sampling time: " << observation.getSamplingTime() << std::endl;
        std::cout << "Frequency range: " << observation.getMinFreq() << " MHz, " << observation.getMaxFreq() << " MHz";
        std::cout << std::endl;
        std::cout << "Subbands: " << observation.getNrSubbands() << " (" << observation.getSubbandBandwidth() << " MHz)";
        std::cout << std::endl;
        std::cout << "Channels: " << observation.getNrChannels() << " (" << observation.getChannelBandwidth() << " MHz)";
        std::cout << std::endl;
        std::cout << "Zapped Channels: " << observation.getNrZappedChannels() << std::endl;
        std::cout << "Integration steps: " << hostMemory.integrationSteps.size() << std::endl;
        if (options.subbandDedispersion)
        {
            std::cout << "Subbanding DMs: " << observation.getNrDMs(true) << " (" << observation.getFirstDM(true) << ", ";
            std::cout << observation.getFirstDM(true) + ((observation.getNrDMs(true) - 1) * observation.getDMStep(true));
            std::cout << ")" << std::endl;
        }
        std::cout << "DMs: " << observation.getNrDMs() << " (" << observation.getFirstDM() << ", ";
        std::cout << observation.getFirstDM() + ((observation.getNrDMs() - 1) * observation.getDMStep()) << ")";
        std::cout << std::endl;
        std::cout << std::endl;
    }

    // Initialize OpenCL
    openclRunTime.context = new cl::Context();
    openclRunTime.platforms = new std::vector<cl::Platform>();
    openclRunTime.devices = new std::vector<cl::Device>();
    openclRunTime.queues = new std::vector<std::vector<cl::CommandQueue>>();
    try
    {
        isa::OpenCL::initializeOpenCL(deviceOptions.platformID, 1, openclRunTime.platforms, openclRunTime.context, openclRunTime.devices, openclRunTime.queues);
    }
    catch (isa::OpenCL::OpenCLError &err)
    {
        std::cerr << err.what() << std::endl;
        return 1;
    }

    // Memory allocation
    allocateHostMemory(observation, options, deviceOptions, dataOptions, kernelConfigurations, hostMemory);
    if (observation.getNrDelayBatches() > observation.getNrBatches())
    {
        std::cerr << "Not enough input batches for the specified search." << std::endl;
        return 1;
    }
    try
    {
        allocateDeviceMemory(observation, openclRunTime, options, deviceOptions, hostMemory, deviceMemory);
    }
    catch (cl::Error &err)
    {
        std::cerr << "Memory error: " << err.what() << " " << err.err() << std::endl;
        return 1;
    }

    // Generate OpenCL kernels
    try
    {
        generateOpenCLKernels(openclRunTime, observation, options, deviceOptions, kernelConfigurations, hostMemory, deviceMemory, kernels);
    }
    catch (std::exception &err)
    {
        std::cerr << "OpenCL code generation error: " << err.what() << std::endl;
        return 1;
    }

    // Generate run time configurations for the OpenCL kernels
    generateOpenCLRunTimeConfigurations(observation, options, deviceOptions, kernelConfigurations, hostMemory, kernelRunTimeConfigurations);

    // Search loop
    pipeline(openclRunTime, observation, options, deviceOptions, dataOptions, timers, kernels, kernelConfigurations, kernelRunTimeConfigurations, hostMemory, deviceMemory, hostMemoryDumpFiles);

    // Store performance statistics before shutting down
    std::ofstream outputStats;
    outputStats.open(dataOptions.outputFile + ".stats");
    outputStats << std::fixed << std::setprecision(6);
    outputStats << "# nrDMs" << std::endl;
    if (options.subbandDedispersion)
    {
        outputStats << observation.getNrDMs(true) * observation.getNrDMs() << std::endl;
    }
    else
    {
        outputStats << observation.getNrDMs() << std::endl;
    }
    outputStats << "# timers.inputLoad" << std::endl;
    outputStats << timers.inputLoad.getTotalTime() << std::endl;
    outputStats << "# timers.search" << std::endl;
    outputStats << timers.search.getTotalTime() << std::endl;
    outputStats << "# inputHandlingTotal inputHandlingAvg err" << std::endl;
    outputStats << timers.inputHandling.getTotalTime() << " " << timers.inputHandling.getAverageTime() << " ";
    outputStats << timers.inputHandling.getStandardDeviation() << std::endl;
    outputStats << "# inputCopyTotal inputCopyAvg err" << std::endl;
    outputStats << timers.inputCopy.getTotalTime() << " " << timers.inputCopy.getAverageTime() << " ";
    outputStats << timers.inputCopy.getStandardDeviation() << std::endl;
    if (!options.subbandDedispersion)
    {
        outputStats << "# dedispersionSingleStepTotal dedispersionSingleStepAvg err" << std::endl;
        outputStats << timers.dedispersionSingleStep.getTotalTime() << " ";
        outputStats << timers.dedispersionSingleStep.getAverageTime() << " ";
        outputStats << timers.dedispersionSingleStep.getStandardDeviation() << std::endl;
    }
    else
    {
        outputStats << "# dedispersionStepOneTotal dedispersionStepOneAvg err" << std::endl;
        outputStats << timers.dedispersionStepOne.getTotalTime() << " ";
        outputStats << timers.dedispersionStepOne.getAverageTime() << " ";
        outputStats << timers.dedispersionStepOne.getStandardDeviation() << std::endl;
        outputStats << "# dedispersionStepTwoTotal dedispersionStepTwoAvg err" << std::endl;
        outputStats << timers.dedispersionStepTwo.getTotalTime() << " ";
        outputStats << timers.dedispersionStepTwo.getAverageTime() << " ";
        outputStats << timers.dedispersionStepTwo.getStandardDeviation() << std::endl;
    }
    outputStats << "# integrationTotal integrationAvg err" << std::endl;
    outputStats << timers.integration.getTotalTime() << " " << timers.integration.getAverageTime() << " ";
    outputStats << timers.integration.getStandardDeviation() << std::endl;
    if (options.snrMode == SNRMode::Standard)
    {
        outputStats << "# snrTotal snrAvg err" << std::endl;
        outputStats << timers.snr.getTotalTime() << " " << timers.snr.getAverageTime() << " ";
        outputStats << timers.snr.getStandardDeviation() << std::endl;
    }
    else if (options.snrMode == SNRMode::Momad)
    {
        outputStats << "# maxTotal maxAvg err" << std::endl;
        outputStats << timers.max.getTotalTime() << " " << timers.max.getAverageTime() << " ";
        outputStats << timers.max.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansStepOneTotal medianOfMediansStepOneAvg err" << std::endl;
        outputStats << timers.medianOfMediansStepOne.getTotalTime() << " " << timers.medianOfMediansStepOne.getAverageTime() << " ";
        outputStats << timers.medianOfMediansStepOne.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansStepTwoTotal medianOfMediansStepTwoAvg err" << std::endl;
        outputStats << timers.medianOfMediansStepTwo.getTotalTime() << " " << timers.medianOfMediansStepTwo.getAverageTime() << " ";
        outputStats << timers.medianOfMediansStepTwo.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansAbsoluteDeviationStepOneTotal medianOfMediansAbsoluteDeviationStepOneAvg err" << std::endl;
        outputStats << timers.medianOfMediansAbsoluteDeviationStepOne.getTotalTime() << " " << timers.medianOfMediansAbsoluteDeviationStepOne.getAverageTime() << " ";
        outputStats << timers.medianOfMediansAbsoluteDeviationStepOne.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansAbsoluteDeviationStepTwoTotal medianOfMediansAbsoluteDeviationStepTwoAvg err" << std::endl;
        outputStats << timers.medianOfMediansAbsoluteDeviationStepTwo.getTotalTime() << " " << timers.medianOfMediansAbsoluteDeviationStepTwo.getAverageTime() << " ";
        outputStats << timers.medianOfMediansAbsoluteDeviationStepTwo.getStandardDeviation() << std::endl;
    }
    outputStats << "# outputCopyTotal outputCopyAvg err" << std::endl;
    outputStats << timers.outputCopy.getTotalTime() << " " << timers.outputCopy.getAverageTime() << " ";
    outputStats << timers.outputCopy.getStandardDeviation() << std::endl;
    outputStats << "# triggerTimeTotal triggerTimeAvg err" << std::endl;
    outputStats << timers.trigger.getTotalTime() << " " << timers.trigger.getAverageTime() << " ";
    outputStats << timers.trigger.getStandardDeviation() << std::endl;
    outputStats.close();

    return 0;
}
<commit_msg>Forgot to remove a parameter.<commit_after>// Copyright 2017 Netherlands Institute for Radio Astronomy (ASTRON)
// Copyright 2017 Netherlands eScience Center
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <configuration.hpp>
#include <CommandLine.hpp>
#include <Kernels.hpp>
#include <Memory.hpp>
#include <Pipeline.hpp>

int main(int argc, char *argv[])
{
    // Command line options
    Options options{};
    DeviceOptions deviceOptions{};
    DataOptions dataOptions{};
    GeneratorOptions generatorOptions{};
    // Memory
    HostMemory hostMemory{};
    DeviceMemory deviceMemory{};
    HostMemoryDumpFiles hostMemoryDumpFiles{};
    // OpenCL kernels
    OpenCLRunTime openclRunTime{};
    KernelConfigurations kernelConfigurations{};
    Kernels kernels{};
    KernelRunTimeConfigurations kernelRunTimeConfigurations{};
    // Timers
    Timers timers{};
    // Observation
    AstroData::Observation observation;

    // Process command line arguments
    isa::utils::ArgumentList args(argc, argv);
    try
    {
        processCommandLineOptions(args, options, deviceOptions, dataOptions, hostMemoryDumpFiles, kernelConfigurations, generatorOptions, observation);
    }
    catch (std::exception &err)
    {
        return 1;
    }

    // Load or generate input data
    try
    {
        hostMemory.input.resize(observation.getNrBeams());
        if (dataOptions.dataLOFAR || dataOptions.dataSIGPROC || dataOptions.dataPSRDADA)
        {
            loadInput(observation, deviceOptions, dataOptions, hostMemory, timers);
        }
        else
        {
            for (unsigned int beam = 0; beam < observation.getNrBeams(); beam++)
            {
                // TODO: if there are multiple synthesized beams, the generated data should take this into account
                hostMemory.input.at(beam) = new std::vector<std::vector<inputDataType> *>(observation.getNrBatches());
                AstroData::generateSinglePulse(generatorOptions.width, generatorOptions.DM, observation, deviceOptions.padding.at(deviceOptions.deviceName), *(hostMemory.input.at(beam)), inputBits, generatorOptions.random);
            }
        }
        try
        {
            hostMemory.zappedChannels.resize(observation.getNrChannels(deviceOptions.padding.at(deviceOptions.deviceName) / sizeof(unsigned int)));
        }
        catch (std::out_of_range &err)
        {
            std::cerr << "No padding specified for " << deviceOptions.deviceName << "." << std::endl;
            return 1;
        }
        try
        {
            AstroData::readZappedChannels(observation, dataOptions.channelsFile, hostMemory.zappedChannels);
            AstroData::readIntegrationSteps(observation, dataOptions.integrationFile, hostMemory.integrationSteps);
        }
        catch (AstroData::FileError &err)
        {
            std::cerr << err.what() << std::endl;
            return 1;
        }
    }
    catch (std::exception &err)
    {
        std::cerr << err.what() << std::endl;
        return 1;
    }

    // Print message with observation and search information
    if (options.print)
    {
        std::cout << "Device: " << deviceOptions.deviceName << " (" + std::to_string(deviceOptions.platformID) + ", ";
        std::cout << std::to_string(deviceOptions.deviceID) + ")" << std::endl;
        std::cout << "Padding: " << deviceOptions.padding[deviceOptions.deviceName] << " bytes" << std::endl;
        std::cout << std::endl;
        std::cout << "Beams: " << observation.getNrBeams() << std::endl;
        std::cout << "Synthesized Beams: " << observation.getNrSynthesizedBeams() << std::endl;
        std::cout << "Batches: " << observation.getNrBatches() << std::endl;
        std::cout << "Samples: " << observation.getNrSamplesPerBatch() << std::endl;
        std::cout << "Sampling time: " << observation.getSamplingTime() << std::endl;
        std::cout << "Frequency range: " << observation.getMinFreq() << " MHz, " << observation.getMaxFreq() << " MHz";
        std::cout << std::endl;
        std::cout << "Subbands: " << observation.getNrSubbands() << " (" << observation.getSubbandBandwidth() << " MHz)";
        std::cout << std::endl;
        std::cout << "Channels: " << observation.getNrChannels() << " (" << observation.getChannelBandwidth() << " MHz)";
        std::cout << std::endl;
        std::cout << "Zapped Channels: " << observation.getNrZappedChannels() << std::endl;
        std::cout << "Integration steps: " << hostMemory.integrationSteps.size() << std::endl;
        if (options.subbandDedispersion)
        {
            std::cout << "Subbanding DMs: " << observation.getNrDMs(true) << " (" << observation.getFirstDM(true) << ", ";
            std::cout << observation.getFirstDM(true) + ((observation.getNrDMs(true) - 1) * observation.getDMStep(true));
            std::cout << ")" << std::endl;
        }
        std::cout << "DMs: " << observation.getNrDMs() << " (" << observation.getFirstDM() << ", ";
        std::cout << observation.getFirstDM() + ((observation.getNrDMs() - 1) * observation.getDMStep()) << ")";
        std::cout << std::endl;
        std::cout << std::endl;
    }

    // Initialize OpenCL
    openclRunTime.context = new cl::Context();
    openclRunTime.platforms = new std::vector<cl::Platform>();
    openclRunTime.devices = new std::vector<cl::Device>();
    openclRunTime.queues = new std::vector<std::vector<cl::CommandQueue>>();
    try
    {
        isa::OpenCL::initializeOpenCL(deviceOptions.platformID, 1, openclRunTime.platforms, openclRunTime.context, openclRunTime.devices, openclRunTime.queues);
    }
    catch (isa::OpenCL::OpenCLError &err)
    {
        std::cerr << err.what() << std::endl;
        return 1;
    }

    // Memory allocation
    allocateHostMemory(observation, options, deviceOptions, dataOptions, kernelConfigurations, hostMemory);
    if (observation.getNrDelayBatches() > observation.getNrBatches())
    {
        std::cerr << "Not enough input batches for the specified search." << std::endl;
        return 1;
    }
    try
    {
        allocateDeviceMemory(observation, openclRunTime, options, deviceOptions, hostMemory, deviceMemory);
    }
    catch (cl::Error &err)
    {
        std::cerr << "Memory error: " << err.what() << " " << err.err() << std::endl;
        return 1;
    }

    // Generate OpenCL kernels
    try
    {
        generateOpenCLKernels(openclRunTime, observation, options, deviceOptions, kernelConfigurations, hostMemory, deviceMemory, kernels);
    }
    catch (std::exception &err)
    {
        std::cerr << "OpenCL code generation error: " << err.what() << std::endl;
        return 1;
    }

    // Generate run time configurations for the OpenCL kernels
    generateOpenCLRunTimeConfigurations(observation, options, deviceOptions, kernelConfigurations, hostMemory, kernelRunTimeConfigurations);

    // Search loop
    pipeline(openclRunTime, observation, options, deviceOptions, dataOptions, timers, kernels, kernelRunTimeConfigurations, hostMemory, deviceMemory, hostMemoryDumpFiles);

    // Store performance statistics before shutting down
    std::ofstream outputStats;
    outputStats.open(dataOptions.outputFile + ".stats");
    outputStats << std::fixed << std::setprecision(6);
    outputStats << "# nrDMs" << std::endl;
    if (options.subbandDedispersion)
    {
        outputStats << observation.getNrDMs(true) * observation.getNrDMs() << std::endl;
    }
    else
    {
        outputStats << observation.getNrDMs() << std::endl;
    }
    outputStats << "# timers.inputLoad" << std::endl;
    outputStats << timers.inputLoad.getTotalTime() << std::endl;
    outputStats << "# timers.search" << std::endl;
    outputStats << timers.search.getTotalTime() << std::endl;
    outputStats << "# inputHandlingTotal inputHandlingAvg err" << std::endl;
    outputStats << timers.inputHandling.getTotalTime() << " " << timers.inputHandling.getAverageTime() << " ";
    outputStats << timers.inputHandling.getStandardDeviation() << std::endl;
    outputStats << "# inputCopyTotal inputCopyAvg err" << std::endl;
    outputStats << timers.inputCopy.getTotalTime() << " " << timers.inputCopy.getAverageTime() << " ";
    outputStats << timers.inputCopy.getStandardDeviation() << std::endl;
    if (!options.subbandDedispersion)
    {
        outputStats << "# dedispersionSingleStepTotal dedispersionSingleStepAvg err" << std::endl;
        outputStats << timers.dedispersionSingleStep.getTotalTime() << " ";
        outputStats << timers.dedispersionSingleStep.getAverageTime() << " ";
        outputStats << timers.dedispersionSingleStep.getStandardDeviation() << std::endl;
    }
    else
    {
        outputStats << "# dedispersionStepOneTotal dedispersionStepOneAvg err" << std::endl;
        outputStats << timers.dedispersionStepOne.getTotalTime() << " ";
        outputStats << timers.dedispersionStepOne.getAverageTime() << " ";
        outputStats << timers.dedispersionStepOne.getStandardDeviation() << std::endl;
        outputStats << "# dedispersionStepTwoTotal dedispersionStepTwoAvg err" << std::endl;
        outputStats << timers.dedispersionStepTwo.getTotalTime() << " ";
        outputStats << timers.dedispersionStepTwo.getAverageTime() << " ";
        outputStats << timers.dedispersionStepTwo.getStandardDeviation() << std::endl;
    }
    outputStats << "# integrationTotal integrationAvg err" << std::endl;
    outputStats << timers.integration.getTotalTime() << " " << timers.integration.getAverageTime() << " ";
    outputStats << timers.integration.getStandardDeviation() << std::endl;
    if (options.snrMode == SNRMode::Standard)
    {
        outputStats << "# snrTotal snrAvg err" << std::endl;
        outputStats << timers.snr.getTotalTime() << " " << timers.snr.getAverageTime() << " ";
        outputStats << timers.snr.getStandardDeviation() << std::endl;
    }
    else if (options.snrMode == SNRMode::Momad)
    {
        outputStats << "# maxTotal maxAvg err" << std::endl;
        outputStats << timers.max.getTotalTime() << " " << timers.max.getAverageTime() << " ";
        outputStats << timers.max.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansStepOneTotal medianOfMediansStepOneAvg err" << std::endl;
        outputStats << timers.medianOfMediansStepOne.getTotalTime() << " " << timers.medianOfMediansStepOne.getAverageTime() << " ";
        outputStats << timers.medianOfMediansStepOne.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansStepTwoTotal medianOfMediansStepTwoAvg err" << std::endl;
        outputStats << timers.medianOfMediansStepTwo.getTotalTime() << " " << timers.medianOfMediansStepTwo.getAverageTime() << " ";
        outputStats << timers.medianOfMediansStepTwo.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansAbsoluteDeviationStepOneTotal medianOfMediansAbsoluteDeviationStepOneAvg err" << std::endl;
        outputStats << timers.medianOfMediansAbsoluteDeviationStepOne.getTotalTime() << " " << timers.medianOfMediansAbsoluteDeviationStepOne.getAverageTime() << " ";
        outputStats << timers.medianOfMediansAbsoluteDeviationStepOne.getStandardDeviation() << std::endl;
        outputStats << "# medianOfMediansAbsoluteDeviationStepTwoTotal medianOfMediansAbsoluteDeviationStepTwoAvg err" << std::endl;
        outputStats << timers.medianOfMediansAbsoluteDeviationStepTwo.getTotalTime() << " " << timers.medianOfMediansAbsoluteDeviationStepTwo.getAverageTime() << " ";
        outputStats << timers.medianOfMediansAbsoluteDeviationStepTwo.getStandardDeviation() << std::endl;
    }
    outputStats << "# outputCopyTotal outputCopyAvg err" << std::endl;
    outputStats << timers.outputCopy.getTotalTime() << " " << timers.outputCopy.getAverageTime() << " ";
    outputStats << timers.outputCopy.getStandardDeviation() << std::endl;
    outputStats << "# triggerTimeTotal triggerTimeAvg err" << std::endl;
    outputStats << timers.trigger.getTotalTime() << " " << timers.trigger.getAverageTime() << " ";
    outputStats << timers.trigger.getStandardDeviation() << std::endl;
    outputStats.close();

    return 0;
}
<|endoftext|>


<commit_before>/**
 * Unit tests for MARS kernel class
 * @author Vyacheslav Kompan kompan.vo@phystech.edu
 * Copyright 2018 MIPT-MIPS
 */

#include "kernel/mars/mars_kernel.h"
#include <simulator.h>

#include <catch.hpp>

#include <cstdio>
#include <sstream>

static const uint8 v0 = 2;
static const uint8 a0 = 4;
static const uint8 a1 = 5;
static const uint8 a2 = 6;

TEST_CASE( "MARS: construct kernel") {
    CHECK_NOTHROW( create_mars_kernel());
}

TEST_CASE( "MARS: print integer") {
    std::ostringstream output;
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( std::cin, output);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 1u); // print integer
    sim->write_cpu_register( a0, narrow_cast<uint64>( -1337));
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "-1337");
}

struct System
{
    std::shared_ptr<Simulator> sim = nullptr;
    std::shared_ptr<Kernel> mars_kernel = nullptr;
    std::shared_ptr<FuncMemory> mem = nullptr;

    template<typename ... KernelArgs>
    System( KernelArgs&&... args)
        : sim( Simulator::create_simulator( "mips64", true, false))
        , mars_kernel( create_mars_kernel(std::forward<KernelArgs>(args)...))
        , mem( FuncMemory::create_plain_memory( 24))
    {
        mars_kernel->set_simulator( sim);
        sim->set_memory( mem);
        mars_kernel->set_memory( mem);
    }
};

TEST_CASE( "MARS: print string")
{
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( "Hello World!", 0x1000);

    sys.sim->write_cpu_register( v0, 4u); // print character
    sys.sim->write_cpu_register( a0, 0x1000u);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Hello World!");
}

TEST_CASE( "MARS: read integer") {
    std::istringstream input( "1337\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 5u); // read integer
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sim->read_cpu_register( v0) == 1337);
}

TEST_CASE( "MARS: read bad integer") {
    std::istringstream input( "133q\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 5u); // read integer
    CHECK_THROWS_AS( mars_kernel->execute(), BadInputValue);
}

TEST_CASE( "MARS: read string")
{
    std::istringstream input( "Hello World\n");
    System sys( input);
    sys.sim->write_cpu_register( v0, 8u); // read string
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 0x5);
    CHECK_NOTHROW( sys.mars_kernel->execute());
    CHECK( sys.mem->read_string(0x1000u) == "Hello");
}

TEST_CASE( "MARS: exit") {
    auto sim = Simulator::create_simulator ("mips64", true, false);
    auto mars_kernel = create_mars_kernel( );
    mars_kernel->set_simulator (sim);

    sim->write_cpu_register( v0, 10u); // exit
    CHECK( mars_kernel->execute().type == SyscallResult::HALT);
    CHECK( mars_kernel->execute().code == 0);
}

TEST_CASE( "MARS: print character") {
    std::ostringstream output;
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( std::cin, output);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 11u); // print character
    sim->write_cpu_register( a0, uint64{ 'x'});
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "x");
}

TEST_CASE( "MARS: read character") {
    std::istringstream input( "z\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 12u); // read character
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sim->read_cpu_register( v0) == 'z');
}

TEST_CASE( "MARS: read bad character") {
    std::istringstream input( "zz\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 12u); // read character
    CHECK_THROWS_AS( mars_kernel->execute(), BadInputValue);
}

TEST_CASE( "MARS: read from stdin")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 0);   // stdin
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.mem->read_string(0x1000u) == "Lorem");
}

TEST_CASE( "MARS: read from stdout")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 1);   // stdin
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
    CHECK( sys.mem->read_string(0x1000u) == "");
}

TEST_CASE( "MARS: read from stderr")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 2);   // stderr
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
    CHECK( sys.mem->read_string(0x1000u) == "");
}

static void write_to_descriptor(System* sys, int desc)
{
    sys->mem->write_string( "Lorem Ipsum", 0x1000);
    sys->sim->write_cpu_register( v0, 15u); // write to file
    sys->sim->write_cpu_register( a0, desc); // stdout
    sys->sim->write_cpu_register( a1, 0x1000u);
    sys->sim->write_cpu_register( a2, 7);
}

TEST_CASE( "MARS: write to stdout")
{
    std::ostringstream output;
    System sys( std::cin, output, output);
    write_to_descriptor(&sys, 1);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Lorem I");
}

TEST_CASE( "MARS: write to stderr")
{
    std::ostringstream output;
    System sys( std::cin, output, output);
    write_to_descriptor(&sys, 2);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Lorem I");
}

TEST_CASE( "MARS: write to stdin")
{
    System sys( std::cin);
    write_to_descriptor(&sys, 0);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
}

TEST_CASE( "MARS: open non existing file")
{
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( "/ksagklhfgldg/sgsfgdsfgadg", 0x1000);
    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 1); // write
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
}

TEST_CASE( "MARS: open, write, read and close a file")
{
    std::string filename = std::tmpnam(nullptr);
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( filename, 0x1000);
    sys.mem->write_string( "Lorem Ipsum\n", 0x2000);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 1); // write
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    auto descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 15u); // write to file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x2000);
    sys.sim->write_cpu_register( a2, 11);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 9); // append
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 15u); // write to file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x2000);
    sys.sim->write_cpu_register( a2, 11);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 0); // read
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x3000);
    sys.sim->write_cpu_register( a2, 22);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.mem->read_string( 0x3000) == "Lorem IpsumLorem Ipsum");

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: close stdout")
{
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel();
    mars_kernel->set_simulator( sim);
    sim->write_cpu_register( v0, 16u); // close file
    sim->write_cpu_register( a0, 0);
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: close error")
{
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel();
    mars_kernel->set_simulator( sim);
    sim->write_cpu_register( v0, 16u); // close file
    sim->write_cpu_register( a0, 111);
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: exit with code")
{
    auto sim = Simulator::create_simulator ("mips64", true, false);
    auto mars_kernel = create_mars_kernel( );
    mars_kernel->set_simulator(sim);

    sim->write_cpu_register( v0, 17u); // exit
    sim->write_cpu_register( a0, 21u); // exit code
    CHECK( mars_kernel->execute().type == SyscallResult::HALT);
    CHECK( mars_kernel->execute().code == 21u);
}
<commit_msg>Do not use std::tmpnam<commit_after>/**
 * Unit tests for MARS kernel class
 * @author Vyacheslav Kompan kompan.vo@phystech.edu
 * Copyright 2018 MIPT-MIPS
 */

#include "kernel/mars/mars_kernel.h"
#include <simulator.h>

#include <catch.hpp>

#include <cstdio>
#include <sstream>

static const uint8 v0 = 2;
static const uint8 a0 = 4;
static const uint8 a1 = 5;
static const uint8 a2 = 6;

TEST_CASE( "MARS: construct kernel") {
    CHECK_NOTHROW( create_mars_kernel());
}

TEST_CASE( "MARS: print integer") {
    std::ostringstream output;
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( std::cin, output);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 1u); // print integer
    sim->write_cpu_register( a0, narrow_cast<uint64>( -1337));
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "-1337");
}

struct System
{
    std::shared_ptr<Simulator> sim = nullptr;
    std::shared_ptr<Kernel> mars_kernel = nullptr;
    std::shared_ptr<FuncMemory> mem = nullptr;

    template<typename ... KernelArgs>
    System( KernelArgs&&... args)
        : sim( Simulator::create_simulator( "mips64", true, false))
        , mars_kernel( create_mars_kernel(std::forward<KernelArgs>(args)...))
        , mem( FuncMemory::create_plain_memory( 24))
    {
        mars_kernel->set_simulator( sim);
        sim->set_memory( mem);
        mars_kernel->set_memory( mem);
    }
};

TEST_CASE( "MARS: print string")
{
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( "Hello World!", 0x1000);

    sys.sim->write_cpu_register( v0, 4u); // print character
    sys.sim->write_cpu_register( a0, 0x1000u);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Hello World!");
}

TEST_CASE( "MARS: read integer") {
    std::istringstream input( "1337\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 5u); // read integer
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sim->read_cpu_register( v0) == 1337);
}

TEST_CASE( "MARS: read bad integer") {
    std::istringstream input( "133q\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 5u); // read integer
    CHECK_THROWS_AS( mars_kernel->execute(), BadInputValue);
}

TEST_CASE( "MARS: read string")
{
    std::istringstream input( "Hello World\n");
    System sys( input);
    sys.sim->write_cpu_register( v0, 8u); // read string
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 0x5);
    CHECK_NOTHROW( sys.mars_kernel->execute());
    CHECK( sys.mem->read_string(0x1000u) == "Hello");
}

TEST_CASE( "MARS: exit") {
    auto sim = Simulator::create_simulator ("mips64", true, false);
    auto mars_kernel = create_mars_kernel( );
    mars_kernel->set_simulator (sim);

    sim->write_cpu_register( v0, 10u); // exit
    CHECK( mars_kernel->execute().type == SyscallResult::HALT);
    CHECK( mars_kernel->execute().code == 0);
}

TEST_CASE( "MARS: print character") {
    std::ostringstream output;
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( std::cin, output);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 11u); // print character
    sim->write_cpu_register( a0, uint64{ 'x'});
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "x");
}

TEST_CASE( "MARS: read character") {
    std::istringstream input( "z\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 12u); // read character
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sim->read_cpu_register( v0) == 'z');
}

TEST_CASE( "MARS: read bad character") {
    std::istringstream input( "zz\n");
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel( input);
    mars_kernel->set_simulator( sim);

    sim->write_cpu_register( v0, 12u); // read character
    CHECK_THROWS_AS( mars_kernel->execute(), BadInputValue);
}

TEST_CASE( "MARS: read from stdin")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 0);   // stdin
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.mem->read_string(0x1000u) == "Lorem");
}

TEST_CASE( "MARS: read from stdout")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 1);   // stdin
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
    CHECK( sys.mem->read_string(0x1000u) == "");
}

TEST_CASE( "MARS: read from stderr")
{
    std::istringstream input( "Lorem Ipsum\n");
    System sys( input);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, 2);   // stderr
    sys.sim->write_cpu_register( a1, 0x1000);
    sys.sim->write_cpu_register( a2, 5);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
    CHECK( sys.mem->read_string(0x1000u) == "");
}

static void write_to_descriptor(System* sys, int desc)
{
    sys->mem->write_string( "Lorem Ipsum", 0x1000);
    sys->sim->write_cpu_register( v0, 15u); // write to file
    sys->sim->write_cpu_register( a0, desc); // stdout
    sys->sim->write_cpu_register( a1, 0x1000u);
    sys->sim->write_cpu_register( a2, 7);
}

TEST_CASE( "MARS: write to stdout")
{
    std::ostringstream output;
    System sys( std::cin, output, output);
    write_to_descriptor(&sys, 1);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Lorem I");
}

TEST_CASE( "MARS: write to stderr")
{
    std::ostringstream output;
    System sys( std::cin, output, output);
    write_to_descriptor(&sys, 2);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( output.str() == "Lorem I");
}

TEST_CASE( "MARS: write to stdin")
{
    System sys( std::cin);
    write_to_descriptor(&sys, 0);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
}

TEST_CASE( "MARS: open non existing file")
{
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( "/ksagklhfgldg/sgsfgdsfgadg", 0x1000);
    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 1); // write
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.sim->read_cpu_register( v0) == all_ones<uint64>());
}

TEST_CASE( "MARS: open, write, read and close a file")
{
    std::string filename("tempfile");
    std::ostringstream output;
    System sys( std::cin, output);
    sys.mem->write_string( filename, 0x1000);
    sys.mem->write_string( "Lorem Ipsum\n", 0x2000);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 1); // write
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    auto descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 15u); // write to file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x2000);
    sys.sim->write_cpu_register( a2, 11);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 9); // append
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 15u); // write to file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x2000);
    sys.sim->write_cpu_register( a2, 11);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);

    sys.sim->write_cpu_register( v0, 13u); // open file
    sys.sim->write_cpu_register( a0, 0x1000u);
    sys.sim->write_cpu_register( a1, 0); // read
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    descriptor = sys.sim->read_cpu_register( v0);
    CHECK( descriptor >= 3);

    sys.sim->write_cpu_register( v0, 14u); // read from file
    sys.sim->write_cpu_register( a0, descriptor);
    sys.sim->write_cpu_register( a1, 0x3000);
    sys.sim->write_cpu_register( a2, 22);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
    CHECK( sys.mem->read_string( 0x3000) == "Lorem IpsumLorem Ipsum");

    sys.sim->write_cpu_register( v0, 16u); // close file
    sys.sim->write_cpu_register( a0, descriptor);
    CHECK( sys.mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: close stdout")
{
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel();
    mars_kernel->set_simulator( sim);
    sim->write_cpu_register( v0, 16u); // close file
    sim->write_cpu_register( a0, 0);
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: close error")
{
    auto sim = Simulator::create_simulator( "mips64", true, false);
    auto mars_kernel = create_mars_kernel();
    mars_kernel->set_simulator( sim);
    sim->write_cpu_register( v0, 16u); // close file
    sim->write_cpu_register( a0, 111);
    CHECK( mars_kernel->execute().type == SyscallResult::SUCCESS);
}

TEST_CASE( "MARS: exit with code")
{
    auto sim = Simulator::create_simulator ("mips64", true, false);
    auto mars_kernel = create_mars_kernel( );
    mars_kernel->set_simulator(sim);

    sim->write_cpu_register( v0, 17u); // exit
    sim->write_cpu_register( a0, 21u); // exit code
    CHECK( mars_kernel->execute().type == SyscallResult::HALT);
    CHECK( mars_kernel->execute().code == 21u);
}
<|endoftext|>