<commit_before>#include <fstream>
#include <sstream>
#include <queue>

#include "pugixml/pugixml.hpp"

#include "pixelboost/data/resources/svgResource.h"
#include "pixelboost/file/fileSystem.h"

using namespace pb;

PB_DEFINE_RESOURCE(pb::SvgResource)

class PathTokenizer
{
public:
    PathTokenizer(const std::string& path);
    ~PathTokenizer();

    enum TokenType
    {
        kTokenTypeUnknown,
        kTokenTypeMoveAbsolute,
        kTokenTypeMoveRelative,
        kTokenTypeCurveAbsolute,
        kTokenTypeCurveRelative,
        kTokenTypeSmoothAbsolute,
        kTokenTypeSmoothRelative,
        kTokenTypeNumber,
    };

    struct Token
    {
        Token(TokenType type = kTokenTypeUnknown, const std::string& data = "");

        TokenType type;
        std::string data;
    };

    const std::queue<Token>& GetTokens();

    bool Tokenize();

private:
    enum State
    {
        kStateMain,
        kStateNumber,
    };

    std::string _Path;
    State _State;
    int _Index;

    std::queue<Token> _Tokens;
};

class PathParser
{
public:
    PathParser(const std::string& path, float scale);
    ~PathParser();

    bool Parse(SvgPath& path);

    struct Point
    {
        Point(float x1, float y1, float cx1, float cy1,  float cx2, float cy2, float x2, float y2);

        float x1;
        float y1;
        float cx1;
        float cy1;

        float cx2;
        float cy2;
        float x2;
        float y2;
    };

    std::vector<Point> Points;

private:
    std::queue<PathTokenizer::Token> _Tokens;

    float _Scale;
    float _X;
    float _Y;
    PathTokenizer _Tokenizer;
};

SvgResource::SvgResource(ResourcePool* pool, const std::string& filename)
    : Resource(pool, filename)
{

}

SvgResource::~SvgResource()
{

}

ResourceError SvgResource::ProcessResource(ResourcePool* pool, ResourceProcess process, const std::string& filename, std::string& errorDetails)
{
    switch (process)
    {
        case kResourceProcessLoad:
        {
            return Load(filename);
        }
        case kResourceProcessProcess:
        {
            pugi::xpath_node svg = _Xml.select_single_node("svg");

            _Size = glm::vec2(atof(svg.node().attribute("width").value()), atof(svg.node().attribute("height").value()))/32.f;

            ParseAll();

            _Xml.reset();

            for (auto& group : _Groups)
            {
                for (auto& path : group.second.Paths)
                {
                    path.Curve.Parameterize();
                }
            }
            return kResourceErrorNone;
        }
        case kResourceProcessUnload:
        {
            _Groups.clear();
            _Size = glm::vec2(0,0);

            return kResourceErrorNone;
        }
        case kResourceProcessPostProcess:
        {
            return kResourceErrorNone;
        }
    }
}

const std::map<std::string, SvgGroup>& SvgResource::GetGroups()
{
    return _Groups;
}

ResourceError SvgResource::Load(const std::string& filename)
{
    std::string data;

    pb::File* file = pb::FileSystem::Instance()->OpenFile(filename, pb::kFileModeRead);

    if (!file)
    {
        return kResourceErrorNoSuchResource;
    }

    file->ReadAll(data);
    delete file;

    if (!_Xml.load_buffer(data.c_str(), data.length()))
    {
        return kResourceErrorSystemError;
    }

    return kResourceErrorNone;
}

bool SvgResource::ParseAll()
{
    pugi::xpath_node_set groups = _Xml.select_nodes("/svg/g");

    for (const auto& group : groups)
    {
        ParseGroup(group.node().attribute("id").value());
    }

    return true;
}

bool SvgResource::ParseGroup(const std::string& name)
{
    SvgGroup group;

    char query[256];

    snprintf(query, 256, "/svg/g[@id='%s']/path", name.c_str());
    pugi::xpath_node_set paths = _Xml.select_nodes(query);

    for (pugi::xpath_node_set::const_iterator pathIt = paths.begin(); pathIt != paths.end(); ++pathIt)
    {
        SvgPath path;
        path.Name = pathIt->node().attribute("id").value();

        PathParser parser(pathIt->node().attribute("d").value(), 32.f);
        parser.Parse(path);

        if (parser.Points.size() > 1)
        {
            auto pointA = parser.Points[0];
            auto pointB = parser.Points[1];
            glm::vec2 pos = glm::vec2(pointA.x1, pointA.y1);
            path.Curve.AddPoint(HermiteCurve2D::Point(pos, glm::vec2(0,0), (glm::vec2(pointB.cx1, pointB.cy1)-pos)*3.f));
        }
        for (int i=0; i<parser.Points.size()-1; i++)
        {
            auto pointA = parser.Points[i];
            auto pointB = parser.Points[i+1];
            glm::vec2 pos = glm::vec2(pointB.x1, pointB.y1);
            path.Curve.AddPoint(HermiteCurve2D::Point(pos, (glm::vec2(pointA.cx2, pointA.cy2)-pos)*3.f, (glm::vec2(pointB.cx1, pointB.cy1)-pos)*3.f));
        }

        group.Paths.push_back(path);
    }

    _Groups[name] = group;

    return true;
}

PathTokenizer::Token::Token(TokenType type, const std::string& data)
    : data(data)
    , type(type)
{

}

PathTokenizer::PathTokenizer(const std::string& path)
{
    _Path = path;
}

PathTokenizer::~PathTokenizer()
{

}

const std::queue<PathTokenizer::Token>& PathTokenizer::GetTokens()
{
    return _Tokens;
}

bool PathTokenizer::Tokenize()
{
    std::string tokenData;
    bool finished = false;
    _Index = 0;
    _State = kStateMain;
    while (!finished)
    {
        char character = _Index >= _Path.length() ? 0 : _Path[_Index];

        switch (_State)
        {
            case kStateMain:
            {
                int absolute = false;
                if (character >= 'A' && character <= 'Z')
                {
                    absolute = true;
                    character -= 'A'-'a';
                }

                if (character == 'm')
                {
                    _Tokens.push(Token(absolute?kTokenTypeMoveAbsolute:kTokenTypeMoveRelative));
                } else if (character == 'c')
                {
                    _Tokens.push(Token(absolute?kTokenTypeCurveAbsolute:kTokenTypeCurveRelative));
                } else if (character == 's')
                {
                    _Tokens.push(Token(absolute?kTokenTypeSmoothAbsolute:kTokenTypeSmoothRelative));
                } else if ((character >= '0' && character <= '9') || character == '-') {
                    _State = kStateNumber;
                    continue;
                } else if (character == 0)
                {
                    finished = true;
                }

                _Index++;

                break;
            }

            case kStateNumber:
            {
                if ((character >= '0' && character <= '9') || character == '-' || character == '.')
                {
                    if (character == '-')
                    {
                        if (tokenData.length())
                        {
                            _Tokens.push(Token(kTokenTypeNumber, tokenData));
                            tokenData = "";
                        }
                    }
                } else
                {
                    if (tokenData.length())
                    {
                        _Tokens.push(Token(kTokenTypeNumber, tokenData));
                        tokenData = "";
                    }

                    _State = kStateMain;
                    continue;
                }

                tokenData += character;
                _Index++;
                break;
            }
        }
    }

    return true;
}

PathParser::Point::Point(float x1, float y1, float cx1, float cy1,  float cx2, float cy2, float x2, float y2)
    : x1(x1)
    , y1(y1)
    , cx1(cx1)
    , cy1(cy1)
    , cx2(cx2)
    , cy2(cy2)
    , x2(x2)
    , y2(y2)
{

}

PathParser::PathParser(const std::string& path, float scale)
    : _Tokenizer(path)
    , _Scale(scale)
{

}

PathParser::~PathParser()
{

}

bool PathParser::Parse(SvgPath& path)
{
    _X = 0;
    _Y = 0;

    _Tokenizer.Tokenize();
    _Tokens = _Tokenizer.GetTokens();

    std::vector<float> numbers;
    PathTokenizer::Token stateToken;
    while (_Tokens.size())
    {
        PathTokenizer::Token token = _Tokens.front();
        _Tokens.pop();

        if (token.type != PathTokenizer::kTokenTypeNumber)
        {
            if (numbers.size() != 0)
                return false;

            stateToken = token;
        } else {
            numbers.push_back(atof(token.data.c_str()));
        }

        switch (stateToken.type)
        {
            case PathTokenizer::kTokenTypeMoveAbsolute:
            {
                if (numbers.size() == 2)
                {
                    _X = numbers[0];
                    _Y = numbers[1];
                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeMoveRelative:
            {
                if (numbers.size() == 2)
                {
                    _X += numbers[0];
                    _Y += numbers[1];
                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeCurveAbsolute:
            {
                if (numbers.size() == 6)
                {
                    Points.push_back(Point(_X, _Y, numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5]));

                    _X = numbers[4];
                    _Y = numbers[5];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeCurveRelative:
            {
                if (numbers.size() == 6)
                {
                    Points.push_back(Point(_X, _Y, _X + numbers[0], _Y + numbers[1], _X + numbers[2], _Y + numbers[3], _X + numbers[4], _Y + numbers[5]));

                    _X += numbers[4];
                    _Y += numbers[5];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeSmoothAbsolute:
            {
                if (numbers.size() == 4)
                {
                    float cx;
                    float cy;

                    if (Points.size())
                    {
                        cx = 2*_X - Points.back().cx2;
                        cy = 2*_Y - Points.back().cy2;
                    } else {
                        cx = _X;
                        cy = _Y;
                    }

                    Points.push_back(Point(_X, _Y, cx, cy, numbers[0], numbers[1], numbers[2], numbers[3]));

                    _X = numbers[2];
                    _Y = numbers[3];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeSmoothRelative:
            {
                if (numbers.size() == 4)
                {
                    float cx;
                    float cy;

                    if (Points.size())
                    {
                        cx = 2*_X - Points.back().cx2;
                        cy = 2*_Y - Points.back().cy2;
                    } else {
                        cx = _X;
                        cy = _Y;
                    }

                    Points.push_back(Point(_X, _Y, cx, cy, _X + numbers[0], _Y + numbers[1], _X + numbers[2], _Y + numbers[3]));

                    _X += numbers[2];
                    _Y += numbers[3];

                    numbers.clear();
                }
                break;
            }
            default:
                break;
        }
    }

    for (auto& point : Points)
    {
        point.x1 /= _Scale;
        point.cx1 /= _Scale;
        point.cx2 /= _Scale;
        point.x2 /= _Scale;
        point.y1 = -point.y1/_Scale;
        point.cy1 = -point.cy1/_Scale;
        point.cy2 = -point.cy2/_Scale;
        point.y2 = -point.y2/_Scale;
    }

    return true;
}
<commit_msg>Add missing GetSize implementation in SvgResource<commit_after>#include <fstream>
#include <sstream>
#include <queue>

#include "pugixml/pugixml.hpp"

#include "pixelboost/data/resources/svgResource.h"
#include "pixelboost/file/fileSystem.h"

using namespace pb;

PB_DEFINE_RESOURCE(pb::SvgResource)

class PathTokenizer
{
public:
    PathTokenizer(const std::string& path);
    ~PathTokenizer();

    enum TokenType
    {
        kTokenTypeUnknown,
        kTokenTypeMoveAbsolute,
        kTokenTypeMoveRelative,
        kTokenTypeCurveAbsolute,
        kTokenTypeCurveRelative,
        kTokenTypeSmoothAbsolute,
        kTokenTypeSmoothRelative,
        kTokenTypeNumber,
    };

    struct Token
    {
        Token(TokenType type = kTokenTypeUnknown, const std::string& data = "");

        TokenType type;
        std::string data;
    };

    const std::queue<Token>& GetTokens();

    bool Tokenize();

private:
    enum State
    {
        kStateMain,
        kStateNumber,
    };

    std::string _Path;
    State _State;
    int _Index;

    std::queue<Token> _Tokens;
};

class PathParser
{
public:
    PathParser(const std::string& path, float scale);
    ~PathParser();

    bool Parse(SvgPath& path);

    struct Point
    {
        Point(float x1, float y1, float cx1, float cy1,  float cx2, float cy2, float x2, float y2);

        float x1;
        float y1;
        float cx1;
        float cy1;

        float cx2;
        float cy2;
        float x2;
        float y2;
    };

    std::vector<Point> Points;

private:
    std::queue<PathTokenizer::Token> _Tokens;

    float _Scale;
    float _X;
    float _Y;
    PathTokenizer _Tokenizer;
};

SvgResource::SvgResource(ResourcePool* pool, const std::string& filename)
    : Resource(pool, filename)
{

}

SvgResource::~SvgResource()
{

}

ResourceError SvgResource::ProcessResource(ResourcePool* pool, ResourceProcess process, const std::string& filename, std::string& errorDetails)
{
    switch (process)
    {
        case kResourceProcessLoad:
        {
            return Load(filename);
        }
        case kResourceProcessProcess:
        {
            pugi::xpath_node svg = _Xml.select_single_node("svg");

            _Size = glm::vec2(atof(svg.node().attribute("width").value()), atof(svg.node().attribute("height").value()))/32.f;

            ParseAll();

            _Xml.reset();

            for (auto& group : _Groups)
            {
                for (auto& path : group.second.Paths)
                {
                    path.Curve.Parameterize();
                }
            }
            return kResourceErrorNone;
        }
        case kResourceProcessUnload:
        {
            _Groups.clear();
            _Size = glm::vec2(0,0);

            return kResourceErrorNone;
        }
        case kResourceProcessPostProcess:
        {
            return kResourceErrorNone;
        }
    }
}

const std::map<std::string, SvgGroup>& SvgResource::GetGroups()
{
    return _Groups;
}

glm::vec2 SvgResource::GetSize()
{
    return _Size;
}

ResourceError SvgResource::Load(const std::string& filename)
{
    std::string data;

    pb::File* file = pb::FileSystem::Instance()->OpenFile(filename, pb::kFileModeRead);

    if (!file)
    {
        return kResourceErrorNoSuchResource;
    }

    file->ReadAll(data);
    delete file;

    if (!_Xml.load_buffer(data.c_str(), data.length()))
    {
        return kResourceErrorSystemError;
    }

    return kResourceErrorNone;
}

bool SvgResource::ParseAll()
{
    pugi::xpath_node_set groups = _Xml.select_nodes("/svg/g");

    for (const auto& group : groups)
    {
        ParseGroup(group.node().attribute("id").value());
    }

    return true;
}

bool SvgResource::ParseGroup(const std::string& name)
{
    SvgGroup group;

    char query[256];

    snprintf(query, 256, "/svg/g[@id='%s']/path", name.c_str());
    pugi::xpath_node_set paths = _Xml.select_nodes(query);

    for (pugi::xpath_node_set::const_iterator pathIt = paths.begin(); pathIt != paths.end(); ++pathIt)
    {
        SvgPath path;
        path.Name = pathIt->node().attribute("id").value();

        PathParser parser(pathIt->node().attribute("d").value(), 32.f);
        parser.Parse(path);

        if (parser.Points.size() > 1)
        {
            auto pointA = parser.Points[0];
            auto pointB = parser.Points[1];
            glm::vec2 pos = glm::vec2(pointA.x1, pointA.y1);
            path.Curve.AddPoint(HermiteCurve2D::Point(pos, glm::vec2(0,0), (glm::vec2(pointB.cx1, pointB.cy1)-pos)*3.f));
        }
        for (int i=0; i<parser.Points.size()-1; i++)
        {
            auto pointA = parser.Points[i];
            auto pointB = parser.Points[i+1];
            glm::vec2 pos = glm::vec2(pointB.x1, pointB.y1);
            path.Curve.AddPoint(HermiteCurve2D::Point(pos, (glm::vec2(pointA.cx2, pointA.cy2)-pos)*3.f, (glm::vec2(pointB.cx1, pointB.cy1)-pos)*3.f));
        }

        group.Paths.push_back(path);
    }

    _Groups[name] = group;

    return true;
}

PathTokenizer::Token::Token(TokenType type, const std::string& data)
    : data(data)
    , type(type)
{

}

PathTokenizer::PathTokenizer(const std::string& path)
{
    _Path = path;
}

PathTokenizer::~PathTokenizer()
{

}

const std::queue<PathTokenizer::Token>& PathTokenizer::GetTokens()
{
    return _Tokens;
}

bool PathTokenizer::Tokenize()
{
    std::string tokenData;
    bool finished = false;
    _Index = 0;
    _State = kStateMain;
    while (!finished)
    {
        char character = _Index >= _Path.length() ? 0 : _Path[_Index];

        switch (_State)
        {
            case kStateMain:
            {
                int absolute = false;
                if (character >= 'A' && character <= 'Z')
                {
                    absolute = true;
                    character -= 'A'-'a';
                }

                if (character == 'm')
                {
                    _Tokens.push(Token(absolute?kTokenTypeMoveAbsolute:kTokenTypeMoveRelative));
                } else if (character == 'c')
                {
                    _Tokens.push(Token(absolute?kTokenTypeCurveAbsolute:kTokenTypeCurveRelative));
                } else if (character == 's')
                {
                    _Tokens.push(Token(absolute?kTokenTypeSmoothAbsolute:kTokenTypeSmoothRelative));
                } else if ((character >= '0' && character <= '9') || character == '-') {
                    _State = kStateNumber;
                    continue;
                } else if (character == 0)
                {
                    finished = true;
                }

                _Index++;

                break;
            }

            case kStateNumber:
            {
                if ((character >= '0' && character <= '9') || character == '-' || character == '.')
                {
                    if (character == '-')
                    {
                        if (tokenData.length())
                        {
                            _Tokens.push(Token(kTokenTypeNumber, tokenData));
                            tokenData = "";
                        }
                    }
                } else
                {
                    if (tokenData.length())
                    {
                        _Tokens.push(Token(kTokenTypeNumber, tokenData));
                        tokenData = "";
                    }

                    _State = kStateMain;
                    continue;
                }

                tokenData += character;
                _Index++;
                break;
            }
        }
    }

    return true;
}

PathParser::Point::Point(float x1, float y1, float cx1, float cy1,  float cx2, float cy2, float x2, float y2)
    : x1(x1)
    , y1(y1)
    , cx1(cx1)
    , cy1(cy1)
    , cx2(cx2)
    , cy2(cy2)
    , x2(x2)
    , y2(y2)
{

}

PathParser::PathParser(const std::string& path, float scale)
    : _Tokenizer(path)
    , _Scale(scale)
{

}

PathParser::~PathParser()
{

}

bool PathParser::Parse(SvgPath& path)
{
    _X = 0;
    _Y = 0;

    _Tokenizer.Tokenize();
    _Tokens = _Tokenizer.GetTokens();

    std::vector<float> numbers;
    PathTokenizer::Token stateToken;
    while (_Tokens.size())
    {
        PathTokenizer::Token token = _Tokens.front();
        _Tokens.pop();

        if (token.type != PathTokenizer::kTokenTypeNumber)
        {
            if (numbers.size() != 0)
                return false;

            stateToken = token;
        } else {
            numbers.push_back(atof(token.data.c_str()));
        }

        switch (stateToken.type)
        {
            case PathTokenizer::kTokenTypeMoveAbsolute:
            {
                if (numbers.size() == 2)
                {
                    _X = numbers[0];
                    _Y = numbers[1];
                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeMoveRelative:
            {
                if (numbers.size() == 2)
                {
                    _X += numbers[0];
                    _Y += numbers[1];
                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeCurveAbsolute:
            {
                if (numbers.size() == 6)
                {
                    Points.push_back(Point(_X, _Y, numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5]));

                    _X = numbers[4];
                    _Y = numbers[5];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeCurveRelative:
            {
                if (numbers.size() == 6)
                {
                    Points.push_back(Point(_X, _Y, _X + numbers[0], _Y + numbers[1], _X + numbers[2], _Y + numbers[3], _X + numbers[4], _Y + numbers[5]));

                    _X += numbers[4];
                    _Y += numbers[5];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeSmoothAbsolute:
            {
                if (numbers.size() == 4)
                {
                    float cx;
                    float cy;

                    if (Points.size())
                    {
                        cx = 2*_X - Points.back().cx2;
                        cy = 2*_Y - Points.back().cy2;
                    } else {
                        cx = _X;
                        cy = _Y;
                    }

                    Points.push_back(Point(_X, _Y, cx, cy, numbers[0], numbers[1], numbers[2], numbers[3]));

                    _X = numbers[2];
                    _Y = numbers[3];

                    numbers.clear();
                }
                break;
            }
            case PathTokenizer::kTokenTypeSmoothRelative:
            {
                if (numbers.size() == 4)
                {
                    float cx;
                    float cy;

                    if (Points.size())
                    {
                        cx = 2*_X - Points.back().cx2;
                        cy = 2*_Y - Points.back().cy2;
                    } else {
                        cx = _X;
                        cy = _Y;
                    }

                    Points.push_back(Point(_X, _Y, cx, cy, _X + numbers[0], _Y + numbers[1], _X + numbers[2], _Y + numbers[3]));

                    _X += numbers[2];
                    _Y += numbers[3];

                    numbers.clear();
                }
                break;
            }
            default:
                break;
        }
    }

    for (auto& point : Points)
    {
        point.x1 /= _Scale;
        point.cx1 /= _Scale;
        point.cx2 /= _Scale;
        point.x2 /= _Scale;
        point.y1 = -point.y1/_Scale;
        point.cy1 = -point.cy1/_Scale;
        point.cy2 = -point.cy2/_Scale;
        point.y2 = -point.y2/_Scale;
    }

    return true;
}
<|endoftext|>


<commit_before>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "app/pch.h"
#include "app/coApp.h"

coResult coApp::ProcessEvents()
{
	MSG msg;
	if (::GetMessageW(&msg, NULL, 0, 0))
	{
		::TranslateMessage(&msg);
		::DispatchMessageW(&msg);
	}
	else
	{
		exitRequested = true;
		return msg.wParam == 0;
	}

	return true;
}
<commit_msg>Fixed main app loop on Windows.<commit_after>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "app/pch.h"
#include "app/coApp.h"

coResult coApp::ProcessEvents()
{
	MSG msg;

	while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		// Translate the message and dispatch it to WindowProc()
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}

	if (msg.message == WM_QUIT)
	{
		exitRequested = true;
		return msg.wParam == 0;
	}

	return true;
}
<|endoftext|>


<commit_before> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    FileMonitor::updates(0) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}
<commit_msg>Filemonitor_test: added the test of the second addFile fonction as fileChange2_test (reverted from commit d818ce134d8ce2c4ce408b9bd07e88ac72a0edee)<commit_after> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    FileMonitor::updates(0) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange2_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath(""),"existing.txt", &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}
<|endoftext|>


<commit_before>#include <gtest\gtest.h>

#include "vm.h"

using namespace elsa::vm;

class StructTest : public testing::Test {
protected:
	virtual void SetUp()
	{
		int ep = 0;
		vm_.add_constant_entry(new FunctionInfo("main", 0, 2, ep, FunctionType::Static));
		vm_.set_entry_point(ep);

		auto si = new StructInfo("my_struct");
		si->add_field(new FieldInfo("field0", OType::Int));
		si->add_field(new FieldInfo("field1", OType::Float));
		si->add_field(new FieldInfo("field2", OType::Int));
		si->add_field(new FieldInfo("field3", OType::Float));
		si->add_field(new FieldInfo("field4", OType::Int));
		vm_.add_constant_entry(si);

		vm_.add_constant_entry(new FloatEntry(12.0f));
		vm_.add_constant_entry(new FloatEntry(99.0f));

		auto si2 = new StructInfo("my_struct2");
		si2->add_field(new FieldInfo("field0", OType::GCOPtr));
		si2->add_field(new FieldInfo("field1", OType::GCOPtr));

		vm_.add_constant_entry(si2);
	}

	virtual void TearDown() {}

	VM vm_;
};

TEST_F(StructTest, NEW)
{
	std::vector<int> p =
	{
		new_struct, 1
	};

	vm_.set_program(p);
	vm_.execute();

	auto obj = vm_.eval_stack_top();

	ASSERT_EQ(OType::GCOPtr, obj.get_type());

	auto si = obj.gco()->si;

	ASSERT_EQ("my_struct", si->get_name());
	ASSERT_EQ(20, si->get_size());
}

TEST_F(StructTest, FIELD_STORE_LOAD)
{
	std::vector<int> p =
	{
		new_struct, 1,
		s_local, 0,

		// Store 77 in field 0 (int)
		l_local, 0,
		iconst, 77,
		s_field, 0,

		// Store 12.0 in field 1 (float)
		l_local, 0,
		fconst, 2,
		s_field, 1,

		// Store 100 in field 2 (int)
		l_local, 0,
		iconst, 100,
		s_field, 2,

		// Store 99.0 in field 3 (float)
		l_local, 0,
		fconst, 3,
		s_field, 3,

		// Store -1829 in field 4 (int)
		l_local, 0,
		iconst, -1829,
		s_field, 4,

		// Load field 0
		l_local, 0,
		l_field, 0,
		halt,

		// Load field 1
		l_local, 0,
		l_field, 1,
		halt,

		// Load field 2
		l_local, 0,
		l_field, 2,
		halt,

		// Load field 3
		l_local, 0,
		l_field, 3,
		halt,

		// Load field 4
		l_local, 0,
		l_field, 4,
		halt,
	};

	vm_.set_program(p);
	vm_.execute();

	ASSERT_EQ(77, vm_.eval_stack_top().i());

	vm_.execute();

	ASSERT_FLOAT_EQ(12.0f, vm_.eval_stack_top().f());

	vm_.execute();

	ASSERT_EQ(100, vm_.eval_stack_top().i());

	vm_.execute();

	ASSERT_FLOAT_EQ(99.0f, vm_.eval_stack_top().f());

	vm_.execute();

	ASSERT_EQ(-1829, vm_.eval_stack_top().i());
}

TEST_F(StructTest, STRUCT_FIELD_STORE_LOAD)
{
	std::vector<int> p =
	{
		new_struct, 1,
		s_local, 0,

		// Store 77 in field 0 (int)
		l_local, 0,
		halt,
		iconst, 77,
		s_field, 0,

		new_struct, 4,
		s_local, 1,

		// Store a pointer to the fist struct in field 0 of the second struct
		l_local, 1,
		halt,
		l_local, 0,
		halt,
		s_field, 0,

		l_local, 1,
		halt,
		l_field, 0,
		halt,
		l_field, 0,
	};

	vm_.set_program(p);

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ(77, vm_.eval_stack_top().i());
}
<commit_msg>wrote some more struct pointer tests<commit_after>#include <gtest\gtest.h>

#include "vm.h"

using namespace elsa::vm;

class StructTest : public testing::Test {
protected:
	virtual void SetUp()
	{
		int ep = 0;
		vm_.add_constant_entry(new FunctionInfo("main", 0, 3, ep, FunctionType::Static));
		vm_.set_entry_point(ep);

		auto si = new StructInfo("my_struct");
		si->add_field(new FieldInfo("field0", OType::Int));
		si->add_field(new FieldInfo("field1", OType::Float));
		si->add_field(new FieldInfo("field2", OType::Int));
		si->add_field(new FieldInfo("field3", OType::Float));
		si->add_field(new FieldInfo("field4", OType::Int));
		vm_.add_constant_entry(si);

		vm_.add_constant_entry(new FloatEntry(12.0f));
		vm_.add_constant_entry(new FloatEntry(99.0f));

		auto si2 = new StructInfo("my_struct2");
		si2->add_field(new FieldInfo("field0", OType::GCOPtr));
		si2->add_field(new FieldInfo("field1", OType::Int));
		si2->add_field(new FieldInfo("field2", OType::GCOPtr));

		vm_.add_constant_entry(si2);
	}

	virtual void TearDown() {}

	VM vm_;
};

TEST_F(StructTest, NEW)
{
	std::vector<int> p =
	{
		new_struct, 1
	};

	vm_.set_program(p);
	vm_.execute();

	auto obj = vm_.eval_stack_top();

	ASSERT_EQ(OType::GCOPtr, obj.get_type());

	auto si = obj.gco()->si;

	ASSERT_EQ("my_struct", si->get_name());
	ASSERT_EQ(20, si->get_size());
}

TEST_F(StructTest, FIELD_STORE_LOAD)
{
	std::vector<int> p =
	{
		new_struct, 1,
		s_local, 0,

		// Store 77 in field 0 (int)
		l_local, 0,
		iconst, 77,
		s_field, 0,

		// Store 12.0 in field 1 (float)
		l_local, 0,
		fconst, 2,
		s_field, 1,

		// Store 100 in field 2 (int)
		l_local, 0,
		iconst, 100,
		s_field, 2,

		// Store 99.0 in field 3 (float)
		l_local, 0,
		fconst, 3,
		s_field, 3,

		// Store -1829 in field 4 (int)
		l_local, 0,
		iconst, -1829,
		s_field, 4,

		// Load field 0
		l_local, 0,
		l_field, 0,
		halt,

		// Load field 1
		l_local, 0,
		l_field, 1,
		halt,

		// Load field 2
		l_local, 0,
		l_field, 2,
		halt,

		// Load field 3
		l_local, 0,
		l_field, 3,
		halt,

		// Load field 4
		l_local, 0,
		l_field, 4,
		halt,
	};

	vm_.set_program(p);
	vm_.execute();

	ASSERT_EQ(77, vm_.eval_stack_top().i());

	vm_.execute();

	ASSERT_FLOAT_EQ(12.0f, vm_.eval_stack_top().f());

	vm_.execute();

	ASSERT_EQ(100, vm_.eval_stack_top().i());

	vm_.execute();

	ASSERT_FLOAT_EQ(99.0f, vm_.eval_stack_top().f());

	vm_.execute();

	ASSERT_EQ(-1829, vm_.eval_stack_top().i());
}

TEST_F(StructTest, STRUCT_FIELD_STORE_LOAD)
{
	std::vector<int> p =
	{
		new_struct, 1,
		s_local, 0,

		// Store 77 in field 0 (int)
		l_local, 0,
		halt,
		iconst, 77,
		s_field, 0,

		new_struct, 4,
		s_local, 1,

		// Store a pointer to the fist struct in field 0 of the second struct
		l_local, 1,
		halt,
		l_local, 0,
		halt,
		s_field, 0,

		l_local, 1,
		halt,
		l_field, 0,
		halt,
		l_field, 0,
	};

	vm_.set_program(p);

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ(77, vm_.eval_stack_top().i());
}

TEST_F(StructTest, STRUCT_ON_STRUCT_FIELD_STORE_LOAD)
{
	std::vector<int> p =
	{
		new_struct, 1,
		s_local, 0,

		l_local, 0,
		iconst, 77,
		s_field, 0,

		l_local, 0,
		fconst, 2,
		s_field, 1,

		l_local, 0,
		iconst, 100,
		s_field, 2,

		l_local, 0,
		fconst, 3,
		s_field, 3,

		l_local, 0,
		iconst, -1829,
		s_field, 4,

		new_struct, 4,
		s_local, 1,

		l_local, 1,
		l_local, 0,
		halt,
		s_field, 0,

		l_local, 1,
		iconst, 12378,
		s_field, 1,

		new_struct, 4,
		halt,
		s_local, 2,

		l_local, 2,
		l_local, 0,
		s_field, 0,

		l_local, 1,
		l_local, 2,
		s_field, 2,

		l_local, 1,
		l_field, 1,
		halt,

		l_local, 1,
		l_field, 2,
		halt,

		// Load 77(field 0) from a pointer to struct 0
		l_local, 1,
		l_field, 2,
		l_field, 0,
		l_field, 0,
		halt,

		// Load 12.0f(field 1) from a pointer to struct 0
		l_local, 1,
		l_field, 2,
		l_field, 0,
		l_field, 1,
		halt,

		// Load 100(field 2) from a pointer to struct 0
		l_local, 1,
		l_field, 2,
		l_field, 0,
		l_field, 2,
		halt,

		// Load 99.0f(field 3) from a pointer to struct 0
		l_local, 1,
		l_field, 2,
		l_field, 0,
		l_field, 3,
		halt,

		// Load -1829(field 4) from a pointer to struct 0
		l_local, 1,
		l_field, 2,
		l_field, 0,
		l_field, 4,
		halt,
	};

	vm_.set_program(p);

	vm_.execute();
	ASSERT_EQ("my_struct", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ(12378, vm_.eval_stack_top().i());

	vm_.execute();
	ASSERT_EQ("my_struct2", vm_.eval_stack_top().gco()->si->get_name());

	vm_.execute();
	ASSERT_EQ(77, vm_.eval_stack_top().i());

	vm_.execute();
	ASSERT_FLOAT_EQ(12.0f, vm_.eval_stack_top().f());

	vm_.execute();
	ASSERT_EQ(100, vm_.eval_stack_top().i());

	vm_.execute();
	ASSERT_FLOAT_EQ(99.0f, vm_.eval_stack_top().f());

	vm_.execute();
	ASSERT_EQ(-1829, vm_.eval_stack_top().i());
}
<|endoftext|>


<commit_before>/*===- Integrator.cpp - libSimulation -=========================================
*
*                                  DEMON
*
* This file is distributed under the BSD Open Source License. See LICENSE.TXT
* for details.
*
*===-----------------------------------------------------------------------===*/

#include "Integrator.h"
#include "CacheOperator.h"
#include <cmath>
#include <limits>

Integrator::Integrator(Cloud * const C, const ForceArray &FA,
                       const double timeStep, double startTime)
: currentTime(startTime), cloud(C), forces(FA), init_dt(timeStep),
operations({{new CacheOperator(C)}})
SEMAPHORES_MALLOC(1) {
    SEMAPHORES_INIT(1);
}

Integrator::~Integrator() {
	for (Operator * const opt : operations)
		delete opt;

    SEMAPHORES_FREE(1);
}

// If particle spacing is less than the specified distance reduce timestep by a
// factor of 10 and recheck with disance reduced by a factor of 10. Once all
// particle spacings are outside the specified distance use the current
// timestep. This allows fine grain control of reduced timesteps.
const double Integrator::modifyTimeStep(float currentDist, double currentTimeStep) const {
	// set constants:
	const cloud_index numPar = cloud->n;
	const float redFactor = 10.0f;

#ifdef DISPATCH_QUEUES
    // You cannot block capture method arguments. Store these values in to non
    // const block captured variables. The correct names supsequently used by
    // BLOCK_VALUE_DIST and BLOCK_VALUE_TIME
    __block float currDist = currentDist;
    __block double currTimeStep = currentTimeStep;
#endif

	// Loop through entire cloud, or until reduction occures. Reset innerIndex
    // after each loop iteration.
#ifdef DISPATCH_QUEUES
	const cloud_index outerLoop = numPar;
#else
	const cloud_index outerLoop = numPar - 1;
#endif
    BEGIN_PARALLEL_FOR(outerIndex, e, outerLoop, FLOAT_STRIDE, dynamic)
		// caculate separation distance b/t adjacent elements:
		const floatV outPosX = loadFloatVector(cloud->x + outerIndex);
		const floatV outPosY = loadFloatVector(cloud->y + outerIndex);

        // seperation (a1 - a2, a3 - a4, a1 - a3, a2 - a4)
        floatV sepx = _mm_hsub_ps(outPosX, _mm_shuffle_ps(outPosX, outPosX, _MM_SHUFFLE(1, 2, 0, 3)));
        floatV sepy = _mm_hsub_ps(outPosY, _mm_shuffle_ps(outPosY, outPosY, _MM_SHUFFLE(1, 2, 0, 3)));

		// If particles are too close, reduce time step:
        while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
            // Only one thread should modify the distance and timesStep at a time.
            SEMAPHORE_WAIT(0)
            if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
                BLOCK_VALUE_DIST /= redFactor;
                BLOCK_VALUE_TIME /= redFactor;
            }
            SEMAPHORE_SIGNAL(0)
        }

        // seperation (a1 - a2, a3 - a4, a1 - a4, a2 - a3) The lower half
        // operation is a repeat of the lower half of the above.
        sepx = _mm_hsub_ps(outPosX, _mm_shuffle_ps(outPosX, outPosX, _MM_SHUFFLE(1, 3, 0, 2)));
        sepy = _mm_hsub_ps(outPosY, _mm_shuffle_ps(outPosY, outPosY, _MM_SHUFFLE(1, 3, 0, 2)));

		// If particles are too close, reduce time step:
		while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
			// Only one thread should modify the distance and timesStep at a time.
			SEMAPHORE_WAIT(0)
			if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				BLOCK_VALUE_DIST /= redFactor;
				BLOCK_VALUE_TIME /= redFactor;
			}
			SEMAPHORE_SIGNAL(0)
		}

		// Calculate separation distance b/t nonadjacent elements:
		for (cloud_index innerIndex = outerIndex + FLOAT_STRIDE; innerIndex < numPar; innerIndex += FLOAT_STRIDE) {
			const floatV inPosX = loadFloatVector(cloud->x + innerIndex);
			const floatV inPosY = loadFloatVector(cloud->y + innerIndex);

			sepx = outPosX - inPosX;
			sepy = outPosY - inPosY;

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(0, 1, 2, 3));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(0, 1, 2, 3));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(1, 0, 3, 2));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(1, 0, 3, 2));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(2, 3, 0, 1));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(2, 3, 0, 1));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}
		}
	END_PARALLEL_FOR

    return BLOCK_VALUE_TIME;
}

inline floatV Integrator::loadFloatVector(double * const x) {
#ifdef __AVX__
    return _mm256_set_ps((float)x[0], (float)x[1], (float)x[2], (float)x[3],
                         (float)x[4], (float)x[5], (float)x[6], (float)x[7]);
#else
    return _mm_set_ps((float)x[0], (float)x[1], (float)x[2], (float)x[3]);
#endif
}

inline bool Integrator::isWithInDistance(const floatV a, const floatV b, const float dist) {
	return (bool)movemask_ps(cmple_ps(sqrt_ps(add_ps(mul_ps(a, a), mul_ps(b, b))), set1_ps(dist)));
}
<commit_msg>Add comments to explain which comparison is which.<commit_after>/*===- Integrator.cpp - libSimulation -=========================================
*
*                                  DEMON
*
* This file is distributed under the BSD Open Source License. See LICENSE.TXT
* for details.
*
*===-----------------------------------------------------------------------===*/

#include "Integrator.h"
#include "CacheOperator.h"
#include <cmath>
#include <limits>

Integrator::Integrator(Cloud * const C, const ForceArray &FA,
                       const double timeStep, double startTime)
: currentTime(startTime), cloud(C), forces(FA), init_dt(timeStep),
operations({{new CacheOperator(C)}})
SEMAPHORES_MALLOC(1) {
    SEMAPHORES_INIT(1);
}

Integrator::~Integrator() {
	for (Operator * const opt : operations)
		delete opt;

    SEMAPHORES_FREE(1);
}

// If particle spacing is less than the specified distance reduce timestep by a
// factor of 10 and recheck with disance reduced by a factor of 10. Once all
// particle spacings are outside the specified distance use the current
// timestep. This allows fine grain control of reduced timesteps.
const double Integrator::modifyTimeStep(float currentDist, double currentTimeStep) const {
	// set constants:
	const cloud_index numPar = cloud->n;
	const float redFactor = 10.0f;

#ifdef DISPATCH_QUEUES
    // You cannot block capture method arguments. Store these values in to non
    // const block captured variables. The correct names supsequently used by
    // BLOCK_VALUE_DIST and BLOCK_VALUE_TIME
    __block float currDist = currentDist;
    __block double currTimeStep = currentTimeStep;
#endif

	// Loop through entire cloud, or until reduction occures. Reset innerIndex
    // after each loop iteration.
#ifdef DISPATCH_QUEUES
	const cloud_index outerLoop = numPar;
#else
	const cloud_index outerLoop = numPar - 1;
#endif
    BEGIN_PARALLEL_FOR(outerIndex, e, outerLoop, FLOAT_STRIDE, dynamic)
		// caculate separation distance b/t adjacent elements:
		const floatV outPosX = loadFloatVector(cloud->x + outerIndex);
		const floatV outPosY = loadFloatVector(cloud->y + outerIndex);

        // seperation (a1 - a2, a3 - a4, a1 - a3, a2 - a4)
        floatV sepx = _mm_hsub_ps(outPosX, _mm_shuffle_ps(outPosX, outPosX, _MM_SHUFFLE(1, 2, 0, 3)));
        floatV sepy = _mm_hsub_ps(outPosY, _mm_shuffle_ps(outPosY, outPosY, _MM_SHUFFLE(1, 2, 0, 3)));

		// If particles are too close, reduce time step:
        while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
            // Only one thread should modify the distance and timesStep at a time.
            SEMAPHORE_WAIT(0)
            if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
                BLOCK_VALUE_DIST /= redFactor;
                BLOCK_VALUE_TIME /= redFactor;
            }
            SEMAPHORE_SIGNAL(0)
        }

        // seperation (a1 - a2, a3 - a4, a1 - a4, a2 - a3) The lower half
        // operation is a repeat of the lower half of the above.
        sepx = _mm_hsub_ps(outPosX, _mm_shuffle_ps(outPosX, outPosX, _MM_SHUFFLE(1, 3, 0, 2)));
        sepy = _mm_hsub_ps(outPosY, _mm_shuffle_ps(outPosY, outPosY, _MM_SHUFFLE(1, 3, 0, 2)));

		// If particles are too close, reduce time step:
		while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
			// Only one thread should modify the distance and timesStep at a time.
			SEMAPHORE_WAIT(0)
			if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				BLOCK_VALUE_DIST /= redFactor;
				BLOCK_VALUE_TIME /= redFactor;
			}
			SEMAPHORE_SIGNAL(0)
		}

		// Calculate separation distance b/t nonadjacent elements:
		for (cloud_index innerIndex = outerIndex + FLOAT_STRIDE; innerIndex < numPar; innerIndex += FLOAT_STRIDE) {
			const floatV inPosX = loadFloatVector(cloud->x + innerIndex);
			const floatV inPosY = loadFloatVector(cloud->y + innerIndex);

            // seperation (a1 - b1, a2 - b2, a3 - b3, a4 - b4)
			sepx = outPosX - inPosX;
			sepy = outPosY - inPosY;

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

            // seperation (a1 - b2, a2 - b3, a3 - b4, a4 - b5)
			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(3, 2, 1, 0));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(3, 2, 1, 0));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

            // seperation (a1 - b3, a2 - b4, a3 - b1, a4 - b2)
			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(0, 3, 2, 1));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(0, 3, 2, 1));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}

            // seperation (a1 - b4, a2 - b1, a3 - b2, a4 - b3)
			sepx = outPosX - _mm_shuffle_ps(inPosX, inPosX, _MM_SHUFFLE(1, 0, 3, 2));
			sepy = outPosY - _mm_shuffle_ps(inPosY, inPosY, _MM_SHUFFLE(1, 0, 3, 2));

			// If particles are too close, reduce time step:
			while (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
				// Only one thread should modify the distance and timesStep at a time.
				SEMAPHORE_WAIT(0)
				if (isWithInDistance(sepx, sepy, BLOCK_VALUE_DIST)) {
					BLOCK_VALUE_DIST /= redFactor;
					BLOCK_VALUE_TIME /= redFactor;
				}
				SEMAPHORE_SIGNAL(0)
			}
		}
	END_PARALLEL_FOR

    return BLOCK_VALUE_TIME;
}

inline floatV Integrator::loadFloatVector(double * const x) {
#ifdef __AVX__
    return _mm256_set_ps((float)x[0], (float)x[1], (float)x[2], (float)x[3],
                         (float)x[4], (float)x[5], (float)x[6], (float)x[7]);
#else
    return _mm_set_ps((float)x[0], (float)x[1], (float)x[2], (float)x[3]);
#endif
}

inline bool Integrator::isWithInDistance(const floatV a, const floatV b, const float dist) {
	return (bool)movemask_ps(cmple_ps(sqrt_ps(add_ps(mul_ps(a, a), mul_ps(b, b))), set1_ps(dist)));
}
<|endoftext|>


<commit_before>/*
    Copyright (c) 2013 Timothy Reaves treaves@silverfieldstech.com

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

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

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
  */
#include "priv/classhandlermanager.h"

#include "classhandler.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaType>
#include <QtCore/QtPlugin>
#include <QtCore/QPluginLoader>
#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtCore/QVariant>

#include "httpserverrequest.h"
#include "headers.h"

namespace Tufao {

// Initialize static members.

/* \warning the variable is never fully initialized.
 * `QCoreApplication::applicationDirPath()` (aka the install location), which
 * can only be retrieved at runtime, after the QCoreApplication was constructed,
 * is never present in this list. */
QStringList ClassHandlerManager::pluginLocations = []() {
    QStringList ret;

    // Add standard locations to pluginLocations
    // First, the typical app config dir's
#ifdef Q_OS_WIN
    // Code can be added here, but, for the time being, I'm leaving it empty.
#elif defined(Q_OS_MAC)
    ret.append(QDir::homePath() + "/Library/Application Support/Tufao");
    ret.append("/Library/Application Support/Tufao");
#else
    ret.append(QDir::homePath() + "/.tufao");
#endif

    // The standard library locations
    for (const QString &libraryPath: QCoreApplication::libraryPaths()) {
        QDir testDir(libraryPath + QDir::separator() + "Tufao");
        if(testDir.exists()){
            ret.append(testDir.absolutePath());
        }
    }

    return ret;
}();

/* ************************************************************************** */
/* Object lifecycle                                                           */
/* ************************************************************************** */
ClassHandlerManager::ClassHandlerManager(const QString &pluginID,
                                         const QString &urlNamespace,
                                         QObject * parent) :
    QObject(parent),
    priv{new Priv{pluginID, urlNamespace}}
{
    // Now load the plugin of interest
    // First list all static plugins.
    foreach (QObject * pluginInterface, QPluginLoader::staticInstances()){
        ClassHandler * plugin = qobject_cast<ClassHandler *>(pluginInterface);
        if (plugin){
            registerHandler(plugin);
        }
    }
///
    // Then list dynamic libraries from the plugins/ directory
    QStringList contents;
    // retrieve a list of all dynamic libraries from the search paths
    {
        auto retrieveDynlib = [&contents](const QString &path) {
            QFileInfo thisPath(QDir(path).filePath("plugins"));
            if (thisPath.isDir()) {
                QDir thisDir(thisPath.absoluteFilePath());
                qDebug() << "Search " << thisPath.absolutePath()
                         << " for plugins.";
                for (const QString &entry: thisDir.entryList()) {
                    if (QLibrary::isLibrary(entry))
                        contents.append(thisDir.filePath(entry));
                }
            }
        };

        for (const QString &path: pluginLocations)
            retrieveDynlib(path);

        QFileInfo installDir(QCoreApplication::applicationDirPath());
        if (installDir.isDir())
            retrieveDynlib(installDir.absolutePath());
    }

    // Check each dynamic library to see if it is a plugin
    foreach (QString pluginPath, contents) {
        QPluginLoader loader(pluginPath);
        // If we were constructed with a pluginID, we need to chech each plugin.
        if(pluginID.isEmpty() || pluginID == loader.metaData().value("IID").toString()) {
            if (!loader.load()) {
                qWarning() << "Couldn't load the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            QObject* obj = loader.instance();
            if (!obj) {
                qWarning() << "Couldn't open the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            ClassHandler * plugin = qobject_cast<ClassHandler *>(obj);
            if (plugin) {
                if (plugin){
                    registerHandler(plugin);
                }
            }
        }

    }
}

ClassHandlerManager::~ClassHandlerManager()
{
    foreach (ClassHandlerManager::PluginDescriptor *descriptor, priv->handlers) {
        delete descriptor;
    }
    delete priv;
}

/* ************************************************************************** */
/* Accessors & mutators                                                       */
/* ************************************************************************** */
QString ClassHandlerManager::urlNamespace() const
{
    return priv->urlNamespace;
}

/* ************************************************************************** */
/* Static Methods                                                             */
/* ************************************************************************** */
void ClassHandlerManager::addPluginLocation(const QString location)
{
    if(!pluginLocations.contains(location)){
        pluginLocations.append(location);
    }
}

/* ************************************************************************** */
/* Private Methods                                                            */
/* ************************************************************************** */
void ClassHandlerManager::dispatchVoidMethod(QMetaMethod method,
                                             ClassHandler * handler,
                                             const QGenericArgument * args) const
{
    method.invoke(handler,
                  Qt::DirectConnection,
                  args[0],
                  args[1],
                  args[2],
                  args[3],
                  args[4],
                  args[5],
                  args[6],
                  args[7],
                  args[8],
                  args[9]
                  );
}

void ClassHandlerManager::dispatchJSONMethod(HttpServerResponse & response,
                                             QMetaMethod method,
                                             ClassHandler *handler,
                                             const QGenericArgument *args) const
{
    QJsonObject result;
    bool wasInvoked = method.invoke(handler,
                                    Qt::DirectConnection,
                                    Q_RETURN_ARG(QJsonObject, result),
                                    args[0],
                                    args[1],
                                    args[2],
                                    args[3],
                                    args[4],
                                    args[5],
                                    args[6],
                                    args[7],
                                    args[8],
                                    args[9]
                                    );
    if(wasInvoked) {
        HttpResponseStatus status = HttpResponseStatus::OK;
        QJsonDocument jsonDocument;
        if(result.contains(ClassHandler::HttpResponseStatusKey)) {
            status = HttpResponseStatus(result[ClassHandler::HttpResponseStatusKey].toInt());
            //The response will either be an JsonObject, or a JsonArray
            if(result[ClassHandler::JsonResponseKey].isArray()) {
                jsonDocument.setArray(result[ClassHandler::JsonResponseKey].toArray());
            } else {
                jsonDocument = QJsonDocument(result[ClassHandler::JsonResponseKey].toObject());
            }
        }
        response.writeHead(status);
        response.headers().replace("Content-Type", "application/json");
        response.end(jsonDocument.toJson());
    }
}

bool ClassHandlerManager::processRequest(HttpServerRequest & request,
                                         HttpServerResponse & response,
                                         const QString className,
                                         const QString methodName,
                                         const QHash<QString, QString> arguments)
{
    bool handled = false;
    bool canHandle = true;
    int methodIndex = selectMethod(className, methodName, arguments);
    if(methodIndex > -1) {
        ClassHandlerManager::PluginDescriptor *handler
            = priv->handlers[className];
        QMetaMethod method = handler->handler->metaObject()->method(methodIndex);

        // Create the arguments
        QGenericArgument argumentTable[10];
        argumentTable[0] = Q_ARG(Tufao::HttpServerRequest, request);
        argumentTable[1] = Q_ARG(Tufao::HttpServerResponse, response);

        // We need this to keep objects in scope until the actual invoke() call.
        QVariant variants[10];

        int argumentIndex = 2;
        while(argumentIndex < method.parameterCount()){
            QString parameterName = method.parameterNames()[argumentIndex];
            // qDebug() << "Processing " << parameterName;
            variants[argumentIndex] = QVariant::fromValue(arguments.value(parameterName));
            int methodType = method.parameterType(argumentIndex);
            if(variants[argumentIndex].canConvert(methodType)) {
                variants[argumentIndex].convert(methodType);
                argumentTable[argumentIndex] = QGenericArgument(variants[argumentIndex].typeName(),
                                                                variants[argumentIndex].data());
                // qDebug() << "Converted "
                //          << arguments.value(parameterName)
                //          << " to type "
                //          << QVariant::typeToName(methodType)
                //          << " index "
                //          << argumentIndex;
            } else {
                qWarning() << "Can not convert "
                           << arguments.value(parameterName)
                           << " to type " << QVariant::typeToName(methodType);
            }
            argumentIndex+=1;
        }
        if(canHandle) {
            if(method.returnType() == QMetaType::QJsonObject) {
                this->dispatchJSONMethod(response, method, handler->handler, argumentTable);
            } else {
                this->dispatchVoidMethod(method, handler->handler, argumentTable);
            }
            handled = true;
        }
    } else {
        qWarning() << "Cound not find a method named with a matching signature.";
    }

    return handled;
}

void ClassHandlerManager::registerHandler(ClassHandler * handler)
{
    // Only process plugins that have not already been registered.
    if (!priv->handlers.contains(handler->objectName())){
        qDebug() << "Registering " << handler->objectName() << " as a handler.";
        bool canDispathTo = false;
        const QMetaObject* metaObject = handler->metaObject();
        for(int methodIndex = metaObject->methodOffset(); methodIndex < metaObject->methodCount(); ++methodIndex) {
            QMetaMethod method = metaObject->method(methodIndex);
            // We only want public slots whos first two arguements are request & response
            if(method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
                QList<QByteArray> parameterNames = method.parameterNames();
                if(parameterNames[0] == QByteArray("request") && parameterNames[1] == QByteArray("response")) {
                    canDispathTo = true;
                    ClassHandlerManager::PluginDescriptor *pluginDescriptor
                        = priv->handlers[handler->objectName()];
                    if(pluginDescriptor == NULL) {
                        pluginDescriptor = new ClassHandlerManager::PluginDescriptor();
                        priv->handlers[handler->objectName()]
                            = pluginDescriptor;
                    }
                    pluginDescriptor->className = handler->objectName();
                    pluginDescriptor->handler = handler;

                    uint parameterHash = qHash(QString::fromLatin1(method.name()));
                    foreach (QByteArray nameBytes, parameterNames) {
                        parameterHash += qHash(QString::fromLatin1(nameBytes));
                    }
                    pluginDescriptor->methods.insert(parameterHash, methodIndex);
                    pluginDescriptor->methodNames.append(QString::fromLatin1(method.name()));

                    QString signature = QString::fromLatin1(method.methodSignature());
                    qDebug() << signature << " is a dispatchable endpoint.";
                }
            }
        }
        if(canDispathTo) {
            handler->init();
        }
    }

}

int ClassHandlerManager::selectMethod(const QString className,
                                      const QString methodName,
                                      const QHash<QString, QString> arguments) const
{
    int methodIndex = -1;
    uint parameterHash = qHash(methodName);
    parameterHash += qHash(QString("request"));
    parameterHash += qHash(QString("response"));
    foreach (QString key, arguments.keys()) {
        parameterHash += qHash(key);
    }
    PluginDescriptor *pluginDescriptor = priv->handlers[className];
    if (pluginDescriptor->methods.contains(parameterHash)) {
        methodIndex = pluginDescriptor->methods.value(parameterHash);
    }
    return methodIndex;
}

/* ************************************************************************** */
/* Override Tufao::AbstractHttpServerRequestHandler Methods                   */
/* ************************************************************************** */
bool ClassHandlerManager::handleRequest(Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response)
{
    /* Apply urlNamespace and resume request dispatching or abort if request has
       a different urlNamespace */
    const QUrl originalUrl = request.url();
    const QString originalPath = originalUrl.path();

    if (!priv->urlNamespace.isEmpty()) {
        if (!(originalPath.size() > priv->urlNamespace.size()
              && originalPath.startsWith(priv->urlNamespace)
              && originalPath[priv->urlNamespace.size()] == '/')) {
            return false;
        }
    }

    const QString namespacedPath = [&originalPath,this]() {
        return originalPath.mid(priv->urlNamespace.size());
    }();

    /* The user MUST NOT view the original url. This design ease the
       implementation of nested handlers.

       request.setUrl(namespacedUrl) MUST be called before pass the request to
       the user and the previous url (originalUrl) MUST be restored if the
       dispatch failed and we'll return the request to HttpServerRequestRouter.
     */
    const QUrl namespacedUrl = [&originalUrl,&namespacedPath]() {
        QUrl ret = originalUrl;
        ret.setPath(namespacedPath);
        return ret;
    }();

    QStringList pathComponents = namespacedPath.split("/", QString::SkipEmptyParts);

    // There must be at least two path components (class & method)
    const int minimumPathComponents = 2;
    if (pathComponents.length() < minimumPathComponents) {
        qWarning() << "Request was dispatched to handler, but too few path"
            " components found.  The path components are"
                   << pathComponents;
        return false;
    }

    if (pathComponents.length() > minimumPathComponents  + 16) {
        // We also can not have too many arguments; 16 is max, as that is 8
        // argumetns plus request & response
        qWarning() << "Request was dispatched to handler, but too many path"
            " components found.  The path components are"
                   << pathComponents;
        return false;
    }

    int pathIndex = 0;
    QString className = pathComponents[pathIndex++];
    QString methodName = pathComponents[pathIndex++];
    // We need to have an even number of path components left
    if ((pathComponents.length() - pathIndex) % 2 != 0) {
        qWarning() << "Can not dispath as an odd number of parameter components"
            " were supplied.";
        return false;
    }

    // See if we have a class handler with a matching method
    if (!(priv->handlers.contains(className)
          && priv->handlers[className]->methodNames.contains(methodName))) {
        if (priv->handlers.contains(className)) {
            qWarning() << "The class" << className << "has no method named"
                       << methodName;
        }
    }

    // Convert the remaining path components into an argument hash
    QHash<QString, QString> arguments;
    while(pathIndex < pathComponents.length()){
        arguments[pathComponents[pathIndex]] = pathComponents[pathIndex + 1];
        pathIndex += 2;
    }

    request.setUrl(namespacedUrl);
    if (!processRequest(request, response, className, methodName, arguments)) {
        request.setUrl(originalUrl);
        return false;
    }

    return true;
}

} // namespace Tufao
<commit_msg>Protecting access to pluginLocations global variable through mutex.<commit_after>/*
    Copyright (c) 2013 Timothy Reaves treaves@silverfieldstech.com

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

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

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
  */
#include "priv/classhandlermanager.h"

#include "classhandler.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaType>
#include <QtCore/QtPlugin>
#include <QtCore/QPluginLoader>
#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtCore/QVariant>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>

#include "httpserverrequest.h"
#include "headers.h"

namespace Tufao {

// Initialize static members.

/* \warning the variable is never fully initialized.
 * `QCoreApplication::applicationDirPath()` (aka the install location), which
 * can only be retrieved at runtime, after the QCoreApplication was constructed,
 * is never present in this list. */
QStringList ClassHandlerManager::pluginLocations = []() {
    QStringList ret;

    // Add standard locations to pluginLocations
    // First, the typical app config dir's
#ifdef Q_OS_WIN
    // Code can be added here, but, for the time being, I'm leaving it empty.
#elif defined(Q_OS_MAC)
    ret.append(QDir::homePath() + "/Library/Application Support/Tufao");
    ret.append("/Library/Application Support/Tufao");
#else
    ret.append(QDir::homePath() + "/.tufao");
#endif

    // The standard library locations
    for (const QString &libraryPath: QCoreApplication::libraryPaths()) {
        QDir testDir(libraryPath + QDir::separator() + "Tufao");
        if(testDir.exists()){
            ret.append(testDir.absolutePath());
        }
    }

    return ret;
}();
QMutex pluginLocationsMutex;

/* ************************************************************************** */
/* Object lifecycle                                                           */
/* ************************************************************************** */
ClassHandlerManager::ClassHandlerManager(const QString &pluginID,
                                         const QString &urlNamespace,
                                         QObject * parent) :
    QObject(parent),
    priv{new Priv{pluginID, urlNamespace}}
{
    // Now load the plugin of interest
    // First list all static plugins.
    foreach (QObject * pluginInterface, QPluginLoader::staticInstances()){
        ClassHandler * plugin = qobject_cast<ClassHandler *>(pluginInterface);
        if (plugin){
            registerHandler(plugin);
        }
    }
///
    // Then list dynamic libraries from the plugins/ directory
    QStringList contents;
    // retrieve a list of all dynamic libraries from the search paths
    {
        QMutexLocker guard(&pluginLocationsMutex);

        auto retrieveDynlib = [&contents](const QString &path) {
            QFileInfo thisPath(QDir(path).filePath("plugins"));
            if (thisPath.isDir()) {
                QDir thisDir(thisPath.absoluteFilePath());
                qDebug() << "Search " << thisPath.absolutePath()
                         << " for plugins.";
                for (const QString &entry: thisDir.entryList()) {
                    if (QLibrary::isLibrary(entry))
                        contents.append(thisDir.filePath(entry));
                }
            }
        };

        for (const QString &path: pluginLocations)
            retrieveDynlib(path);

        QFileInfo installDir(QCoreApplication::applicationDirPath());
        if (installDir.isDir())
            retrieveDynlib(installDir.absolutePath());
    }

    // Check each dynamic library to see if it is a plugin
    foreach (QString pluginPath, contents) {
        QPluginLoader loader(pluginPath);
        // If we were constructed with a pluginID, we need to chech each plugin.
        if(pluginID.isEmpty() || pluginID == loader.metaData().value("IID").toString()) {
            if (!loader.load()) {
                qWarning() << "Couldn't load the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            QObject* obj = loader.instance();
            if (!obj) {
                qWarning() << "Couldn't open the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            ClassHandler * plugin = qobject_cast<ClassHandler *>(obj);
            if (plugin) {
                if (plugin){
                    registerHandler(plugin);
                }
            }
        }

    }
}

ClassHandlerManager::~ClassHandlerManager()
{
    foreach (ClassHandlerManager::PluginDescriptor *descriptor, priv->handlers) {
        delete descriptor;
    }
    delete priv;
}

/* ************************************************************************** */
/* Accessors & mutators                                                       */
/* ************************************************************************** */
QString ClassHandlerManager::urlNamespace() const
{
    return priv->urlNamespace;
}

/* ************************************************************************** */
/* Static Methods                                                             */
/* ************************************************************************** */
void ClassHandlerManager::addPluginLocation(const QString location)
{
    QMutexLocker guard(&pluginLocationsMutex);

    if(!pluginLocations.contains(location)){
        pluginLocations.append(location);
    }
}

/* ************************************************************************** */
/* Private Methods                                                            */
/* ************************************************************************** */
void ClassHandlerManager::dispatchVoidMethod(QMetaMethod method,
                                             ClassHandler * handler,
                                             const QGenericArgument * args) const
{
    method.invoke(handler,
                  Qt::DirectConnection,
                  args[0],
                  args[1],
                  args[2],
                  args[3],
                  args[4],
                  args[5],
                  args[6],
                  args[7],
                  args[8],
                  args[9]
                  );
}

void ClassHandlerManager::dispatchJSONMethod(HttpServerResponse & response,
                                             QMetaMethod method,
                                             ClassHandler *handler,
                                             const QGenericArgument *args) const
{
    QJsonObject result;
    bool wasInvoked = method.invoke(handler,
                                    Qt::DirectConnection,
                                    Q_RETURN_ARG(QJsonObject, result),
                                    args[0],
                                    args[1],
                                    args[2],
                                    args[3],
                                    args[4],
                                    args[5],
                                    args[6],
                                    args[7],
                                    args[8],
                                    args[9]
                                    );
    if(wasInvoked) {
        HttpResponseStatus status = HttpResponseStatus::OK;
        QJsonDocument jsonDocument;
        if(result.contains(ClassHandler::HttpResponseStatusKey)) {
            status = HttpResponseStatus(result[ClassHandler::HttpResponseStatusKey].toInt());
            //The response will either be an JsonObject, or a JsonArray
            if(result[ClassHandler::JsonResponseKey].isArray()) {
                jsonDocument.setArray(result[ClassHandler::JsonResponseKey].toArray());
            } else {
                jsonDocument = QJsonDocument(result[ClassHandler::JsonResponseKey].toObject());
            }
        }
        response.writeHead(status);
        response.headers().replace("Content-Type", "application/json");
        response.end(jsonDocument.toJson());
    }
}

bool ClassHandlerManager::processRequest(HttpServerRequest & request,
                                         HttpServerResponse & response,
                                         const QString className,
                                         const QString methodName,
                                         const QHash<QString, QString> arguments)
{
    bool handled = false;
    bool canHandle = true;
    int methodIndex = selectMethod(className, methodName, arguments);
    if(methodIndex > -1) {
        ClassHandlerManager::PluginDescriptor *handler
            = priv->handlers[className];
        QMetaMethod method = handler->handler->metaObject()->method(methodIndex);

        // Create the arguments
        QGenericArgument argumentTable[10];
        argumentTable[0] = Q_ARG(Tufao::HttpServerRequest, request);
        argumentTable[1] = Q_ARG(Tufao::HttpServerResponse, response);

        // We need this to keep objects in scope until the actual invoke() call.
        QVariant variants[10];

        int argumentIndex = 2;
        while(argumentIndex < method.parameterCount()){
            QString parameterName = method.parameterNames()[argumentIndex];
            // qDebug() << "Processing " << parameterName;
            variants[argumentIndex] = QVariant::fromValue(arguments.value(parameterName));
            int methodType = method.parameterType(argumentIndex);
            if(variants[argumentIndex].canConvert(methodType)) {
                variants[argumentIndex].convert(methodType);
                argumentTable[argumentIndex] = QGenericArgument(variants[argumentIndex].typeName(),
                                                                variants[argumentIndex].data());
                // qDebug() << "Converted "
                //          << arguments.value(parameterName)
                //          << " to type "
                //          << QVariant::typeToName(methodType)
                //          << " index "
                //          << argumentIndex;
            } else {
                qWarning() << "Can not convert "
                           << arguments.value(parameterName)
                           << " to type " << QVariant::typeToName(methodType);
            }
            argumentIndex+=1;
        }
        if(canHandle) {
            if(method.returnType() == QMetaType::QJsonObject) {
                this->dispatchJSONMethod(response, method, handler->handler, argumentTable);
            } else {
                this->dispatchVoidMethod(method, handler->handler, argumentTable);
            }
            handled = true;
        }
    } else {
        qWarning() << "Cound not find a method named with a matching signature.";
    }

    return handled;
}

void ClassHandlerManager::registerHandler(ClassHandler * handler)
{
    // Only process plugins that have not already been registered.
    if (!priv->handlers.contains(handler->objectName())){
        qDebug() << "Registering " << handler->objectName() << " as a handler.";
        bool canDispathTo = false;
        const QMetaObject* metaObject = handler->metaObject();
        for(int methodIndex = metaObject->methodOffset(); methodIndex < metaObject->methodCount(); ++methodIndex) {
            QMetaMethod method = metaObject->method(methodIndex);
            // We only want public slots whos first two arguements are request & response
            if(method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
                QList<QByteArray> parameterNames = method.parameterNames();
                if(parameterNames[0] == QByteArray("request") && parameterNames[1] == QByteArray("response")) {
                    canDispathTo = true;
                    ClassHandlerManager::PluginDescriptor *pluginDescriptor
                        = priv->handlers[handler->objectName()];
                    if(pluginDescriptor == NULL) {
                        pluginDescriptor = new ClassHandlerManager::PluginDescriptor();
                        priv->handlers[handler->objectName()]
                            = pluginDescriptor;
                    }
                    pluginDescriptor->className = handler->objectName();
                    pluginDescriptor->handler = handler;

                    uint parameterHash = qHash(QString::fromLatin1(method.name()));
                    foreach (QByteArray nameBytes, parameterNames) {
                        parameterHash += qHash(QString::fromLatin1(nameBytes));
                    }
                    pluginDescriptor->methods.insert(parameterHash, methodIndex);
                    pluginDescriptor->methodNames.append(QString::fromLatin1(method.name()));

                    QString signature = QString::fromLatin1(method.methodSignature());
                    qDebug() << signature << " is a dispatchable endpoint.";
                }
            }
        }
        if(canDispathTo) {
            handler->init();
        }
    }

}

int ClassHandlerManager::selectMethod(const QString className,
                                      const QString methodName,
                                      const QHash<QString, QString> arguments) const
{
    int methodIndex = -1;
    uint parameterHash = qHash(methodName);
    parameterHash += qHash(QString("request"));
    parameterHash += qHash(QString("response"));
    foreach (QString key, arguments.keys()) {
        parameterHash += qHash(key);
    }
    PluginDescriptor *pluginDescriptor = priv->handlers[className];
    if (pluginDescriptor->methods.contains(parameterHash)) {
        methodIndex = pluginDescriptor->methods.value(parameterHash);
    }
    return methodIndex;
}

/* ************************************************************************** */
/* Override Tufao::AbstractHttpServerRequestHandler Methods                   */
/* ************************************************************************** */
bool ClassHandlerManager::handleRequest(Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response)
{
    /* Apply urlNamespace and resume request dispatching or abort if request has
       a different urlNamespace */
    const QUrl originalUrl = request.url();
    const QString originalPath = originalUrl.path();

    if (!priv->urlNamespace.isEmpty()) {
        if (!(originalPath.size() > priv->urlNamespace.size()
              && originalPath.startsWith(priv->urlNamespace)
              && originalPath[priv->urlNamespace.size()] == '/')) {
            return false;
        }
    }

    const QString namespacedPath = [&originalPath,this]() {
        return originalPath.mid(priv->urlNamespace.size());
    }();

    /* The user MUST NOT view the original url. This design ease the
       implementation of nested handlers.

       request.setUrl(namespacedUrl) MUST be called before pass the request to
       the user and the previous url (originalUrl) MUST be restored if the
       dispatch failed and we'll return the request to HttpServerRequestRouter.
     */
    const QUrl namespacedUrl = [&originalUrl,&namespacedPath]() {
        QUrl ret = originalUrl;
        ret.setPath(namespacedPath);
        return ret;
    }();

    QStringList pathComponents = namespacedPath.split("/", QString::SkipEmptyParts);

    // There must be at least two path components (class & method)
    const int minimumPathComponents = 2;
    if (pathComponents.length() < minimumPathComponents) {
        qWarning() << "Request was dispatched to handler, but too few path"
            " components found.  The path components are"
                   << pathComponents;
        return false;
    }

    if (pathComponents.length() > minimumPathComponents  + 16) {
        // We also can not have too many arguments; 16 is max, as that is 8
        // argumetns plus request & response
        qWarning() << "Request was dispatched to handler, but too many path"
            " components found.  The path components are"
                   << pathComponents;
        return false;
    }

    int pathIndex = 0;
    QString className = pathComponents[pathIndex++];
    QString methodName = pathComponents[pathIndex++];
    // We need to have an even number of path components left
    if ((pathComponents.length() - pathIndex) % 2 != 0) {
        qWarning() << "Can not dispath as an odd number of parameter components"
            " were supplied.";
        return false;
    }

    // See if we have a class handler with a matching method
    if (!(priv->handlers.contains(className)
          && priv->handlers[className]->methodNames.contains(methodName))) {
        if (priv->handlers.contains(className)) {
            qWarning() << "The class" << className << "has no method named"
                       << methodName;
        }
    }

    // Convert the remaining path components into an argument hash
    QHash<QString, QString> arguments;
    while(pathIndex < pathComponents.length()){
        arguments[pathComponents[pathIndex]] = pathComponents[pathIndex + 1];
        pathIndex += 2;
    }

    request.setUrl(namespacedUrl);
    if (!processRequest(request, response, className, methodName, arguments)) {
        request.setUrl(originalUrl);
        return false;
    }

    return true;
}

} // namespace Tufao
<|endoftext|>