<commit_before>//=============================================================================================================
/**
* @file     mne_bem.cpp
* @author   Jana Kiesel <jana.kiesel@tu-ilmenau.de>;
*           Christoph Dinh <chdinh@nmr.mgh.harvard.edu>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     June, 2015
*
* @section  LICENSE
*
* Copyright (C) 2015, Jana Kiesel, Christoph Dinh and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    MNEBem class implementation.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "mne_bem.h"

#include <utils/mnemath.h>
#include <fs/label.h>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace UTILSLIB;
using namespace FSLIB;
using namespace MNELIB;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

MNEBem::MNEBem()
{
}

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

MNEBem::MNEBem(const MNEBem &p_MNEBem)
: m_qListBemSurface(p_MNEBem.m_qListBemSurface)
{

}

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

MNEBem::MNEBem(QIODevice &p_IODevice)   //const MNESourceSpace &p_MNESourceSpace
//: m_qListBemSurface()
{
    FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
    FiffDirTree t_Tree;
//    QList<FiffDirEntry>fiffDirEntries;

//    if(!t_pStream->open(t_Tree, fiffDirEntries))
//    {
//        qCritical() << "Could not open FIFF stream!";
////        return false;
//    }

    if(!MNEBem::readFromStream(t_pStream, true, t_Tree, *this))
    {
        t_pStream->device()->close();
        std::cout << "Could not read the source spaces\n"; // ToDo throw error
        //ToDo error(me,'Could not read the source spaces (%s)',mne_omit_first_line(lasterr));
//        return false;
    }

}

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

MNEBem::~MNEBem()
{

}

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

bool MNEBem::readFromStream(FiffStream::SPtr& p_pStream, bool add_geom, FiffDirTree& p_Tree, MNEBem& p_Bem)
{
    //
    //   Open the file, create directory
    //
    bool open_here = false;
    if (!p_pStream->device()->isOpen())
    {
        QList<FiffDirEntry> t_Dir;
        QString t_sFileName = p_pStream->streamName();

        QFile t_file(t_sFileName);//ToDo TCPSocket;
        p_pStream = FiffStream::SPtr(new FiffStream(&t_file));
        if(!p_pStream->open(p_Tree, t_Dir))
            return false;
        open_here = true;
//        if(t_pDir)
//            delete t_pDir;

        //
        //   Find all BEM surfaces
        //

        QList<FiffDirTree>bem = p_Tree.dir_tree_find(FIFFB_BEM);
        if(bem.isEmpty())
        {
            qCritical() << "No BEM block found!";
            return false;
        }

        QList<FiffDirTree>bemsurf = p_Tree.dir_tree_find(FIFFB_BEM_SURF);
        if(bemsurf.isEmpty())
        {
            qCritical() << "No BEM surfaces found!";
            return false;
        }

        for(int k = 0; k < bemsurf.size(); ++k)
        {
            MNEBemSurface  p_BemSurface;
            printf("\tReading a BEM surface...");
            MNEBem::readBemSurface(p_pStream.data(), bemsurf[k], p_BemSurface);
            p_BemSurface.addTriangleData();
             if (add_geom)
                p_BemSurface.addVertexNormals();
            printf("\t[done]\n" );

            p_Bem.m_qListBemSurface.append(p_BemSurface);

    //           src(k) = this;
        }
    }

    return true;
}

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

bool MNEBem::readBemSurface(FiffStream *p_pStream, const FiffDirTree &p_Tree, MNEBemSurface &p_BemSurface)
{
    p_BemSurface.clear();

    FiffTag::SPtr t_pTag;

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_ID, t_pTag))
         p_BemSurface.id = FIFFV_BEM_SURF_ID_UNKNOWN;
    else
         p_BemSurface.id = *t_pTag->toInt();

        qDebug() << "Read SourceSpace ID; type:" << t_pTag->getType() << "value:" << *t_pTag->toInt();

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SIGMA, t_pTag))
         p_BemSurface.sigma = 1.0;
    else
         p_BemSurface.sigma = *t_pTag->toFloat();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NNODE, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "np not found!";
        return false;
    }
    else
         p_BemSurface.np = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NTRI, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "ntri not found!";
        return false;
    }
    else
         p_BemSurface.ntri = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_MNE_COORD_FRAME, t_pTag))
    {
        qWarning() << "FIFF_MNE_COORD_FRAME not found, trying FIFF_BEM_COORD_FRAME.";
        if(!p_Tree.find_tag(p_pStream, FIFF_BEM_COORD_FRAME, t_pTag))
        {
            p_pStream->device()->close();
            std::cout << "Coordinate frame information not found."; //ToDo: throw error.
            return false;
        }
        else
            p_BemSurface.coord_frame = *t_pTag->toInt();
    }
    else
         p_BemSurface.coord_frame = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    //
    //   Vertices, normals, and triangles
    //
    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NODES, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "Vertex data not found."; //ToDo: throw error.
        return false;
    }

    p_BemSurface.rr = t_pTag->toFloatMatrix().transpose();
    qint32 rows_rr = p_BemSurface.rr.rows();
        qDebug() << "last element rr: " << p_BemSurface.rr(rows_rr-1, 0) << p_BemSurface.rr(rows_rr-1, 1) << p_BemSurface.rr(rows_rr-1, 2);

    if (rows_rr != p_BemSurface.np)
    {
        p_pStream->device()->close();
        std::cout << "Vertex information is incorrect."; //ToDo: throw error.
        return false;
    }
        qDebug() << "Surf Nodes; type:" << t_pTag->getType();

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NORMALS, t_pTag))
    {
        if(!p_Tree.find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_NORMALS, t_pTag))
        {
            p_pStream->device()->close();
            std::cout << "Vertex normals not found."; //ToDo: throw error.
            return false;
        }

        p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
    }
    else
    {
        p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
    }

    if (p_BemSurface.nn.rows() != p_BemSurface.np)
    {
        p_pStream->device()->close();
        std::cout << "Vertex normal information is incorrect."; //ToDo: throw error.
        return false;
    }

    qDebug() << "Source Space Normals; type:" << t_pTag->getType();

    //=====================================================================
    if (p_BemSurface.ntri > 0)
    {
        if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_TRIANGLES, t_pTag))
        {
            if(!p_Tree.find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_TRIANGLES, t_pTag))
            {
                p_pStream->device()->close();
                std::cout << "Triangulation not found."; //ToDo: throw error.
                return false;
            }
            else
            {
                p_BemSurface.tris = t_pTag->toIntMatrix().transpose();
                p_BemSurface.tris -= MatrixXi::Constant(p_BemSurface.tris.rows(),3,1);//0 based indizes
            }
        }
        else
        {
            p_BemSurface.tris = t_pTag->toIntMatrix().transpose();
            p_BemSurface.tris -= MatrixXi::Constant(p_BemSurface.tris.rows(),3,1);//0 based indizes
        }

        if (p_BemSurface.tris.rows() != p_BemSurface.ntri)
        {
            p_pStream->device()->close();
            std::cout << "Triangulation information is incorrect."; //ToDo: throw error.
            return false;
        }
    }
    else
    {
        MatrixXi p_defaultMatrix(0, 0);
        p_BemSurface.tris = p_defaultMatrix;
    }
        qDebug() << "Triangles; type:" << t_pTag->getType() << "rows:" << p_BemSurface.tris.rows() << "cols:" << p_BemSurface.tris.cols();
        qDebug() << "First Triangle: " << p_BemSurface.tris(0, 0) << p_BemSurface.tris(0, 1) << p_BemSurface.tris(0, 2);
        qDebug() << "Last Triangle: " << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 0) << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 1) << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 2);

    return true;
}

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

void MNEBem::write(QIODevice &p_IODevice)
{
    //
    //   Open the file, create directory
    //

    // Create the file and save the essentials
    FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
    printf("Write BEM surface in %s...", t_pStream->streamName().toUtf8().constData());
    this->writeToStream(t_pStream.data());
}

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

void MNEBem::writeToStream(FiffStream* p_pStream)
{
    for(qint32 h = 0; h < m_qListBemSurface.size(); ++h)
    {
        printf("\tWrite a bem surface... ");
        p_pStream->start_block(FIFFB_BEM_SURF);
        m_qListBemSurface[h].writeToStream(p_pStream);
        p_pStream->end_block(FIFFB_BEM_SURF);
        printf("[done]\n");
    }
    printf("\t%d bem surfaces written\n", m_qListBemSurface.size());
}

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

const MNEBemSurface& MNEBem::operator[] (qint32 idx) const
{
    if (idx>=m_qListBemSurface.length())
    {
        qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
        idx=0;
    }
    return m_qListBemSurface[idx];
}

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

MNEBemSurface& MNEBem::operator[] (qint32 idx)
{
    if (idx>=m_qListBemSurface.length())
    {
        qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
        idx=0;
    }
    return m_qListBemSurface[idx];

//    if(idx == 0)
//        return m_qListBemSurface[0];
//    else if(idx == 1)
//        return m_qListBemSurface[1];
//    else if(idx == 2)
//        return m_qListBemSurface[2];
//    else
//    {
//        qWarning("Warning: Index is not '0', '1' or '2'! Returning '0'.");
//        return m_qListBemSurface[0];
//    }
}
<commit_msg>[LIB-54] Debugging MNEBem<commit_after>//=============================================================================================================
/**
* @file     mne_bem.cpp
* @author   Jana Kiesel <jana.kiesel@tu-ilmenau.de>;
*           Christoph Dinh <chdinh@nmr.mgh.harvard.edu>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     June, 2015
*
* @section  LICENSE
*
* Copyright (C) 2015, Jana Kiesel, Christoph Dinh and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    MNEBem class implementation.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "mne_bem.h"

#include <utils/mnemath.h>
#include <fs/label.h>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace UTILSLIB;
using namespace FSLIB;
using namespace MNELIB;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

MNEBem::MNEBem()
{
}

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

MNEBem::MNEBem(const MNEBem &p_MNEBem)
: m_qListBemSurface(p_MNEBem.m_qListBemSurface)
{

}

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

MNEBem::MNEBem(QIODevice &p_IODevice)   //const MNESourceSpace &p_MNESourceSpace
//: m_qListBemSurface()
{
    FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
    FiffDirTree t_Tree;
//    QList<FiffDirEntry>fiffDirEntries;

//    if(!t_pStream->open(t_Tree, fiffDirEntries))
//    {
//        qCritical() << "Could not open FIFF stream!";
////        return false;
//    }

    if(!MNEBem::readFromStream(t_pStream, true, t_Tree, *this))
    {
        t_pStream->device()->close();
        std::cout << "Could not read the source spaces\n"; // ToDo throw error
        //ToDo error(me,'Could not read the source spaces (%s)',mne_omit_first_line(lasterr));
//        return false;
    }

    bool testStream =t_pStream->device()->isOpen();
}

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

MNEBem::~MNEBem()
{

}

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

bool MNEBem::readFromStream(FiffStream::SPtr& p_pStream, bool add_geom, FiffDirTree& p_Tree, MNEBem& p_Bem)
{
    //
    //   Open the file, create directory
    //
    bool open_here = false;
    QFile t_file;

    if (!p_pStream->device()->isOpen())
    {
        QList<FiffDirEntry> t_Dir;
        QString t_sFileName = p_pStream->streamName();

        t_file.setFileName(t_sFileName);//ToDo TCPSocket;
        p_pStream = FiffStream::SPtr(new FiffStream(&t_file));
        if(!p_pStream->open(p_Tree, t_Dir))
            return false;
        open_here = true;
    }

    //
    //   Find all BEM surfaces
    //

    QList<FiffDirTree>bem = p_Tree.dir_tree_find(FIFFB_BEM);
    if(bem.isEmpty())
    {
        qCritical() << "No BEM block found!";
        if(open_here)
            p_pStream->device()->close();
        return false;
    }

    QList<FiffDirTree>bemsurf = p_Tree.dir_tree_find(FIFFB_BEM_SURF);
    if(bemsurf.isEmpty())
    {
        qCritical() << "No BEM surfaces found!";
        if(open_here)
            p_pStream->device()->close();
        return false;
    }

    for(int k = 0; k < bemsurf.size(); ++k)
    {
        MNEBemSurface  p_BemSurface;
        printf("\tReading a BEM surface...");
        MNEBem::readBemSurface(p_pStream.data(), bemsurf[k], p_BemSurface);
        p_BemSurface.addTriangleData();
         if (add_geom)
            p_BemSurface.addVertexNormals();
        printf("\t[done]\n" );

        p_Bem.m_qListBemSurface.append(p_BemSurface);
    }

    printf("\t%d bem surfaces read\n", bemsurf.size());

    if(open_here)
        p_pStream->device()->close();

    return true;
}

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

bool MNEBem::readBemSurface(FiffStream *p_pStream, const FiffDirTree &p_Tree, MNEBemSurface &p_BemSurface)
{
    p_BemSurface.clear();

    FiffTag::SPtr t_pTag;

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_ID, t_pTag))
         p_BemSurface.id = FIFFV_BEM_SURF_ID_UNKNOWN;
    else
         p_BemSurface.id = *t_pTag->toInt();

        qDebug() << "Read SourceSpace ID; type:" << t_pTag->getType() << "value:" << *t_pTag->toInt();

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SIGMA, t_pTag))
         p_BemSurface.sigma = 1.0;
    else
         p_BemSurface.sigma = *t_pTag->toFloat();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NNODE, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "np not found!";
        return false;
    }
    else
         p_BemSurface.np = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NTRI, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "ntri not found!";
        return false;
    }
    else
         p_BemSurface.ntri = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_MNE_COORD_FRAME, t_pTag))
    {
        qWarning() << "FIFF_MNE_COORD_FRAME not found, trying FIFF_BEM_COORD_FRAME.";
        if(!p_Tree.find_tag(p_pStream, FIFF_BEM_COORD_FRAME, t_pTag))
        {
            p_pStream->device()->close();
            std::cout << "Coordinate frame information not found."; //ToDo: throw error.
            return false;
        }
        else
            p_BemSurface.coord_frame = *t_pTag->toInt();
    }
    else
         p_BemSurface.coord_frame = *t_pTag->toInt();

//        qDebug() <<

    //=====================================================================
    //
    //   Vertices, normals, and triangles
    //
    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NODES, t_pTag))
    {
        p_pStream->device()->close();
        std::cout << "Vertex data not found."; //ToDo: throw error.
        return false;
    }

    p_BemSurface.rr = t_pTag->toFloatMatrix().transpose();
    qint32 rows_rr = p_BemSurface.rr.rows();
        qDebug() << "last element rr: " << p_BemSurface.rr(rows_rr-1, 0) << p_BemSurface.rr(rows_rr-1, 1) << p_BemSurface.rr(rows_rr-1, 2);

    if (rows_rr != p_BemSurface.np)
    {
        p_pStream->device()->close();
        std::cout << "Vertex information is incorrect."; //ToDo: throw error.
        return false;
    }
        qDebug() << "Surf Nodes; type:" << t_pTag->getType();

    //=====================================================================
    if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_NORMALS, t_pTag))
    {
        if(!p_Tree.find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_NORMALS, t_pTag))
        {
            p_pStream->device()->close();
            std::cout << "Vertex normals not found."; //ToDo: throw error.
            return false;
        }

        p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
    }
    else
    {
        p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
    }

    if (p_BemSurface.nn.rows() != p_BemSurface.np)
    {
        p_pStream->device()->close();
        std::cout << "Vertex normal information is incorrect."; //ToDo: throw error.
        return false;
    }

    qDebug() << "Source Space Normals; type:" << t_pTag->getType();

    //=====================================================================
    if (p_BemSurface.ntri > 0)
    {
        if(!p_Tree.find_tag(p_pStream, FIFF_BEM_SURF_TRIANGLES, t_pTag))
        {
            if(!p_Tree.find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_TRIANGLES, t_pTag))
            {
                p_pStream->device()->close();
                std::cout << "Triangulation not found."; //ToDo: throw error.
                return false;
            }
            else
            {
                p_BemSurface.tris = t_pTag->toIntMatrix().transpose();
                p_BemSurface.tris -= MatrixXi::Constant(p_BemSurface.tris.rows(),3,1);//0 based indizes
            }
        }
        else
        {
            p_BemSurface.tris = t_pTag->toIntMatrix().transpose();
            p_BemSurface.tris -= MatrixXi::Constant(p_BemSurface.tris.rows(),3,1);//0 based indizes
        }

        if (p_BemSurface.tris.rows() != p_BemSurface.ntri)
        {
            p_pStream->device()->close();
            std::cout << "Triangulation information is incorrect."; //ToDo: throw error.
            return false;
        }
    }
    else
    {
        MatrixXi p_defaultMatrix(0, 0);
        p_BemSurface.tris = p_defaultMatrix;
    }
        qDebug() << "Triangles; type:" << t_pTag->getType() << "rows:" << p_BemSurface.tris.rows() << "cols:" << p_BemSurface.tris.cols();
        qDebug() << "First Triangle: " << p_BemSurface.tris(0, 0) << p_BemSurface.tris(0, 1) << p_BemSurface.tris(0, 2);
        qDebug() << "Last Triangle: " << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 0) << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 1) << p_BemSurface.tris(p_BemSurface.tris.rows()-1, 2);

    return true;
}

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

void MNEBem::write(QIODevice &p_IODevice)
{
    //
    //   Open the file, create directory
    //

    // Create the file and save the essentials
    FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
    printf("Write BEM surface in %s...", t_pStream->streamName().toUtf8().constData());
    this->writeToStream(t_pStream.data());
    bool test= t_pStream->device()->isOpen();
}

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

void MNEBem::writeToStream(FiffStream* p_pStream)
{
    for(qint32 h = 0; h < m_qListBemSurface.size(); ++h)
    {
        printf("\tWrite a bem surface... ");
        p_pStream->start_block(FIFFB_BEM_SURF);
        m_qListBemSurface[h].writeToStream(p_pStream);
        p_pStream->end_block(FIFFB_BEM_SURF);
        printf("[done]\n");
    }
    printf("\t%d bem surfaces written\n", m_qListBemSurface.size());
}

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

const MNEBemSurface& MNEBem::operator[] (qint32 idx) const
{
    if (idx>=m_qListBemSurface.length())
    {
        qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
        idx=0;
    }
    return m_qListBemSurface[idx];
}

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

MNEBemSurface& MNEBem::operator[] (qint32 idx)
{
    if (idx>=m_qListBemSurface.length())
    {
        qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
        idx=0;
    }
    return m_qListBemSurface[idx];

//    if(idx == 0)
//        return m_qListBemSurface[0];
//    else if(idx == 1)
//        return m_qListBemSurface[1];
//    else if(idx == 2)
//        return m_qListBemSurface[2];
//    else
//    {
//        qWarning("Warning: Index is not '0', '1' or '2'! Returning '0'.");
//        return m_qListBemSurface[0];
//    }
}
<|endoftext|>


<commit_before>#include "interpreter.h"

#include "details/autoconfigurer.h"
#include "details/robotImplementations/unrealRobotModelImplementation.h"

#include <QtCore/QDebug>

using namespace qReal;
using namespace interpreters::robots;
using namespace interpreters::robots::details;

const Id startingElementType = Id("RobotsMetamodel", "RobotsDiagram", "InitialNode");
const Id startingElementType1 = Id("RobotsMetamodel", "RobotsDiagram", "InitialBlock");

Interpreter::Interpreter()
	: mGraphicalModelApi(NULL)
	, mLogicalModelApi(NULL)
	, mInterpretersInterface(NULL)
	, mState(idle)
	, mRobotModel(new RobotModel())
	, mBlocksTable(NULL)
	, mBluetoothRobotCommunication(NULL)
{
	mParser = NULL;
	mBlocksTable = NULL;
	mTimer = new QTimer();

	mD2RobotModel = new d2Model::D2RobotModel();
	mD2ModelWidget = mD2RobotModel->createModelWidget();
}

void Interpreter::init(GraphicalModelAssistInterface const &graphicalModelApi
	, LogicalModelAssistInterface const &logicalModelApi
	, qReal::gui::MainWindowInterpretersInterface &interpretersInterface)
{
	mGraphicalModelApi = &graphicalModelApi;
	mLogicalModelApi = &logicalModelApi;
	mInterpretersInterface = &interpretersInterface;

	mParser = new RobotsBlockParser(mInterpretersInterface->errorReporter());
	mBlocksTable = new BlocksTable(graphicalModelApi, logicalModelApi, mRobotModel, mInterpretersInterface->errorReporter(), mParser);

	QString const comPort = SettingsManager::instance()->value("bluetoothPortName", "").toString();
	mBluetoothRobotCommunication = new BluetoothRobotCommunication(comPort);
	robotModelType::robotModelTypeEnum const modelType = static_cast<robotModelType::robotModelTypeEnum>(SettingsManager::value("robotModel", "1").toInt());
	setRobotImplementation(modelType, mBluetoothRobotCommunication);
}

Interpreter::~Interpreter()
{
	foreach (Thread * const thread, mThreads)
		delete thread;
	delete mBlocksTable;
}

void Interpreter::interpret()
{
	mInterpretersInterface->errorReporter()->clear();

	Id const &currentDiagramId = mInterpretersInterface->activeDiagram();

	if (!mConnected) {
		mInterpretersInterface->errorReporter()->addInformation(tr("No connection to robot"));
		return;
	}
	if (mState == interpreting) {
		mInterpretersInterface->errorReporter()->addInformation(tr("Interpreter is already running"));
		return;
	}

	mState = interpreting;

	mBlocksTable->setIdleForBlocks();

	Id const startingElement = findStartingElement(currentDiagramId);
	if (startingElement == Id()) {
		mInterpretersInterface->errorReporter()->addError(tr("No entry point found, please add Initial Node to a diagram"));
		return;
	}

	Autoconfigurer configurer(*mGraphicalModelApi, mBlocksTable, mInterpretersInterface->errorReporter(), mRobotModel);
	if (!configurer.configure(currentDiagramId))
		return;

	mRobotModel->robotImpl().startInterpretation();

	Thread * const initialThread = new Thread(*mInterpretersInterface, *mBlocksTable, startingElement);
	addThread(initialThread);
}

void Interpreter::stop()
{
	mRobotModel->stopRobot();
	mState = idle;
	foreach (Thread *thread, mThreads)
		delete thread;
	mBlocksTable->setFailure();
	/*mBlocksTable->clear();
	mThreads.clear();
	mTimer->stop();*/
}

void Interpreter::stopRobot()
{
	stop();
}

void Interpreter::showD2ModelWidget(bool isVisible)
{
	mD2ModelWidget->init(isVisible);
}

void Interpreter::setD2ModelWidgetActions(QAction *runAction, QAction *stopAction)
{
	mD2ModelWidget->setD2ModelWidgetActions(runAction, stopAction);
}

void Interpreter::setRobotImplementation(robotModelType::robotModelTypeEnum implementationType, RobotCommunicationInterface * const robotCommunicationInterface)
{
	disconnect(&mRobotModel->robotImpl(), SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	mConnected = false;
	robotImplementations::AbstractRobotModelImplementation *robotImpl =
			robotImplementations::AbstractRobotModelImplementation::robotModel(implementationType, robotCommunicationInterface, mD2RobotModel);
	setRobotImplementation(robotImpl);
	connect(robotImpl, SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	if (implementationType != robotModelType::real)
		mRobotModel->init();
}

void Interpreter::connectedSlot(bool success)
{
	if (success) {
		mConnected = true;
		if (mRobotModel->robotImpl().needsConnection())
			mInterpretersInterface->errorReporter()->addInformation(tr("Connected successfully"));
	} else {
		mConnected = false;
		mInterpretersInterface->errorReporter()->addError(tr("Can't connect to a robot."));
	}
}

Id const Interpreter::findStartingElement(Id const &diagram) const
{
	IdList const children = mGraphicalModelApi->graphicalRepoApi().children(diagram);

	foreach (Id const child, children) {
		if (child.type() == startingElementType || child.type() == startingElementType1)
			return child;
	}

	return Id();
}

void Interpreter::threadStopped()
{
	Thread *thread = static_cast<Thread *>(sender());

	mThreads.removeAll(thread);
	delete thread;

	if (mThreads.isEmpty())
		stop();
}

void Interpreter::newThread(details::blocks::Block * const startBlock)
{
	Thread * const thread = new Thread(*mInterpretersInterface, *mBlocksTable, startBlock->id());
	addThread(thread);
}

void Interpreter::configureSensors(sensorType::SensorTypeEnum const &port1
		, sensorType::SensorTypeEnum const &port2
		, sensorType::SensorTypeEnum const &port3
		, sensorType::SensorTypeEnum const &port4)
{
	mRobotModel->configureSensors(port1, port2, port3, port4);
}

void Interpreter::addThread(details::Thread * const thread)
{
	mThreads.append(thread);
	connect(thread, SIGNAL(stopped()), this, SLOT(threadStopped()));
	connect(thread, SIGNAL(newThread(details::blocks::Block*const)), this, SLOT(newThread(details::blocks::Block*const)));

	thread->interpret();
}

interpreters::robots::details::RobotModel *Interpreter::robotModel()
{
	return mRobotModel;
}

void Interpreter::setRobotModel(details::RobotModel * const robotModel)
{
	mRobotModel = robotModel;
}

void Interpreter::setRobotImplementation(details::robotImplementations::AbstractRobotModelImplementation *robotImpl)
{
	mRobotModel->setRobotImplementation(robotImpl);
	if (robotImpl)
		connect(&mRobotModel->robotImpl(), SIGNAL(connected(bool)), this, SLOT(runTimer()));
}

void Interpreter::runTimer()
{
	mTimer->start(1000);
	connect(mTimer, SIGNAL(timeout()), this, SLOT(readSensorValues()));
	if (mRobotModel->sensor(inputPort::port1)) {
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot1(int)));
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot2(int)));
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot3(int)));
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot4(int)));
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
}

void Interpreter::readSensorValues()
{
	if (mState == idle)
		return;

	if (mRobotModel->sensor(inputPort::port1))
		mRobotModel->sensor(inputPort::port1)->read();
	if (mRobotModel->sensor(inputPort::port2))
		mRobotModel->sensor(inputPort::port2)->read();
	if (mRobotModel->sensor(inputPort::port3))
		mRobotModel->sensor(inputPort::port3)->read();
	if (mRobotModel->sensor(inputPort::port4))
		mRobotModel->sensor(inputPort::port4)->read();
}

void Interpreter::slotFailure()
{
	qDebug() << "slotFailure";
}

void Interpreter::responseSlot1(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor1"), sensorValue);
}

void Interpreter::responseSlot2(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor2"), sensorValue);
}

void Interpreter::responseSlot3(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor3"), sensorValue);
}

void Interpreter::responseSlot4(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor4"), sensorValue);
}

void Interpreter::updateSensorValues(const QString &sensorVariableName, int sensorValue)
{
	(*(mParser->getVariables()))[sensorVariableName] = Number(sensorValue, Number::intType);
//	qDebug() << sensorVariableName << sensorValue;
}

void Interpreter::connectToRobot()
{
	mRobotModel->init();
}

void Interpreter::setBluetoothPortName(QString const &portName)
{
	mBluetoothRobotCommunication->setPortName(portName);
}

void Interpreter::setRobotModelType(robotModelType::robotModelTypeEnum robotModelType)
{
	setRobotImplementation(robotModelType, mBluetoothRobotCommunication);
}
<commit_msg>fixed segfault on repeated run-stop 2d-model sequence. closed #195<commit_after>#include "interpreter.h"

#include "details/autoconfigurer.h"
#include "details/robotImplementations/unrealRobotModelImplementation.h"

#include <QtCore/QDebug>

using namespace qReal;
using namespace interpreters::robots;
using namespace interpreters::robots::details;

const Id startingElementType = Id("RobotsMetamodel", "RobotsDiagram", "InitialNode");
const Id startingElementType1 = Id("RobotsMetamodel", "RobotsDiagram", "InitialBlock");

Interpreter::Interpreter()
	: mGraphicalModelApi(NULL)
	, mLogicalModelApi(NULL)
	, mInterpretersInterface(NULL)
	, mState(idle)
	, mRobotModel(new RobotModel())
	, mBlocksTable(NULL)
	, mBluetoothRobotCommunication(NULL)
{
	mParser = NULL;
	mBlocksTable = NULL;
	mTimer = new QTimer();

	mD2RobotModel = new d2Model::D2RobotModel();
	mD2ModelWidget = mD2RobotModel->createModelWidget();
}

void Interpreter::init(GraphicalModelAssistInterface const &graphicalModelApi
	, LogicalModelAssistInterface const &logicalModelApi
	, qReal::gui::MainWindowInterpretersInterface &interpretersInterface)
{
	mGraphicalModelApi = &graphicalModelApi;
	mLogicalModelApi = &logicalModelApi;
	mInterpretersInterface = &interpretersInterface;

	mParser = new RobotsBlockParser(mInterpretersInterface->errorReporter());
	mBlocksTable = new BlocksTable(graphicalModelApi, logicalModelApi, mRobotModel, mInterpretersInterface->errorReporter(), mParser);

	QString const comPort = SettingsManager::instance()->value("bluetoothPortName", "").toString();
	mBluetoothRobotCommunication = new BluetoothRobotCommunication(comPort);
	robotModelType::robotModelTypeEnum const modelType = static_cast<robotModelType::robotModelTypeEnum>(SettingsManager::value("robotModel", "1").toInt());
	setRobotImplementation(modelType, mBluetoothRobotCommunication);
}

Interpreter::~Interpreter()
{
	foreach (Thread * const thread, mThreads)
		delete thread;
	delete mBlocksTable;
}

void Interpreter::interpret()
{
	mInterpretersInterface->errorReporter()->clear();

	Id const &currentDiagramId = mInterpretersInterface->activeDiagram();

	if (!mConnected) {
		mInterpretersInterface->errorReporter()->addInformation(tr("No connection to robot"));
		return;
	}
	if (mState == interpreting) {
		mInterpretersInterface->errorReporter()->addInformation(tr("Interpreter is already running"));
		return;
	}

	mState = interpreting;

	mBlocksTable->setIdleForBlocks();

	Id const startingElement = findStartingElement(currentDiagramId);
	if (startingElement == Id()) {
		mInterpretersInterface->errorReporter()->addError(tr("No entry point found, please add Initial Node to a diagram"));
		return;
	}

	Autoconfigurer configurer(*mGraphicalModelApi, mBlocksTable, mInterpretersInterface->errorReporter(), mRobotModel);
	if (!configurer.configure(currentDiagramId))
		return;

	mRobotModel->robotImpl().startInterpretation();

	Thread * const initialThread = new Thread(*mInterpretersInterface, *mBlocksTable, startingElement);
	addThread(initialThread);
}

void Interpreter::stop()
{
	mRobotModel->stopRobot();
	mState = idle;
	foreach (Thread *thread, mThreads) {
		delete thread;
		mThreads.removeAll(thread);
	}
	mBlocksTable->setFailure();
	/*mBlocksTable->clear();
	mThreads.clear();
	mTimer->stop();*/
}

void Interpreter::stopRobot()
{
	stop();
}

void Interpreter::showD2ModelWidget(bool isVisible)
{
	mD2ModelWidget->init(isVisible);
}

void Interpreter::setD2ModelWidgetActions(QAction *runAction, QAction *stopAction)
{
	mD2ModelWidget->setD2ModelWidgetActions(runAction, stopAction);
}

void Interpreter::setRobotImplementation(robotModelType::robotModelTypeEnum implementationType, RobotCommunicationInterface * const robotCommunicationInterface)
{
	disconnect(&mRobotModel->robotImpl(), SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	mConnected = false;
	robotImplementations::AbstractRobotModelImplementation *robotImpl =
			robotImplementations::AbstractRobotModelImplementation::robotModel(implementationType, robotCommunicationInterface, mD2RobotModel);
	setRobotImplementation(robotImpl);
	connect(robotImpl, SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	if (implementationType != robotModelType::real)
		mRobotModel->init();
}

void Interpreter::connectedSlot(bool success)
{
	if (success) {
		mConnected = true;
		if (mRobotModel->robotImpl().needsConnection())
			mInterpretersInterface->errorReporter()->addInformation(tr("Connected successfully"));
	} else {
		mConnected = false;
		mInterpretersInterface->errorReporter()->addError(tr("Can't connect to a robot."));
	}
}

Id const Interpreter::findStartingElement(Id const &diagram) const
{
	IdList const children = mGraphicalModelApi->graphicalRepoApi().children(diagram);

	foreach (Id const child, children) {
		if (child.type() == startingElementType || child.type() == startingElementType1)
			return child;
	}

	return Id();
}

void Interpreter::threadStopped()
{
	Thread *thread = static_cast<Thread *>(sender());

	mThreads.removeAll(thread);
	delete thread;

	if (mThreads.isEmpty())
		stop();
}

void Interpreter::newThread(details::blocks::Block * const startBlock)
{
	Thread * const thread = new Thread(*mInterpretersInterface, *mBlocksTable, startBlock->id());
	addThread(thread);
}

void Interpreter::configureSensors(sensorType::SensorTypeEnum const &port1
		, sensorType::SensorTypeEnum const &port2
		, sensorType::SensorTypeEnum const &port3
		, sensorType::SensorTypeEnum const &port4)
{
	mRobotModel->configureSensors(port1, port2, port3, port4);
}

void Interpreter::addThread(details::Thread * const thread)
{
	mThreads.append(thread);
	connect(thread, SIGNAL(stopped()), this, SLOT(threadStopped()));
	connect(thread, SIGNAL(newThread(details::blocks::Block*const)), this, SLOT(newThread(details::blocks::Block*const)));

	thread->interpret();
}

interpreters::robots::details::RobotModel *Interpreter::robotModel()
{
	return mRobotModel;
}

void Interpreter::setRobotModel(details::RobotModel * const robotModel)
{
	mRobotModel = robotModel;
}

void Interpreter::setRobotImplementation(details::robotImplementations::AbstractRobotModelImplementation *robotImpl)
{
	mRobotModel->setRobotImplementation(robotImpl);
	if (robotImpl)
		connect(&mRobotModel->robotImpl(), SIGNAL(connected(bool)), this, SLOT(runTimer()));
}

void Interpreter::runTimer()
{
	mTimer->start(1000);
	connect(mTimer, SIGNAL(timeout()), this, SLOT(readSensorValues()));
	if (mRobotModel->sensor(inputPort::port1)) {
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot1(int)));
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot2(int)));
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot3(int)));
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot4(int)));
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
}

void Interpreter::readSensorValues()
{
	if (mState == idle)
		return;

	if (mRobotModel->sensor(inputPort::port1))
		mRobotModel->sensor(inputPort::port1)->read();
	if (mRobotModel->sensor(inputPort::port2))
		mRobotModel->sensor(inputPort::port2)->read();
	if (mRobotModel->sensor(inputPort::port3))
		mRobotModel->sensor(inputPort::port3)->read();
	if (mRobotModel->sensor(inputPort::port4))
		mRobotModel->sensor(inputPort::port4)->read();
}

void Interpreter::slotFailure()
{
	qDebug() << "slotFailure";
}

void Interpreter::responseSlot1(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor1"), sensorValue);
}

void Interpreter::responseSlot2(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor2"), sensorValue);
}

void Interpreter::responseSlot3(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor3"), sensorValue);
}

void Interpreter::responseSlot4(int sensorValue)
{
	updateSensorValues(QObject::tr("Sensor4"), sensorValue);
}

void Interpreter::updateSensorValues(const QString &sensorVariableName, int sensorValue)
{
	(*(mParser->getVariables()))[sensorVariableName] = Number(sensorValue, Number::intType);
//	qDebug() << sensorVariableName << sensorValue;
}

void Interpreter::connectToRobot()
{
	mRobotModel->init();
}

void Interpreter::setBluetoothPortName(QString const &portName)
{
	mBluetoothRobotCommunication->setPortName(portName);
}

void Interpreter::setRobotModelType(robotModelType::robotModelTypeEnum robotModelType)
{
	setRobotImplementation(robotModelType, mBluetoothRobotCommunication);
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/coro/lazy.h>
#include <vespa/vespalib/coro/sync_wait.h>
#include <vespa/vespalib/util/require.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <mutex>

#include <thread>

using vespalib::coro::Lazy;
using vespalib::coro::sync_wait;

std::mutex thread_lock;
std::vector<std::jthread> threads;
struct JoinThreads {
    ~JoinThreads() {
        threads.clear();
    }
};

auto run_in_other_thread() {
    struct awaiter {
        bool await_ready() const noexcept { return false; }
        void await_suspend(std::coroutine_handle<> handle) const {
            auto guard = std::lock_guard(thread_lock);
            threads.push_back(std::jthread(handle));
        }
        void await_resume() const noexcept {}
    };
    return awaiter();
}

Lazy<int> make_lazy(int value) {
    co_return value;
}

Lazy<int> async_add_values(int a, int b) {
    auto lazy_a = make_lazy(a);
    auto lazy_b = make_lazy(b);
    co_return (co_await lazy_a + co_await lazy_b);
}

Lazy<int> async_sum(Lazy<int> a, Lazy<int> b) {
    co_return (co_await a + co_await b);
}

Lazy<std::unique_ptr<int>> move_only_int() {
    co_return std::make_unique<int>(123);
}

Lazy<int> extract_rvalue() {
    auto res = co_await move_only_int();
    co_return *res;
}

Lazy<int> will_throw() {
    REQUIRE_FAILED("failed on purpose");
    co_return 123;
}

template<typename T>
Lazy<T> forward_value(Lazy<T> value) {
    co_return co_await std::move(value);
}

template <typename T>
Lazy<T> switch_thread(Lazy<T> value) {
    std::cerr << "switching from thread " << std::this_thread::get_id() << std::endl;
    co_await run_in_other_thread();
    std::cerr << "........... to thread " << std::this_thread::get_id() << std::endl;
    co_return co_await value;
}

TEST(LazyTest, simple_lazy_value) {
    auto lazy = make_lazy(42);
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 42);
}

TEST(LazyTest, async_sum_of_async_values) {
    auto lazy = async_add_values(10, 20);
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 30);
}

TEST(LazyTest, async_sum_of_external_async_values) {
    auto a = make_lazy(100);
    auto b = make_lazy(200);
    auto lazy = async_sum(std::move(a), std::move(b));
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 300);
}

TEST(LazyTest, extract_rvalue_from_lazy_in_coroutine) {
    auto lazy = extract_rvalue();
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 123);
}

TEST(LazyTest, extract_rvalue_from_lazy_in_sync_wait) {
    auto result = sync_wait(move_only_int());
    EXPECT_EQ(*result, 123);
}

TEST(LazyTest, calculate_result_in_another_thread) {
    JoinThreads thread_guard;
    auto result = sync_wait(switch_thread(make_lazy(7)));
    EXPECT_EQ(result, 7);
}

TEST(LazyTest, exceptions_are_propagated) {
    JoinThreads thread_guard;
    auto lazy = switch_thread(forward_value(will_throw()));
    EXPECT_THROW(sync_wait(lazy), vespalib::RequireFailedException);
}

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>Add fallback variant when std::jthread is not available. (#24440)<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/coro/lazy.h>
#include <vespa/vespalib/coro/sync_wait.h>
#include <vespa/vespalib/util/require.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <mutex>

#include <thread>

using vespalib::coro::Lazy;
using vespalib::coro::sync_wait;

std::mutex thread_lock;
std::vector<std::thread> threads;
struct JoinThreads {
    ~JoinThreads() {
        for (auto &thread: threads) {
            thread.join();
        }
        threads.clear();
    }
};

auto run_in_other_thread() {
    struct awaiter {
        bool await_ready() const noexcept { return false; }
        void await_suspend(std::coroutine_handle<> handle) const {
            auto guard = std::lock_guard(thread_lock);
            threads.push_back(std::thread(handle));
        }
        void await_resume() const noexcept {}
    };
    return awaiter();
}

Lazy<int> make_lazy(int value) {
    co_return value;
}

Lazy<int> async_add_values(int a, int b) {
    auto lazy_a = make_lazy(a);
    auto lazy_b = make_lazy(b);
    co_return (co_await lazy_a + co_await lazy_b);
}

Lazy<int> async_sum(Lazy<int> a, Lazy<int> b) {
    co_return (co_await a + co_await b);
}

Lazy<std::unique_ptr<int>> move_only_int() {
    co_return std::make_unique<int>(123);
}

Lazy<int> extract_rvalue() {
    auto res = co_await move_only_int();
    co_return *res;
}

Lazy<int> will_throw() {
    REQUIRE_FAILED("failed on purpose");
    co_return 123;
}

template<typename T>
Lazy<T> forward_value(Lazy<T> value) {
    co_return co_await std::move(value);
}

template <typename T>
Lazy<T> switch_thread(Lazy<T> value) {
    std::cerr << "switching from thread " << std::this_thread::get_id() << std::endl;
    co_await run_in_other_thread();
    std::cerr << "........... to thread " << std::this_thread::get_id() << std::endl;
    co_return co_await value;
}

TEST(LazyTest, simple_lazy_value) {
    auto lazy = make_lazy(42);
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 42);
}

TEST(LazyTest, async_sum_of_async_values) {
    auto lazy = async_add_values(10, 20);
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 30);
}

TEST(LazyTest, async_sum_of_external_async_values) {
    auto a = make_lazy(100);
    auto b = make_lazy(200);
    auto lazy = async_sum(std::move(a), std::move(b));
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 300);
}

TEST(LazyTest, extract_rvalue_from_lazy_in_coroutine) {
    auto lazy = extract_rvalue();
    auto result = sync_wait(lazy);
    EXPECT_EQ(result, 123);
}

TEST(LazyTest, extract_rvalue_from_lazy_in_sync_wait) {
    auto result = sync_wait(move_only_int());
    EXPECT_EQ(*result, 123);
}

TEST(LazyTest, calculate_result_in_another_thread) {
    JoinThreads thread_guard;
    auto result = sync_wait(switch_thread(make_lazy(7)));
    EXPECT_EQ(result, 7);
}

TEST(LazyTest, exceptions_are_propagated) {
    JoinThreads thread_guard;
    auto lazy = switch_thread(forward_value(will_throw()));
    EXPECT_THROW(sync_wait(lazy), vespalib::RequireFailedException);
}

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include "array.h"
#include <cstdlib>
#include <cstring>
#include <tr1/type_traits>

namespace vespalib {

template <typename T>
void construct(T * dest, const T * source, size_t sz, std::tr1::false_type)
{
    for (size_t i(0); i < sz; i++) {
        ::new (static_cast<void *>(dest + i)) T(*(source + i));
    }
}

template <typename T>
void construct(T * dest, const T * source, size_t sz, std::tr1::true_type)
{
    memcpy(dest, source, sz*sizeof(T));
}

template <typename T>
void construct(T * dest, size_t sz, std::tr1::false_type)
{
    for (size_t i(0); i < sz; i++) {
        void *ptr = &dest[i];
        new(ptr) T();
    }
}

template <typename T>
void construct(T * dest, size_t sz, std::tr1::true_type)
{
    (void) dest;
    (void) sz;
}

template <typename T>
void construct(T * dest, size_t sz, T val, std::tr1::false_type)
{
    for (size_t i(0); i < sz; i++) {
        void *ptr = &dest[i];
        new(ptr) T(val);
    }
}

template <typename T>
void construct(T * dest, size_t sz, T val, std::tr1::true_type)
{
    for (size_t i(0); i < sz; i++) {
        dest[i] = val;
    }
}

template <typename T>
Array<T>::Array(const Array & rhs)
    : _array(rhs._array.create(rhs.size() * sizeof(T))),
      _sz(rhs.size())
{
    construct(array(0), rhs.array(0), _sz, std::tr1::has_trivial_destructor<T>());
}

template <typename T>
Array<T> & Array<T>::operator =(const Array & rhs)
{
    if (&rhs != this) {
        Array t(rhs);
        swap(t);
    }
    return *this;
}

template <typename T>
Array<T> & Array<T>::operator =(Array && rhs) {
    if (&rhs != this) {
        Array t(std::move(rhs));
        swap(t);
    }
    return *this;
}

template <typename T>
void Array<T>::assign(const_iterator begin_, const_iterator end_) {
    Array tmp(begin_, end_);
    swap(tmp);
}
template <typename T>
void Array<T>::reserve(size_t n) {
    if (capacity() < n) {
        increase(n);
    }
}

template <typename T>
bool Array<T>::try_unreserve(size_t n)
{
    if (n >= capacity()) {
        return false;
    }
    if (n < size()) {
        return false;
    }
    return _array.resize_inplace(n * sizeof(T));
}

template <typename T>
void Array<T>::resize(size_t n)
{
    if (n > capacity()) {
        reserve(n);
    }
    if (n > _sz) {
        construct(array(_sz), n-_sz, std::tr1::has_trivial_destructor<T>());
    } else if (n < _sz) {
        std::destroy(array(n), array(_sz));
    }
    _sz = n;
}

template <typename T>
void move(T * dest, T * source, size_t sz, std::tr1::false_type)
{
    for (size_t i(0); i < sz; i++) {
        ::new (static_cast<void *>(dest + i)) T(std::move(*(source + i)));
        std::destroy_at(source + i);
    }
}

template <typename T>
void move(T * dest, const T * source, size_t sz, std::tr1::true_type)
{
    memcpy(dest, source, sz*sizeof(T));
}

template <typename T>
void Array<T>::increase(size_t n)
{
    Alloc newArray(_array.create(sizeof(T)*n));
    if (capacity() > 0) {
        move(static_cast<T *>(newArray.get()), array(0), _sz, std::tr1::has_trivial_destructor<T>());
    }
    _array.swap(newArray);
}

template <typename T>
Array<T>::Array(const Alloc & initial)
    : _array(initial.create(0)),
      _sz(0)
{ }

template <typename T>
Array<T>::Array(Alloc && buf, size_t sz) :
    _array(std::move(buf)),
    _sz(sz)
{
}

template <typename T>
Array<T>::Array(Array &&rhs)
    :  _array(std::move(rhs._array)),
       _sz(rhs._sz)
{
    rhs._sz = 0;
}

template <typename T>
Array<T>::Array(size_t sz, const Alloc & initial) :
    _array(initial.create(sz * sizeof(T))),
    _sz(sz)
{
    construct(array(0), _sz, std::tr1::has_trivial_destructor<T>());
}

template <typename T>
Array<T>::Array(size_t sz, T value, const Alloc & initial) :
    _array(initial.create(sz * sizeof(T))),
    _sz(sz)
{
    construct(array(0), _sz, value, std::tr1::has_trivial_destructor<T>());
}

template <typename T>
Array<T>::Array(const_iterator begin_, const_iterator end_, const Alloc & initial) :
    _array(initial.create(begin_ != end_ ? sizeof(T) * (end_-begin_) : 0)),
    _sz(end_-begin_)
{
    construct(array(0), begin_, _sz, std::tr1::has_trivial_destructor<T>());
}

template <typename T>
Array<T>::~Array()
{
    cleanup();
}

template <typename T>
void Array<T>::cleanup()
{
    std::destroy(array(0), array(_sz));
    _sz = 0;
    Alloc().swap(_array);
}

}

<commit_msg>Use normal type traits.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include "array.h"
#include <cstdlib>
#include <cstring>
#include <type_traits>

namespace vespalib {

template <typename T>
void construct(T * dest, const T * source, size_t sz, std::false_type)
{
    for (size_t i(0); i < sz; i++) {
        ::new (static_cast<void *>(dest + i)) T(*(source + i));
    }
}

template <typename T>
void construct(T * dest, const T * source, size_t sz, std::true_type)
{
    memcpy(dest, source, sz*sizeof(T));
}

template <typename T>
void construct(T * dest, size_t sz, std::false_type)
{
    for (size_t i(0); i < sz; i++) {
        void *ptr = &dest[i];
        new(ptr) T();
    }
}

template <typename T>
void construct(T * dest, size_t sz, std::true_type)
{
    (void) dest;
    (void) sz;
}

template <typename T>
void construct(T * dest, size_t sz, T val, std::false_type)
{
    for (size_t i(0); i < sz; i++) {
        void *ptr = &dest[i];
        new(ptr) T(val);
    }
}

template <typename T>
void construct(T * dest, size_t sz, T val, std::true_type)
{
    for (size_t i(0); i < sz; i++) {
        dest[i] = val;
    }
}

template <typename T>
Array<T>::Array(const Array & rhs)
    : _array(rhs._array.create(rhs.size() * sizeof(T))),
      _sz(rhs.size())
{
    construct(array(0), rhs.array(0), _sz, std::is_trivially_copyable<T>());
}

template <typename T>
Array<T> & Array<T>::operator =(const Array & rhs)
{
    if (&rhs != this) {
        Array t(rhs);
        swap(t);
    }
    return *this;
}

template <typename T>
Array<T> & Array<T>::operator =(Array && rhs) {
    if (&rhs != this) {
        Array t(std::move(rhs));
        swap(t);
    }
    return *this;
}

template <typename T>
void Array<T>::assign(const_iterator begin_, const_iterator end_) {
    Array tmp(begin_, end_);
    swap(tmp);
}
template <typename T>
void Array<T>::reserve(size_t n) {
    if (capacity() < n) {
        increase(n);
    }
}

template <typename T>
bool Array<T>::try_unreserve(size_t n)
{
    if (n >= capacity()) {
        return false;
    }
    if (n < size()) {
        return false;
    }
    return _array.resize_inplace(n * sizeof(T));
}

template <typename T>
void Array<T>::resize(size_t n)
{
    if (n > capacity()) {
        reserve(n);
    }
    if (n > _sz) {
        construct(array(_sz), n-_sz, std::is_trivially_default_constructible<T>());
    } else if (n < _sz) {
        std::destroy(array(n), array(_sz));
    }
    _sz = n;
}

template <typename T>
void move(T * dest, T * source, size_t sz, std::false_type)
{
    for (size_t i(0); i < sz; i++) {
        ::new (static_cast<void *>(dest + i)) T(std::move(*(source + i)));
        std::destroy_at(source + i);
    }
}

template <typename T>
void move(T * dest, const T * source, size_t sz, std::true_type)
{
    memcpy(dest, source, sz*sizeof(T));
}

template <typename T>
void Array<T>::increase(size_t n)
{
    Alloc newArray(_array.create(sizeof(T)*n));
    if (capacity() > 0) {
        move(static_cast<T *>(newArray.get()), array(0), _sz, std::is_trivially_copyable<T>());
    }
    _array.swap(newArray);
}

template <typename T>
Array<T>::Array(const Alloc & initial)
    : _array(initial.create(0)),
      _sz(0)
{ }

template <typename T>
Array<T>::Array(Alloc && buf, size_t sz) :
    _array(std::move(buf)),
    _sz(sz)
{
}

template <typename T>
Array<T>::Array(Array &&rhs)
    :  _array(std::move(rhs._array)),
       _sz(rhs._sz)
{
    rhs._sz = 0;
}

template <typename T>
Array<T>::Array(size_t sz, const Alloc & initial) :
    _array(initial.create(sz * sizeof(T))),
    _sz(sz)
{
    construct(array(0), _sz, std::is_trivially_default_constructible<T>());
}

template <typename T>
Array<T>::Array(size_t sz, T value, const Alloc & initial) :
    _array(initial.create(sz * sizeof(T))),
    _sz(sz)
{
    construct(array(0), _sz, value, std::is_trivially_copyable<T>());
}

template <typename T>
Array<T>::Array(const_iterator begin_, const_iterator end_, const Alloc & initial) :
    _array(initial.create(begin_ != end_ ? sizeof(T) * (end_-begin_) : 0)),
    _sz(end_-begin_)
{
    construct(array(0), begin_, _sz, std::is_trivially_copyable<T>());
}

template <typename T>
Array<T>::~Array()
{
    cleanup();
}

template <typename T>
void Array<T>::cleanup()
{
    std::destroy(array(0), array(_sz));
    _sz = 0;
    Alloc().swap(_array);
}

}

<|endoftext|>


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

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

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

 QGROUNDCONTROL 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

#include "QGCUASFileView.h"
#include "uas/QGCUASFileManager.h"

#include <QFileDialog>
#include <QDir>
#include <QMessageBox>

QGCUASFileView::QGCUASFileView(QWidget *parent, QGCUASFileManager *manager) :
    QWidget(parent),
    _manager(manager)
{
    _ui.setupUi(this);

    bool success = connect(_ui.listFilesButton, SIGNAL(clicked()), this, SLOT(_refreshTree()));
    Q_ASSERT(success);
    success = connect(_ui.downloadButton, SIGNAL(clicked()), this, SLOT(_downloadFiles()));
    Q_ASSERT(success);
    success = connect(_ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
    Q_ASSERT(success);
    Q_UNUSED(success);
}

void QGCUASFileView::_downloadFiles(void)
{
    QString dir = QFileDialog::getExistingDirectory(this, tr("Download Directory"),
                                                     QDir::homePath(),
                                                     QFileDialog::ShowDirsOnly
                                                     | QFileDialog::DontResolveSymlinks);
    // And now download to this location
    QString path;
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() == _typeFile) {
        do {
            path.prepend("/" + item->text(0));
            item = item->parent();
        } while (item);
        qDebug() << "Download: " << path;

        bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
        Q_ASSERT(success);
        success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
        Q_ASSERT(success);
        Q_UNUSED(success);
        _manager->downloadPath(path, QDir(dir));
    }
}

void QGCUASFileView::_refreshTree(void)
{
    QTreeWidgetItem* item;

    for (int i=_ui.treeWidget->invisibleRootItem()->childCount(); i>=0; i--) {
        item = _ui.treeWidget->takeTopLevelItem(i);
        delete item;
    }

    _walkIndexStack.clear();
    _walkItemStack.clear();
    _walkIndexStack.append(0);
    _walkItemStack.append(_ui.treeWidget->invisibleRootItem());

    bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
    Q_ASSERT(success);
    success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
    Q_ASSERT(success);
    success = connect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
    Q_ASSERT(success);
    Q_UNUSED(success);

    qDebug() << "List: /";
    _manager->listDirectory("/");
}

void QGCUASFileView::_treeStatusMessage(const QString& msg)
{
    int type;
    if (msg.startsWith("F")) {
        type = _typeFile;
    } else if (msg.startsWith("D")) {
        type = _typeDir;
        if (msg == "D." || msg == "D..") {
            return;
        }
    } else {
        Q_ASSERT(false);
    }

    QTreeWidgetItem* item;
    if (_walkItemStack.count() == 0) {
        item = new QTreeWidgetItem(_ui.treeWidget, type);
    } else {
        item = new QTreeWidgetItem(_walkItemStack.last(), type);
    }
    Q_CHECK_PTR(item);

    item->setText(0, msg.right(msg.size() - 1));
}

void QGCUASFileView::_treeErrorMessage(const QString& msg)
{
    QTreeWidgetItem* item;
    if (_walkItemStack.count() == 0) {
        item = new QTreeWidgetItem(_ui.treeWidget, _typeError);
    } else {
        item = new QTreeWidgetItem(_walkItemStack.last(), _typeError);
    }
    Q_CHECK_PTR(item);

    item->setText(0, tr("Error: ") + msg);
}

void QGCUASFileView::_listComplete(void)
{
    // Walk the current items, traversing down into directories

Again:
    int walkIndex = _walkIndexStack.last();
    QTreeWidgetItem* parentItem = _walkItemStack.last();
    QTreeWidgetItem* childItem = parentItem->child(walkIndex);

    // Loop until we hit a directory
    while (childItem && childItem->type() != _typeDir) {
        // Move to next index at current level
        _walkIndexStack.last() = ++walkIndex;
        childItem = parentItem->child(walkIndex);
    }

    if (childItem) {
        // Process this item
        QString text = childItem->text(0);

        // Move to the next item for processing at this level
        _walkIndexStack.last() = ++walkIndex;

        // Push this new directory on the stack
        _walkItemStack.append(childItem);
        _walkIndexStack.append(0);

        // Ask for the directory list
        QString dir;
        for (int i=1; i<_walkItemStack.count(); i++) {
            QTreeWidgetItem* item = _walkItemStack[i];
            dir.append("/" + item->text(0));
        }
        qDebug() << "List:" << dir;
        _manager->listDirectory(dir);
    } else {
        // We have run out of items at the this level, pop the stack and keep going at that level
        _walkIndexStack.removeLast();
        _walkItemStack.removeLast();
        if (_walkIndexStack.count() != 0) {
            goto Again;
        } else {
            disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
            disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
            disconnect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
        }
    }
}

void QGCUASFileView::_downloadStatusMessage(const QString& msg)
{
    disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
    disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));

    QMessageBox msgBox;
    msgBox.setWindowModality(Qt::ApplicationModal);
    msgBox.setText(msg);
    msgBox.exec();
}

void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
    Q_UNUSED(previous);
    _ui.downloadButton->setEnabled(current->type() == _typeFile);
}
<commit_msg>Silence maybe_uninitialized<commit_after>/*=====================================================================

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

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

 QGROUNDCONTROL 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

#include "QGCUASFileView.h"
#include "uas/QGCUASFileManager.h"

#include <QFileDialog>
#include <QDir>
#include <QMessageBox>

QGCUASFileView::QGCUASFileView(QWidget *parent, QGCUASFileManager *manager) :
    QWidget(parent),
    _manager(manager)
{
    _ui.setupUi(this);

    bool success = connect(_ui.listFilesButton, SIGNAL(clicked()), this, SLOT(_refreshTree()));
    Q_ASSERT(success);
    success = connect(_ui.downloadButton, SIGNAL(clicked()), this, SLOT(_downloadFiles()));
    Q_ASSERT(success);
    success = connect(_ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
    Q_ASSERT(success);
    Q_UNUSED(success);
}

void QGCUASFileView::_downloadFiles(void)
{
    QString dir = QFileDialog::getExistingDirectory(this, tr("Download Directory"),
                                                     QDir::homePath(),
                                                     QFileDialog::ShowDirsOnly
                                                     | QFileDialog::DontResolveSymlinks);
    // And now download to this location
    QString path;
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() == _typeFile) {
        do {
            path.prepend("/" + item->text(0));
            item = item->parent();
        } while (item);
        qDebug() << "Download: " << path;

        bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
        Q_ASSERT(success);
        success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
        Q_ASSERT(success);
        Q_UNUSED(success);
        _manager->downloadPath(path, QDir(dir));
    }
}

void QGCUASFileView::_refreshTree(void)
{
    QTreeWidgetItem* item;

    for (int i=_ui.treeWidget->invisibleRootItem()->childCount(); i>=0; i--) {
        item = _ui.treeWidget->takeTopLevelItem(i);
        delete item;
    }

    _walkIndexStack.clear();
    _walkItemStack.clear();
    _walkIndexStack.append(0);
    _walkItemStack.append(_ui.treeWidget->invisibleRootItem());

    bool success = connect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
    Q_ASSERT(success);
    success = connect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
    Q_ASSERT(success);
    success = connect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
    Q_ASSERT(success);
    Q_UNUSED(success);

    qDebug() << "List: /";
    _manager->listDirectory("/");
}

void QGCUASFileView::_treeStatusMessage(const QString& msg)
{
    int type;
    if (msg.startsWith("F")) {
        type = _typeFile;
    } else if (msg.startsWith("D")) {
        type = _typeDir;
        if (msg == "D." || msg == "D..") {
            return;
        }
    } else {
        Q_ASSERT(false);
        return; // Silence maybe-unitialized on type below
    }

    QTreeWidgetItem* item;
    if (_walkItemStack.count() == 0) {
        item = new QTreeWidgetItem(_ui.treeWidget, type);
    } else {
        item = new QTreeWidgetItem(_walkItemStack.last(), type);
    }
    Q_CHECK_PTR(item);

    item->setText(0, msg.right(msg.size() - 1));
}

void QGCUASFileView::_treeErrorMessage(const QString& msg)
{
    QTreeWidgetItem* item;
    if (_walkItemStack.count() == 0) {
        item = new QTreeWidgetItem(_ui.treeWidget, _typeError);
    } else {
        item = new QTreeWidgetItem(_walkItemStack.last(), _typeError);
    }
    Q_CHECK_PTR(item);

    item->setText(0, tr("Error: ") + msg);
}

void QGCUASFileView::_listComplete(void)
{
    // Walk the current items, traversing down into directories

Again:
    int walkIndex = _walkIndexStack.last();
    QTreeWidgetItem* parentItem = _walkItemStack.last();
    QTreeWidgetItem* childItem = parentItem->child(walkIndex);

    // Loop until we hit a directory
    while (childItem && childItem->type() != _typeDir) {
        // Move to next index at current level
        _walkIndexStack.last() = ++walkIndex;
        childItem = parentItem->child(walkIndex);
    }

    if (childItem) {
        // Process this item
        QString text = childItem->text(0);

        // Move to the next item for processing at this level
        _walkIndexStack.last() = ++walkIndex;

        // Push this new directory on the stack
        _walkItemStack.append(childItem);
        _walkIndexStack.append(0);

        // Ask for the directory list
        QString dir;
        for (int i=1; i<_walkItemStack.count(); i++) {
            QTreeWidgetItem* item = _walkItemStack[i];
            dir.append("/" + item->text(0));
        }
        qDebug() << "List:" << dir;
        _manager->listDirectory(dir);
    } else {
        // We have run out of items at the this level, pop the stack and keep going at that level
        _walkIndexStack.removeLast();
        _walkItemStack.removeLast();
        if (_walkIndexStack.count() != 0) {
            goto Again;
        } else {
            disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_treeStatusMessage(QString)));
            disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_treeErrorMessage(QString)));
            disconnect(_manager, SIGNAL(listComplete(void)), this, SLOT(_listComplete(void)));
        }
    }
}

void QGCUASFileView::_downloadStatusMessage(const QString& msg)
{
    disconnect(_manager, SIGNAL(statusMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));
    disconnect(_manager, SIGNAL(errorMessage(QString)), this, SLOT(_downloadStatusMessage(QString)));

    QMessageBox msgBox;
    msgBox.setWindowModality(Qt::ApplicationModal);
    msgBox.setText(msg);
    msgBox.exec();
}

void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
    Q_UNUSED(previous);
    _ui.downloadButton->setEnabled(current->type() == _typeFile);
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/testapp.h>
#include <vespamalloc/malloc/allocchunk.h>
#include <vespamalloc/malloc/mmappool.h>

TEST("verify lock freeness of atomics"){
    {
        std::atomic<uint32_t> uint32V;
        ASSERT_TRUE(uint32V.is_lock_free());
    }
    {
        std::atomic<uint64_t> uint64V;
        ASSERT_TRUE(uint64V.is_lock_free());
    }
    {
        std::atomic<vespamalloc::TaggedPtr> taggedPtr;
        ASSERT_EQUAL(16u, sizeof(vespamalloc::TaggedPtr));
        // See https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02344.html for background
        ASSERT_TRUE(taggedPtr.is_lock_free() || !taggedPtr.is_lock_free());
    }

}

TEST("test explicit mmap/munmap") {
    vespamalloc::MMapPool mmapPool;
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());

    void * mmap1 = mmapPool.mmap(0xe000);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0xe000u, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(0xe000u, mmapPool.get_size(mmap1));
    mmapPool.unmap(mmap1);
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());
    mmap1 = mmapPool.mmap(0xe000);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0xe000u, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(0xe000u, mmapPool.get_size(mmap1));

    void * mmap2 = mmapPool.mmap(0x1e000);
    EXPECT_EQUAL(2u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0x2c000u, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(0xe000u, mmapPool.get_size(mmap1));
    EXPECT_EQUAL(0x1e000u, mmapPool.get_size(mmap2));
    mmapPool.unmap(mmap1);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0x1e000u, mmapPool.getMmappedBytes());
    mmapPool.unmap(mmap2);
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Handle 64k page size in vespamalloc testatomic unit test.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/testapp.h>
#include <vespamalloc/malloc/allocchunk.h>
#include <vespamalloc/malloc/mmappool.h>
#include <unistd.h>

TEST("verify lock freeness of atomics"){
    {
        std::atomic<uint32_t> uint32V;
        ASSERT_TRUE(uint32V.is_lock_free());
    }
    {
        std::atomic<uint64_t> uint64V;
        ASSERT_TRUE(uint64V.is_lock_free());
    }
    {
        std::atomic<vespamalloc::TaggedPtr> taggedPtr;
        ASSERT_EQUAL(16u, sizeof(vespamalloc::TaggedPtr));
        // See https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02344.html for background
        ASSERT_TRUE(taggedPtr.is_lock_free() || !taggedPtr.is_lock_free());
    }

}

TEST("test explicit mmap/munmap") {
    vespamalloc::MMapPool mmapPool;
    size_t page_size = getpagesize();
    size_t mmap1_size = 3 * page_size;
    size_t mmap2_size = 7 * page_size;
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());

    void * mmap1 = mmapPool.mmap(mmap1_size);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(mmap1_size, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(mmap1_size, mmapPool.get_size(mmap1));
    mmapPool.unmap(mmap1);
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());
    mmap1 = mmapPool.mmap(mmap1_size);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(mmap1_size, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(mmap1_size, mmapPool.get_size(mmap1));

    void * mmap2 = mmapPool.mmap(mmap2_size);
    EXPECT_EQUAL(2u, mmapPool.getNumMappings());
    EXPECT_EQUAL(mmap1_size + mmap2_size, mmapPool.getMmappedBytes());
    EXPECT_EQUAL(mmap1_size, mmapPool.get_size(mmap1));
    EXPECT_EQUAL(mmap2_size, mmapPool.get_size(mmap2));
    mmapPool.unmap(mmap1);
    EXPECT_EQUAL(1u, mmapPool.getNumMappings());
    EXPECT_EQUAL(mmap2_size, mmapPool.getMmappedBytes());
    mmapPool.unmap(mmap2);
    EXPECT_EQUAL(0u, mmapPool.getNumMappings());
    EXPECT_EQUAL(0u, mmapPool.getMmappedBytes());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>#include <unistd.h>
#include <getopt.h>
#include <libgen.h> // FIXME

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <map>

#include "cppunit-header.h"

typedef std::map<std::string, CppUnit::Outputter *> OutputterMap;
typedef std::map<std::string, CppUnit::TestListener *> ListenerMap;

//////////////////////////////////////////////////////////////////////
void usage()
{
}

//////////////////////////////////////////////////////////////////////
void dump(CppUnit::Test * test)
{
	if (test)
	{
		std::cout << test->getName() << std::endl;

		if (test->getChildTestCount())
		{
			for (int i = 0; i < test->getChildTestCount(); i++)
			{
				dump(test->getChildTestAt(i));
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////
CppUnit::Test * find(CppUnit::Test * root, const std::string & name)
{
	CppUnit::Test * found = nullptr;

	if (root)
	{
		if (name == root->getName())
			found = root;
		else if (root->getChildTestCount())
		{
			for (int i = 0; (!found) && i < root->getChildTestCount(); ++i)
				found = find(root->getChildTestAt(i), name);
		}
	}

	return found;
}

//////////////////////////////////////////////////////////////////////
int main(int argc, char ** argv)
{
	CppUnit::TestResult result;
	CppUnit::TestResultCollector collector;

	std::cerr << "----> Starting test" << std::endl;

	result.addListener(&collector);

	std::cerr << "----> Starting test2" << std::endl;
	OutputterMap allOutputters{
		{ "compiler", new CppUnit::CompilerOutputter(&collector, std::cout)},
		{ "text", new CppUnit::TextOutputter(&collector, std::cout) },
		{ "xml", new CppUnit::XmlOutputter(&collector, std::cout) },
		{"none", nullptr },
	};

	ListenerMap allListeners{
		{ "dots", new CppUnit::TextTestProgressListener() },
		{ "brief", new CppUnit::BriefTestProgressListener() },
		{ "none", nullptr },
	};

	CppUnit::Outputter * outputter = allOutputters.find("compiler")->second;
	CppUnit::TestListener * listener = allListeners.find("dots")->second;

	char flag = 0;

	std::string runTest = basename(argv[0]);

	std::cerr << "----> Starting test3" << std::endl;
	if (!find(CppUnit::TestFactoryRegistry::getRegistry().makeTest(), runTest))
	{
		runTest = "All Tests";
	}

	int repeat = 1;

	while ((flag = getopt(argc, argv, "r:t:o:p:lh")) != -1)
	{
		switch(flag)
		{
		case 'r':
			repeat = atoi(optarg);
			break;

		case 'o':
			{
			OutputterMap::const_iterator it = allOutputters.find(optarg);

			if (it == allOutputters.end())
			{
				std::cerr << "Unknown outputter: " << optarg << std::endl;
				return 1;
			}

			outputter = it->second;
			}
			break;

		case 'p':
			{
			std::string progress(optarg);

			ListenerMap::const_iterator it = allListeners.find(optarg);

			if (it == allListeners.end())
			{
				std::cerr << "Unknown listener: " << optarg << std::endl;
				return 1;
			}

			listener = it->second;
			}
			break;

		case 'l':
			dump(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
			return 0;
			break; // not reached

		case 't':
			runTest = optarg;
			break;

		case 'h':
		default:
			usage();
			return 1;
			break; // not reached
		}

	}

	if (listener != nullptr)
		result.addListener(listener);

	std::cerr << "----> Starting test4" << std::endl;
	CppUnit::Test * run = find(CppUnit::TestFactoryRegistry::getRegistry().makeTest(), runTest);

	if (run == nullptr)
	{
		std::cerr << "Unknown test case: " << runTest << std::endl;
		return 1;
	}

	CppUnit::TestRunner runner;

	runner.addTest(run);

	for (int i = 0; i < repeat; i++) {
		runner.run(result);
	}

	std::cerr << "----> Starting test5" << std::endl;
	if (outputter) outputter->write();

	return collector.testErrors() + collector.testFailures();
}

<commit_msg>Removed debug statements<commit_after>#include <unistd.h>
#include <getopt.h>
#include <libgen.h> // FIXME

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <map>

#include "cppunit-header.h"

typedef std::map<std::string, CppUnit::Outputter *> OutputterMap;
typedef std::map<std::string, CppUnit::TestListener *> ListenerMap;

//////////////////////////////////////////////////////////////////////
void usage()
{
}

//////////////////////////////////////////////////////////////////////
void dump(CppUnit::Test * test)
{
	if (test)
	{
		std::cout << test->getName() << std::endl;

		if (test->getChildTestCount())
		{
			for (int i = 0; i < test->getChildTestCount(); i++)
			{
				dump(test->getChildTestAt(i));
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////
CppUnit::Test * find(CppUnit::Test * root, const std::string & name)
{
	CppUnit::Test * found = nullptr;

	if (root)
	{
		if (name == root->getName())
			found = root;
		else if (root->getChildTestCount())
		{
			for (int i = 0; (!found) && i < root->getChildTestCount(); ++i)
				found = find(root->getChildTestAt(i), name);
		}
	}

	return found;
}

//////////////////////////////////////////////////////////////////////
int main(int argc, char ** argv)
{
	CppUnit::TestResult result;
	CppUnit::TestResultCollector collector;

	result.addListener(&collector);

	OutputterMap allOutputters{
		{ "compiler", new CppUnit::CompilerOutputter(&collector, std::cout)},
		{ "text", new CppUnit::TextOutputter(&collector, std::cout) },
		{ "xml", new CppUnit::XmlOutputter(&collector, std::cout) },
		{"none", nullptr },
	};

	ListenerMap allListeners{
		{ "dots", new CppUnit::TextTestProgressListener() },
		{ "brief", new CppUnit::BriefTestProgressListener() },
		{ "none", nullptr },
	};

	CppUnit::Outputter * outputter = allOutputters.find("compiler")->second;
	CppUnit::TestListener * listener = allListeners.find("dots")->second;

	char flag = 0;

	std::string runTest = basename(argv[0]);

	if (!find(CppUnit::TestFactoryRegistry::getRegistry().makeTest(), runTest))
	{
		runTest = "All Tests";
	}

	int repeat = 1;

	while ((flag = getopt(argc, argv, "r:t:o:p:lh")) != -1)
	{
		switch(flag)
		{
		case 'r':
			repeat = atoi(optarg);
			break;

		case 'o':
			{
			OutputterMap::const_iterator it = allOutputters.find(optarg);

			if (it == allOutputters.end())
			{
				std::cerr << "Unknown outputter: " << optarg << std::endl;
				return 1;
			}

			outputter = it->second;
			}
			break;

		case 'p':
			{
			std::string progress(optarg);

			ListenerMap::const_iterator it = allListeners.find(optarg);

			if (it == allListeners.end())
			{
				std::cerr << "Unknown listener: " << optarg << std::endl;
				return 1;
			}

			listener = it->second;
			}
			break;

		case 'l':
			dump(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
			return 0;
			break; // not reached

		case 't':
			runTest = optarg;
			break;

		case 'h':
		default:
			usage();
			return 1;
			break; // not reached
		}

	}

	if (listener != nullptr)
		result.addListener(listener);

	CppUnit::Test * run = find(CppUnit::TestFactoryRegistry::getRegistry().makeTest(), runTest);

	if (run == nullptr)
	{
		std::cerr << "Unknown test case: " << runTest << std::endl;
		return 1;
	}

	CppUnit::TestRunner runner;

	runner.addTest(run);

	for (int i = 0; i < repeat; i++) {
		runner.run(result);
	}

	if (outputter) outputter->write();

	return collector.testErrors() + collector.testFailures();
}

<|endoftext|>


<commit_before>//
//  CPUFriend.cpp
//  CPUFriend
//
//  Copyright © 2017 Vanilla. All rights reserved.
//

#include <Headers/kern_api.hpp>

#include "CPUFriend.hpp"

static const char *kextX86PP[] = { "/System/Library/Extensions/IOPlatformPluginFamily.kext/Contents/PlugIns/X86PlatformPlugin.kext/Contents/MacOS/X86PlatformPlugin" };
static const char *kextX86PPId = "com.apple.driver.X86PlatformPlugin";

static KernelPatcher::KextInfo kextList[] {
	{ kextX86PPId, kextX86PP, arrsize(kextX86PP), {}, {}, KernelPatcher::KextInfo::Unloaded }
};

static constexpr size_t kextListSize = arrsize(kextList);

static CPUFriendPlugin *callbackCpuf = nullptr;

OSDefineMetaClassAndStructors(CPUFriendData, IOService)

IOService *CPUFriendData::probe(IOService *provider, SInt32 *score)
{
	if (provider) {
		if (callbackCpuf) {
			if (!callbackCpuf->frequencyData) {
				auto name = provider->getName();
				if (!name)
					name = "(null)";
				DBGLOG("probe", "looking for cf-frequency-data in %s", name);

				auto data = OSDynamicCast(OSData, provider->getProperty("cf-frequency-data"));
				if (!data) {
					auto cpu = provider->getParentEntry(gIOServicePlane);
					if (cpu) {
						name = cpu->getName();
						if (!name)
							name = "(null)";
						DBGLOG("probe", "looking for cf-frequency-data in %s", name);
						data = OSDynamicCast(OSData, cpu->getProperty("cf-frequency-data"));
					} else {
						SYSLOG("probe", "unable to access cpu parent");
					}
				}

				if (data) {
					callbackCpuf->frequencyDataSize = data->getLength();
					callbackCpuf->frequencyData = data->getBytesNoCopy();
				} else {
					SYSLOG("probe", "failed to obtain cf-frequency-data");
				}
			}
		} else
			SYSLOG("probe", "missing storage instance");
	}

	return nullptr;
}

bool CPUFriendPlugin::init()
{
	callbackCpuf = this;

	LiluAPI::Error error = lilu.onKextLoad(kextList, kextListSize, [](void *user, KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) {
		static_cast<CPUFriendPlugin *>(user)->processKext(patcher, index, address, size);
	}, this);

	if (error != LiluAPI::Error::NoError) {
		SYSLOG("init", "failed to register onKextLoad method %d", error);
		return false;
	}

	return true;
}

void CPUFriendPlugin::myConfigResourceCallback(uint32_t requestTag, kern_return_t result, const void *resourceData, uint32_t resourceDataLength, void *context)
{
	if (callbackCpuf && callbackCpuf->orgConfigLoadCallback) {
		auto data = callbackCpuf->frequencyData;
		auto sz = callbackCpuf->frequencyDataSize;
		if (data && sz > 0) {
			DBGLOG("myConfigResourceCallback", "feeding frequency data %u", sz);
			resourceData = data;
			resourceDataLength = sz;
			result = kOSReturnSuccess;
		} else {
			SYSLOG("myConfigResourceCallback", "failed to feed cpu data (%u, %d)", sz, data != nullptr);
		}
		callbackCpuf->orgConfigLoadCallback(requestTag, result, resourceData, resourceDataLength, context);
	} else {
		SYSLOG("myConfigResourceCallback", "config callback arrived at nowhere");
	}
}

void CPUFriendPlugin::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size)
{
	if (progressState != ProcessingState::EverythingDone) {
		for (size_t i = 0; i < kextListSize; i++) {
			if (kextList[i].loadIndex == index) {
				DBGLOG("processKext", "current kext is %s progressState %d", kextList[i].id, progressState);
				// clear error from the very beginning just in case
				patcher.clearError();
				if (!strcmp(kextList[i].id, kextX86PPId)) {
					auto callback = patcher.solveSymbol(index, "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
					if (callback) {
						orgConfigLoadCallback = reinterpret_cast<t_callback>(patcher.routeFunction(callback, reinterpret_cast<mach_vm_address_t>(myConfigResourceCallback), true));
						if (patcher.getError() == KernelPatcher::Error::NoError) {
							DBGLOG("processKext", "routed %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
						} else {
							SYSLOG("processKext", "failed to route %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
						}
					} else {
						SYSLOG("processKext", "failed to find %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
					}
					progressState |= ProcessingState::CallbackRouted;
				}
			}
		}
	}
	// Ignore all the errors for other processors
	patcher.clearError();
}
<commit_msg>kext matching is refactored (using indexes instead of strcmp).<commit_after>//
//  CPUFriend.cpp
//  CPUFriend
//
//  Copyright © 2017 Vanilla. All rights reserved.
//

#include <Headers/kern_api.hpp>

#include "CPUFriend.hpp"

static const char *kextX86PP[] = { "/System/Library/Extensions/IOPlatformPluginFamily.kext/Contents/PlugIns/X86PlatformPlugin.kext/Contents/MacOS/X86PlatformPlugin" };

enum : size_t {
	KextX86
};

static KernelPatcher::KextInfo kextList[] {
	{ "com.apple.driver.X86PlatformPlugin", kextX86PP, arrsize(kextX86PP), {}, {}, KernelPatcher::KextInfo::Unloaded }
};

static constexpr size_t kextListSize = arrsize(kextList);

static CPUFriendPlugin *callbackCpuf = nullptr;

OSDefineMetaClassAndStructors(CPUFriendData, IOService)

IOService *CPUFriendData::probe(IOService *provider, SInt32 *score)
{
	if (provider) {
		if (callbackCpuf) {
			if (!callbackCpuf->frequencyData) {
				auto name = provider->getName();
				if (!name)
					name = "(null)";
				DBGLOG("probe", "looking for cf-frequency-data in %s", name);

				auto data = OSDynamicCast(OSData, provider->getProperty("cf-frequency-data"));
				if (!data) {
					auto cpu = provider->getParentEntry(gIOServicePlane);
					if (cpu) {
						name = cpu->getName();
						if (!name)
							name = "(null)";
						DBGLOG("probe", "looking for cf-frequency-data in %s", name);
						data = OSDynamicCast(OSData, cpu->getProperty("cf-frequency-data"));
					} else {
						SYSLOG("probe", "unable to access cpu parent");
					}
				}

				if (data) {
					callbackCpuf->frequencyDataSize = data->getLength();
					callbackCpuf->frequencyData = data->getBytesNoCopy();
				} else {
					SYSLOG("probe", "failed to obtain cf-frequency-data");
				}
			}
		} else
			SYSLOG("probe", "missing storage instance");
	}

	return nullptr;
}

bool CPUFriendPlugin::init()
{
	callbackCpuf = this;

	LiluAPI::Error error = lilu.onKextLoad(kextList, kextListSize, [](void *user, KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) {
		static_cast<CPUFriendPlugin *>(user)->processKext(patcher, index, address, size);
	}, this);

	if (error != LiluAPI::Error::NoError) {
		SYSLOG("init", "failed to register onKextLoad method %d", error);
		return false;
	}

	return true;
}

void CPUFriendPlugin::myConfigResourceCallback(uint32_t requestTag, kern_return_t result, const void *resourceData, uint32_t resourceDataLength, void *context)
{
	if (callbackCpuf && callbackCpuf->orgConfigLoadCallback) {
		auto data = callbackCpuf->frequencyData;
		auto sz = callbackCpuf->frequencyDataSize;
		if (data && sz > 0) {
			DBGLOG("myConfigResourceCallback", "feeding frequency data %u", sz);
			resourceData = data;
			resourceDataLength = sz;
			result = kOSReturnSuccess;
		} else {
			SYSLOG("myConfigResourceCallback", "failed to feed cpu data (%u, %d)", sz, data != nullptr);
		}
		callbackCpuf->orgConfigLoadCallback(requestTag, result, resourceData, resourceDataLength, context);
	} else {
		SYSLOG("myConfigResourceCallback", "config callback arrived at nowhere");
	}
}

void CPUFriendPlugin::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size)
{
	if (progressState != ProcessingState::EverythingDone) {
		for (size_t i = 0; i < kextListSize; i++) {
			if (kextList[i].loadIndex == index) {
				DBGLOG("processKext", "current kext is %s progressState %d", kextList[i].id, progressState);
				// clear error from the very beginning just in case
				patcher.clearError();
				if (i == KextX86) {
					auto callback = patcher.solveSymbol(index, "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
					if (callback) {
						orgConfigLoadCallback = reinterpret_cast<t_callback>(patcher.routeFunction(callback, reinterpret_cast<mach_vm_address_t>(myConfigResourceCallback), true));

						if (patcher.getError() == KernelPatcher::Error::NoError)
							DBGLOG("processKext", "routed %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
						else
							SYSLOG("processKext", "failed to route %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
					} else {
						SYSLOG("processKext", "failed to find %s", "__ZN17X86PlatformPlugin22configResourceCallbackEjiPKvjPv");
					}
					progressState |= ProcessingState::CallbackRouted;
				}
			}
		}
	}
	// Ignore all the errors for other processors
	patcher.clearError();
}
<|endoftext|>