<commit_before>
#include "Log.h"
#include "Socket.h"
#include "Buffer.h"
#include <string.h>

#include <atomic>

#define SOCKET_MAX_CONNECTIONS 5

/* ------------------------------------- */
#if (defined __CYGWIN__ || defined __GNUC__)

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define SOCKET_HANDLE int
#define INVALID_SOCKET 0
#define SOCKET_ERROR -1

namespace {
    std::string ErrorMessage (const std::string& inMethod) {
        return "[Socket] CygWin::" + inMethod + " failed";
    }
}

/* ------------------------------------- */
#elif _MSC_VER >= 1800

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define SOCKET_HANDLE SOCKET

namespace {
    std::string ErrorMessage (const std::string& inMethod) {
        return "[Socket] WinSock2::" + inMethod + " failed with code " + std::to_string (WSAGetLastError ());
    }
}

/* ------------------------------------- */
#else
static_assert (true, "Incompatible compiler");
#endif
/* ------------------------------------- */

class OS::Socket::Implementation {

public:
    Implementation () :
        mSocketHandle (INVALID_SOCKET),
        mLatestAddrInfo (NULL),
        mIsConnected (false),
        mIsListening (false)
    {
    }
    ~Implementation () {
        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") is being destructed"));
    }

public:

    bool IsConnected () const {
        return mIsConnected;
    }

    bool IsListening () const {
        return mIsListening;
    }

    bool Initialize (SOCKET_HANDLE inSocketHandle) {
        mIsListening = false;
        mIsConnected = true;
        mSocketHandle = inSocketHandle;
        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") initialized"));
        return true;
    }

    bool Initialize (const std::string& inAddress, const std::string& inPort) {

        mIsListening = false;
        mIsConnected = false;

        struct addrinfo hints;

#if (defined __CYGWIN__ || defined __GNUC__)
        ::memset (&hints, 0, sizeof (hints));
#else
        ZeroMemory (&hints, sizeof (hints));
#endif
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;

        // Resolve the server address and port
        int res = getaddrinfo (inAddress.c_str (), inPort.c_str (), &hints, &mLatestAddrInfo);
        if (res != 0) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("getaddrinfo"));
            return false;
        }

        // Create a SOCKET for connecting to server
        mSocketHandle = socket (mLatestAddrInfo->ai_family, mLatestAddrInfo->ai_socktype, mLatestAddrInfo->ai_protocol);
        if (mSocketHandle == INVALID_SOCKET) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("socket"));
            return false;
        }

        LOGMESSAGE (OS::Log::kDebug, "[Socket](" + std::to_string (GetId ()) + ") initialized at " + inAddress + ":" + inPort);
        return true;
    }

    void Close () {

        if (mIsConnected || mIsListening) {

            freeaddrinfo (mLatestAddrInfo);

            shutdown (mSocketHandle, 2);

#if (defined __CYGWIN__ || defined __GNUC__)
            close (mSocketHandle);
#else
            closesocket (mSocketHandle);
#endif
            LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") closed"));
        }

        mIsConnected = false;
        mIsListening = false;
    }

    bool Listen () {

        if (!mIsListening) {

            int result = bind (mSocketHandle, mLatestAddrInfo->ai_addr, (int) mLatestAddrInfo->ai_addrlen);
            if (result == SOCKET_ERROR) {
                LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("bind"));
                return false;
            }

            result = listen (mSocketHandle, SOCKET_MAX_CONNECTIONS);
            if (result == SOCKET_ERROR) {
                LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("listen"));
                return false;
            }
        }

        mIsListening = true;
        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") listening..."));
        return true;
    }

    bool Accept (OS::Socket& outSocket) {

        if (!mIsListening) {
            return false;
        }

        SOCKET_HANDLE clientSocket = accept (mSocketHandle, NULL, NULL);
        if (clientSocket == INVALID_SOCKET) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("accept"));
            return false;
        }
        outSocket.Initialize (clientSocket);

        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") connected to socket " + std::to_string (outSocket.GetId ())));
        return true;
    }

    bool Connect () {

        if (mIsConnected) {
            return true;
        }

        // Connect to server.
        int res = connect (mSocketHandle, mLatestAddrInfo->ai_addr, (int) mLatestAddrInfo->ai_addrlen);
        if (res == SOCKET_ERROR) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("connect"));
            return false;
        }

        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") connected to server"));
        mIsConnected = true;
        return true;
    }

    unsigned GetId () const {
        return static_cast<unsigned> (mSocketHandle);
    }

    bool Send (const OS::Buffer& inBuffer) {

        if (!mIsConnected) {
            return false;
        }

        int result = send (mSocketHandle, inBuffer.GetDataPointer (), inBuffer.GetSize (), 0);
        if (result == SOCKET_ERROR) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("send"));
            Close ();
            return false;
        }
        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket] Send ") + std::to_string (inBuffer.GetSize ()) + std::string (" bytes to client id ") + std::to_string (mSocketHandle));
        return true;
    }

    bool Receive (OS::Buffer& outBuffer) {

        if (!mIsConnected) {
            return false;
        }

        int result = recv (mSocketHandle, outBuffer.GetDataPointer (), outBuffer.GetMaxSize (), 0);
        if (result == 0) {
            LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket] Received termination signal from client with id ") + std::to_string (mSocketHandle));
            Close ();
            return false;
        }
        if (result < 0) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("recv"));
            Close ();
            return false;
        }

        outBuffer.Resize (result);

        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket] Received ") + std::to_string (result) + std::string (" bytes from client with id ") + std::to_string (mSocketHandle));
        return true;
    }

private:
    SOCKET_HANDLE mSocketHandle;
    struct addrinfo *mLatestAddrInfo;
    std::atomic<bool> mIsConnected;
    std::atomic<bool> mIsListening;
};

/* ------------------------------------- */

OS::Socket::Socket (const std::string& inAddress, const std::string& inPortNumber) :
    mAddress (inAddress),
    mPortNumber (inPortNumber),
    mImpl (std::make_unique <Implementation> ())
{
}

OS::Socket::~Socket () {
}

bool OS::Socket::Initialize () {
    return mImpl->Initialize (mAddress, mPortNumber);
}

bool OS::Socket::Initialize (unsigned inHandle) {
    return mImpl->Initialize (inHandle);
}

void OS::Socket::Close () {
    mImpl->Close ();
}

unsigned OS::Socket::GetId () {
    return mImpl->GetId ();
}

bool OS::Socket::Listen () {
    return mImpl->Listen ();
}

bool OS::Socket::Accept (OS::Socket& outSocket) {
    return mImpl->Accept (outSocket);
}

bool OS::Socket::Connect () {
    return mImpl->Connect ();
}

bool OS::Socket::Send (const Buffer& inBuffer) {
    return mImpl->Send (inBuffer);
}

bool OS::Socket::Receive (Buffer& outBuffer) {
    return mImpl->Receive (outBuffer);
}

bool OS::Socket::IsConnected () const {
    return mImpl->IsConnected ();
}

bool OS::Socket::IsListening () const {
    return mImpl->IsListening ();
}
<commit_msg>reuse address<commit_after>
#include "Log.h"
#include "Socket.h"
#include "Buffer.h"
#include <string.h>

#include <atomic>

#define SOCKET_MAX_CONNECTIONS 5

/* ------------------------------------- */
#if (defined __CYGWIN__ || defined __GNUC__)

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define SOCKET_HANDLE int
#define INVALID_SOCKET 0
#define SOCKET_ERROR -1

namespace {
    std::string ErrorMessage (const std::string& inMethod) {
        return "[Socket] CygWin::" + inMethod + " failed";
    }
}

/* ------------------------------------- */
#elif _MSC_VER >= 1800

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define SOCKET_HANDLE SOCKET

namespace {
    std::string ErrorMessage (const std::string& inMethod) {
        return "[Socket] WinSock2::" + inMethod + " failed with code " + std::to_string (WSAGetLastError ());
    }
}

/* ------------------------------------- */
#else
static_assert (true, "Incompatible compiler");
#endif
/* ------------------------------------- */

class OS::Socket::Implementation {

public:
    Implementation () :
        mSocketHandle (INVALID_SOCKET),
        mLatestAddrInfo (NULL),
        mIsConnected (false),
        mIsListening (false)
    {
    }
    ~Implementation () {
        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") is being destructed"));
    }

public:

    bool IsConnected () const {
        return mIsConnected;
    }

    bool IsListening () const {
        return mIsListening;
    }

    bool Initialize (SOCKET_HANDLE inSocketHandle) {
        mIsListening = false;
        mIsConnected = true;
        mSocketHandle = inSocketHandle;
        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") initialized"));
        return true;
    }

    bool Initialize (const std::string& inAddress, const std::string& inPort) {

        mIsListening = false;
        mIsConnected = false;

        struct addrinfo hints;

#if (defined __CYGWIN__ || defined __GNUC__)
        ::memset (&hints, 0, sizeof (hints));
#else
        ZeroMemory (&hints, sizeof (hints));
#endif
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;

        // Resolve the server address and port
        int res = getaddrinfo (inAddress.c_str (), inPort.c_str (), &hints, &mLatestAddrInfo);
        if (res != 0) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("getaddrinfo"));
            return false;
        }

        // Create a SOCKET for connecting to server
        mSocketHandle = socket (mLatestAddrInfo->ai_family, mLatestAddrInfo->ai_socktype, mLatestAddrInfo->ai_protocol);
        if (mSocketHandle == INVALID_SOCKET) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("socket"));
            return false;
        }

        int flag (1);
#if (defined __CYGWIN__ || defined __GNUC__)
        if (setsockopt (mSocketHandle, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&flag), sizeof(flag)) < 0) {
#else
        if (setsockopt (mSocketHandle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<char*>(&flag), sizeof(flag)) < 0) {
#endif
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("setsocketopt"));
            return false;
        }

        LOGMESSAGE (OS::Log::kDebug, "[Socket](" + std::to_string (GetId ()) + ") initialized at " + inAddress + ":" + inPort);
        return true;
    }

    void Close () {

        if (mIsConnected || mIsListening) {

            freeaddrinfo (mLatestAddrInfo);

            shutdown (mSocketHandle, 2);

#if (defined __CYGWIN__ || defined __GNUC__)
            close (mSocketHandle);
#else
            closesocket (mSocketHandle);
#endif
            LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") closed"));
        }

        mIsConnected = false;
        mIsListening = false;
    }

    bool Listen () {

        if (!mIsListening) {

            int result = bind (mSocketHandle, mLatestAddrInfo->ai_addr, (int) mLatestAddrInfo->ai_addrlen);
            if (result == SOCKET_ERROR) {
                LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("bind"));
                return false;
            }

            result = listen (mSocketHandle, SOCKET_MAX_CONNECTIONS);
            if (result == SOCKET_ERROR) {
                LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("listen"));
                return false;
            }
        }

        mIsListening = true;
        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") listening..."));
        return true;
    }

    bool Accept (OS::Socket& outSocket) {

        if (!mIsListening) {
            return false;
        }

        SOCKET_HANDLE clientSocket = accept (mSocketHandle, NULL, NULL);
        if (clientSocket == INVALID_SOCKET) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("accept"));
            return false;
        }
        outSocket.Initialize (clientSocket);

        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") connected to socket " + std::to_string (outSocket.GetId ())));
        return true;
    }

    bool Connect () {

        if (mIsConnected) {
            return true;
        }

        // Connect to server.
        int res = connect (mSocketHandle, mLatestAddrInfo->ai_addr, (int) mLatestAddrInfo->ai_addrlen);
        if (res == SOCKET_ERROR) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("connect"));
            return false;
        }

        LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket](") + std::to_string (GetId ()) + std::string (") connected to server"));
        mIsConnected = true;
        return true;
    }

    unsigned GetId () const {
        return static_cast<unsigned> (mSocketHandle);
    }

    bool Send (const OS::Buffer& inBuffer) {

        if (!mIsConnected) {
            return false;
        }

        int result = send (mSocketHandle, inBuffer.GetDataPointer (), inBuffer.GetSize (), 0);
        if (result == SOCKET_ERROR) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("send"));
            Close ();
            return false;
        }
        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket] Send ") + std::to_string (inBuffer.GetSize ()) + std::string (" bytes to client id ") + std::to_string (mSocketHandle));
        return true;
    }

    bool Receive (OS::Buffer& outBuffer) {

        if (!mIsConnected) {
            return false;
        }

        int result = recv (mSocketHandle, outBuffer.GetDataPointer (), outBuffer.GetMaxSize (), 0);
        if (result == 0) {
            LOGMESSAGE (OS::Log::kDebug, std::string ("[Socket] Received termination signal from client with id ") + std::to_string (mSocketHandle));
            Close ();
            return false;
        }
        if (result < 0) {
            LOGMESSAGE (OS::Log::kDebug, ErrorMessage ("recv"));
            Close ();
            return false;
        }

        outBuffer.Resize (result);

        LOGMESSAGE (OS::Log::kTrace, std::string ("[Socket] Received ") + std::to_string (result) + std::string (" bytes from client with id ") + std::to_string (mSocketHandle));
        return true;
    }

private:
    SOCKET_HANDLE mSocketHandle;
    struct addrinfo *mLatestAddrInfo;
    std::atomic<bool> mIsConnected;
    std::atomic<bool> mIsListening;
};

/* ------------------------------------- */

OS::Socket::Socket (const std::string& inAddress, const std::string& inPortNumber) :
    mAddress (inAddress),
    mPortNumber (inPortNumber),
    mImpl (std::make_unique <Implementation> ())
{
}

OS::Socket::~Socket () {
}

bool OS::Socket::Initialize () {
    return mImpl->Initialize (mAddress, mPortNumber);
}

bool OS::Socket::Initialize (unsigned inHandle) {
    return mImpl->Initialize (inHandle);
}

void OS::Socket::Close () {
    mImpl->Close ();
}

unsigned OS::Socket::GetId () {
    return mImpl->GetId ();
}

bool OS::Socket::Listen () {
    return mImpl->Listen ();
}

bool OS::Socket::Accept (OS::Socket& outSocket) {
    return mImpl->Accept (outSocket);
}

bool OS::Socket::Connect () {
    return mImpl->Connect ();
}

bool OS::Socket::Send (const Buffer& inBuffer) {
    return mImpl->Send (inBuffer);
}

bool OS::Socket::Receive (Buffer& outBuffer) {
    return mImpl->Receive (outBuffer);
}

bool OS::Socket::IsConnected () const {
    return mImpl->IsConnected ();
}

bool OS::Socket::IsListening () const {
    return mImpl->IsListening ();
}
<|endoftext|>


<commit_before>/**
 * @file   BoxPacketTest.cpp
 * @brief  BoxPacket class tester.
 * @author zer0
 * @date   2019-05-22
 */

#include <gtest/gtest.h>
#include <libtbag/box/BoxPacket.hpp>

using namespace libtbag;
using namespace libtbag::box;
using namespace libtbag::box::details;

TEST(BoxPacketTest, Default)
{
    box_data box;
    box_clear(&box);

    ASSERT_EQ(E_SUCCESS, box_resize_args(&box, BT_INT32, BD_CPU, nullptr, 3, 4, 3, 2));
    ui32 i = 0;
    for (i = 0; i < 24; ++i) {
        box_data_set(&box, &i, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, ((si32*)box.data)[i]);
    }

    ASSERT_EQ(BT_INT32, box.type);
    ASSERT_EQ(BD_CPU, box.device);
    ASSERT_EQ(0, box.ext[0]);
    ASSERT_EQ(0, box.ext[1]);
    ASSERT_EQ(0, box.ext[2]);
    ASSERT_EQ(0, box.ext[3]);
    ASSERT_NE(nullptr, box.data);
    ASSERT_EQ(4*3*2, box.size);
    ASSERT_EQ(4*3*2*sizeof(ui32), box.total_data_byte);
    ASSERT_NE(nullptr, box.dims);
    ASSERT_EQ(3, box.rank);
    ASSERT_EQ(3*sizeof(ui32), box.total_dims_byte);

    BoxPacket packet;
    ASSERT_EQ(E_SUCCESS, packet.build(&box));
    auto * pointer = packet.point();
    ASSERT_NE(nullptr, pointer);
    auto size = packet.size();
    ASSERT_LT(1, size);

    box_data box2;
    box_clear(&box2);

    BoxPacket packet2;
    std::size_t computed_size = 0;
    ASSERT_EQ(E_SUCCESS, packet2.parse((char const *)pointer, size, &box2, &computed_size));
    ASSERT_LT(1, computed_size);
    ASSERT_EQ(computed_size, size);

    ASSERT_EQ(box.type, box2.type);
    ASSERT_EQ(box.device, box2.device);
    ASSERT_EQ(box.ext[0], box2.ext[0]);
    ASSERT_EQ(box.ext[1], box2.ext[1]);
    ASSERT_EQ(box.ext[2], box2.ext[2]);
    ASSERT_EQ(box.ext[3], box2.ext[3]);
    ASSERT_NE(box.data, box2.data);
    ASSERT_EQ(box.total_data_byte, box2.total_data_byte);
    ASSERT_EQ(box.size, box2.size);
    ASSERT_NE(box.dims, box2.dims);
    ASSERT_EQ(box.total_dims_byte, box2.total_dims_byte);
    ASSERT_EQ(box.rank, box2.rank);

    for (i = 0; i < 24; ++i) {
        si32 box_data;
        box_data_get(&box, &box_data, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, box_data);

        si32 box2_data;
        box_data_get(&box2, &box2_data, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, box2_data);
        ASSERT_EQ(i, ((si32*)box2.data)[i]);
    }

    box_free(&box);
    box_free(&box2);
}

<commit_msg>Create BoxPacketTest.ToJson tester.<commit_after>/**
 * @file   BoxPacketTest.cpp
 * @brief  BoxPacket class tester.
 * @author zer0
 * @date   2019-05-22
 */

#include <gtest/gtest.h>
#include <libtbag/3rd/jsoncpp/json.h>
#include <libtbag/box/BoxPacket.hpp>

using namespace libtbag;
using namespace libtbag::box;
using namespace libtbag::box::details;

TEST(BoxPacketTest, Default)
{
    box_data box;
    box_clear(&box);

    ASSERT_EQ(E_SUCCESS, box_resize_args(&box, BT_INT32, BD_CPU, nullptr, 3, 4, 3, 2));
    ui32 i = 0;
    for (i = 0; i < 24; ++i) {
        box_data_set(&box, &i, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, ((si32*)box.data)[i]);
    }

    ASSERT_EQ(BT_INT32, box.type);
    ASSERT_EQ(BD_CPU, box.device);
    ASSERT_EQ(0, box.ext[0]);
    ASSERT_EQ(0, box.ext[1]);
    ASSERT_EQ(0, box.ext[2]);
    ASSERT_EQ(0, box.ext[3]);
    ASSERT_NE(nullptr, box.data);
    ASSERT_EQ(4*3*2, box.size);
    ASSERT_EQ(4*3*2*sizeof(ui32), box.total_data_byte);
    ASSERT_NE(nullptr, box.dims);
    ASSERT_EQ(3, box.rank);
    ASSERT_EQ(3*sizeof(ui32), box.total_dims_byte);

    BoxPacket packet;
    ASSERT_EQ(E_SUCCESS, packet.build(&box));
    auto * pointer = packet.point();
    ASSERT_NE(nullptr, pointer);
    auto size = packet.size();
    ASSERT_LT(1, size);

    box_data box2;
    box_clear(&box2);

    BoxPacket packet2;
    std::size_t computed_size = 0;
    ASSERT_EQ(E_SUCCESS, packet2.parse((char const *)pointer, size, &box2, &computed_size));
    ASSERT_LT(1, computed_size);
    ASSERT_EQ(computed_size, size);

    ASSERT_EQ(box.type, box2.type);
    ASSERT_EQ(box.device, box2.device);
    ASSERT_EQ(box.ext[0], box2.ext[0]);
    ASSERT_EQ(box.ext[1], box2.ext[1]);
    ASSERT_EQ(box.ext[2], box2.ext[2]);
    ASSERT_EQ(box.ext[3], box2.ext[3]);
    ASSERT_NE(box.data, box2.data);
    ASSERT_EQ(box.total_data_byte, box2.total_data_byte);
    ASSERT_EQ(box.size, box2.size);
    ASSERT_NE(box.dims, box2.dims);
    ASSERT_EQ(box.total_dims_byte, box2.total_dims_byte);
    ASSERT_EQ(box.rank, box2.rank);

    for (i = 0; i < 24; ++i) {
        si32 box_data;
        box_data_get(&box, &box_data, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, box_data);

        si32 box2_data;
        box_data_get(&box2, &box2_data, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, box2_data);
        ASSERT_EQ(i, ((si32*)box2.data)[i]);
    }

    box_free(&box);
    box_free(&box2);
}

TEST(BoxPacketTest, ToJson)
{
    box_data box;
    box_clear(&box);

    ASSERT_EQ(E_SUCCESS, box_resize_args(&box, BT_INT32, BD_CPU, nullptr, 3, 4, 3, 2));
    ui32 i = 0;
    for (i = 0; i < 24; ++i) {
        box_data_set(&box, &i, BT_INT32, BD_CPU, nullptr, i);
        ASSERT_EQ(i, ((si32*)box.data)[i]);
    }

    BoxPacket packet;
    ASSERT_EQ(E_SUCCESS, packet.build(&box));
    ASSERT_FALSE(packet.toJsonString().empty());

    // Json::Value root;
    // Json::Reader reader;
    // ASSERT_TRUE(reader.parse(packet.toJsonString(), root));
    // ASSERT_TRUE(root["dims"].isArray());
    // ASSERT_EQ(3, root["dims"].size());
    // ASSERT_EQ(4, root["dims"][0].asInt());
    // ASSERT_EQ(3, root["dims"][1].asInt());
    // ASSERT_EQ(2, root["dims"][2].asInt());

    box_free(&box);
}

<|endoftext|>


<commit_before>/**
 * \ file FixedDelayLineFilter.cpp
 */

#include <iostream>

#include <ATK/Delay/FixedDelayLineFilter.h>

#include <ATK/Core/InPointerFilter.h>
#include <ATK/Core/OutPointerFilter.h>

#include <ATK/Mock/SimpleSinusGeneratorFilter.h>
#include <ATK/Mock/TriangleCheckerFilter.h>

#include <ATK/Tools/SumFilter.h>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp>

#include <boost/math/constants/constants.hpp>

#define PROCESSSIZE (1024*64)

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_line100_delay_1_test )
{
  ATK::FixedDelayLineFilter<float> filter(100);
  BOOST_CHECK_THROW(filter.set_delay(-1), std::out_of_range);
}

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_line100_delay_100_test )
{
  ATK::FixedDelayLineFilter<float> filter(100);
  BOOST_CHECK_THROW(filter.set_delay(100), std::out_of_range);
}

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_sinus_line100_delay50_test )
{
  std::array<float, PROCESSSIZE> data;
  for(ptrdiff_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = std::sin(2 * boost::math::constants::pi<float>() * (i+1.)/48000 * 1000);
  }

  ATK::InPointerFilter<float> generator(data.data(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  std::array<float, PROCESSSIZE> outdata;

  ATK::FixedDelayLineFilter<float> filter(100);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);

  ATK::OutPointerFilter<float> output(outdata.data(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(1);
  output.process(49);
  output.process(51);
  output.process(PROCESSSIZE - 1 - 49 -51);

  for(ptrdiff_t i = 0; i < 50; ++i)
  {
    BOOST_REQUIRE_EQUAL(0, outdata[i]);
  }

  for(ptrdiff_t i = 50; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_EQUAL(data[i - 50], outdata[i]);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_line100_delay50_progressive_test)
{
  ATK::SimpleSinusGeneratorFilter<float> generator;
  generator.set_output_sampling_rate(48000);
  generator.set_frequency(480);

  ATK::FixedDelayLineFilter<float> filter(100);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);
  filter.process(50);

  ATK::SumFilter<float> sum;
  sum.set_input_sampling_rate(48000);
  sum.set_input_port(0, &filter, 0);
  sum.set_input_port(1, &generator, 0);

  ATK::TriangleCheckerFilter<float> output;
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &sum, 0);
  output.set_amplitude(0);

  for (std::size_t i = 1; i < 100; ++i)
  {
    output.process(i);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_line1000_delay50_progressive_test)
{
  ATK::SimpleSinusGeneratorFilter<float> generator;
  generator.set_output_sampling_rate(48000);
  generator.set_frequency(480);

  ATK::FixedDelayLineFilter<float> filter(1000);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);
  filter.process(50);

  ATK::SumFilter<float> sum;
  sum.set_input_sampling_rate(48000);
  sum.set_input_port(0, &filter, 0);
  sum.set_input_port(1, &generator, 0);

  ATK::TriangleCheckerFilter<float> output;
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &sum, 0);
  output.set_amplitude(0);

  for (std::size_t i = 50; i < 1000; ++i)
  {
    output.process(i);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_delay_test )
{
  ATK::FixedDelayLineFilter<float> filter(128);
  filter.set_delay(10);
  BOOST_CHECK_EQUAL(filter.get_delay(), 10);
}
<commit_msg>Fix check for zero delay<commit_after>/**
 * \ file FixedDelayLineFilter.cpp
 */

#include <iostream>

#include <ATK/Delay/FixedDelayLineFilter.h>

#include <ATK/Core/InPointerFilter.h>
#include <ATK/Core/OutPointerFilter.h>

#include <ATK/Mock/SimpleSinusGeneratorFilter.h>
#include <ATK/Mock/TriangleCheckerFilter.h>

#include <ATK/Tools/SumFilter.h>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp>

#include <boost/math/constants/constants.hpp>

#define PROCESSSIZE (1024*64)

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_line100_delay_1_test )
{
  ATK::FixedDelayLineFilter<float> filter(100);
  BOOST_CHECK_THROW(filter.set_delay(0), std::out_of_range);
}

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_line100_delay_100_test )
{
  ATK::FixedDelayLineFilter<float> filter(100);
  BOOST_CHECK_THROW(filter.set_delay(100), std::out_of_range);
}

BOOST_AUTO_TEST_CASE( FixedDelayLineFilter_sinus_line100_delay50_test )
{
  std::array<float, PROCESSSIZE> data;
  for(ptrdiff_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = std::sin(2 * boost::math::constants::pi<float>() * (i+1.)/48000 * 1000);
  }

  ATK::InPointerFilter<float> generator(data.data(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  std::array<float, PROCESSSIZE> outdata;

  ATK::FixedDelayLineFilter<float> filter(100);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);

  ATK::OutPointerFilter<float> output(outdata.data(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(1);
  output.process(49);
  output.process(51);
  output.process(PROCESSSIZE - 1 - 49 -51);

  for(ptrdiff_t i = 0; i < 50; ++i)
  {
    BOOST_REQUIRE_EQUAL(0, outdata[i]);
  }

  for(ptrdiff_t i = 50; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_EQUAL(data[i - 50], outdata[i]);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_line100_delay50_progressive_test)
{
  ATK::SimpleSinusGeneratorFilter<float> generator;
  generator.set_output_sampling_rate(48000);
  generator.set_frequency(480);

  ATK::FixedDelayLineFilter<float> filter(100);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);
  filter.process(50);

  ATK::SumFilter<float> sum;
  sum.set_input_sampling_rate(48000);
  sum.set_input_port(0, &filter, 0);
  sum.set_input_port(1, &generator, 0);

  ATK::TriangleCheckerFilter<float> output;
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &sum, 0);
  output.set_amplitude(0);

  for (std::size_t i = 1; i < 100; ++i)
  {
    output.process(i);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_line1000_delay50_progressive_test)
{
  ATK::SimpleSinusGeneratorFilter<float> generator;
  generator.set_output_sampling_rate(48000);
  generator.set_frequency(480);

  ATK::FixedDelayLineFilter<float> filter(1000);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_delay(50);
  filter.process(50);

  ATK::SumFilter<float> sum;
  sum.set_input_sampling_rate(48000);
  sum.set_input_port(0, &filter, 0);
  sum.set_input_port(1, &generator, 0);

  ATK::TriangleCheckerFilter<float> output;
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &sum, 0);
  output.set_amplitude(0);

  for (std::size_t i = 50; i < 1000; ++i)
  {
    output.process(i);
  }
}

BOOST_AUTO_TEST_CASE(FixedDelayLineFilter_sinus_delay_test )
{
  ATK::FixedDelayLineFilter<float> filter(128);
  filter.set_delay(10);
  BOOST_CHECK_EQUAL(filter.get_delay(), 10);
}
<|endoftext|>


<commit_before>#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <Nazara/Shader/SpirvWriter.hpp>
#include <Catch/catch.hpp>
#include <cctype>

void ExpectingGLSL(Nz::ShaderAst::StatementPtr& shader, std::string_view expectedOutput)
{
	Nz::GlslWriter writer;

	std::string output = writer.Generate(shader);
	std::size_t funcOffset = output.find("void main()");
	std::string_view subset = Nz::Trim(output).substr(funcOffset);
	expectedOutput = Nz::Trim(expectedOutput);

	REQUIRE(subset == expectedOutput);
}

void ExpectingSpirV(Nz::ShaderAst::StatementPtr& shader, std::string_view expectedOutput)
{
	Nz::SpirvWriter writer;
	auto spirv = writer.Generate(shader);

	Nz::SpirvPrinter printer;

	Nz::SpirvPrinter::Settings settings;
	settings.printHeader = false;
	settings.printParameters = false;

	std::string output = printer.Print(spirv.data(), spirv.size(), settings);
	std::size_t funcOffset = output.find("OpFunction");
	std::string_view subset = Nz::Trim(output).substr(funcOffset);
	expectedOutput = Nz::Trim(expectedOutput);

	REQUIRE(subset == expectedOutput);
}

SCENARIO("Shader generation", "[Shader]")
{
	SECTION("Nested member loading")
	{
		std::vector<Nz::ShaderAst::StatementPtr> statements;

		Nz::ShaderAst::StructDescription innerStructDesc;
		{
			innerStructDesc.name = "innerStruct";
			auto& member = innerStructDesc.members.emplace_back();
			member.name = "field";
			member.type = Nz::ShaderAst::VectorType{ 3, Nz::ShaderAst::PrimitiveType::Float32 };
		}
		statements.push_back(Nz::ShaderBuilder::DeclareStruct(std::move(innerStructDesc)));

		Nz::ShaderAst::StructDescription outerStruct;
		{
			outerStruct.name = "outerStruct";
			auto& member = outerStruct.members.emplace_back();
			member.name = "s";
			member.type = Nz::ShaderAst::IdentifierType{ "innerStruct" };
		}
		statements.push_back(Nz::ShaderBuilder::DeclareStruct(std::move(outerStruct)));

		auto external = std::make_unique<Nz::ShaderAst::DeclareExternalStatement>();
		external->externalVars.push_back({
			std::nullopt,
			"ubo",
			Nz::ShaderAst::UniformType{ Nz::ShaderAst::IdentifierType{ "outerStruct" } }
		});
		statements.push_back(std::move(external));

		SECTION("Nested AccessMember")
		{
			auto ubo = Nz::ShaderBuilder::Identifier("ubo");
			auto firstAccess = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s" });
			auto secondAccess = Nz::ShaderBuilder::AccessMember(std::move(firstAccess), { "field" });

			auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(secondAccess), { Nz::ShaderAst::SwizzleComponent::Third });
			auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::PrimitiveType::Float32, std::move(swizzle));

			statements.push_back(Nz::ShaderBuilder::DeclareFunction("main", std::move(varDecl)));

			Nz::ShaderAst::StatementPtr shader = Nz::ShaderBuilder::MultiStatement(std::move(statements));

			SECTION("Generating GLSL")
			{
				ExpectingGLSL(shader, R"(
void main()
{
	float result = ubo.s.field.z;
}
)");
			}
			SECTION("Generating Spir-V")
			{
				ExpectingSpirV(shader, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
			}
		}

		SECTION("AccessMember with multiples fields")
		{
			auto ubo = Nz::ShaderBuilder::Identifier("ubo");
			auto access = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s", "field" });

			auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(access), { Nz::ShaderAst::SwizzleComponent::Third });
			auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::PrimitiveType::Float32, std::move(swizzle));

			statements.push_back(Nz::ShaderBuilder::DeclareFunction("main", std::move(varDecl)));

			Nz::ShaderAst::StatementPtr shader = Nz::ShaderBuilder::MultiStatement(std::move(statements));

			SECTION("Generating GLSL")
			{
				ExpectingGLSL(shader, R"(
void main()
{
	float result = ubo.s.field.z;
}
)");
			}
			SECTION("Generating Spir-V")
			{
				ExpectingSpirV(shader, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
			}
		}
	}
}
<commit_msg>Tests: Fix Shader/AccessMember<commit_after>#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <Nazara/Shader/SpirvWriter.hpp>
#include <Catch/catch.hpp>
#include <cctype>

void ExpectingGLSL(Nz::ShaderAst::StatementPtr& shader, std::string_view expectedOutput)
{
	Nz::GlslWriter writer;

	std::string output = writer.Generate(shader);
	std::size_t funcOffset = output.find("void main()");
	std::string_view subset = Nz::Trim(output).substr(funcOffset);
	expectedOutput = Nz::Trim(expectedOutput);

	REQUIRE(subset == expectedOutput);
}

void ExpectingSpirV(Nz::ShaderAst::StatementPtr& shader, std::string_view expectedOutput)
{
	Nz::SpirvWriter writer;
	auto spirv = writer.Generate(shader);

	Nz::SpirvPrinter printer;

	Nz::SpirvPrinter::Settings settings;
	settings.printHeader = false;
	settings.printParameters = false;

	std::string output = printer.Print(spirv.data(), spirv.size(), settings);
	std::size_t funcOffset = output.find("OpFunction");
	std::string_view subset = Nz::Trim(output).substr(funcOffset);
	expectedOutput = Nz::Trim(expectedOutput);

	REQUIRE(subset == expectedOutput);
}

SCENARIO("Shader generation", "[Shader]")
{
	SECTION("Nested member loading")
	{
		std::vector<Nz::ShaderAst::StatementPtr> statements;

		Nz::ShaderAst::StructDescription innerStructDesc;
		{
			innerStructDesc.name = "innerStruct";
			auto& member = innerStructDesc.members.emplace_back();
			member.name = "field";
			member.type = Nz::ShaderAst::VectorType{ 3, Nz::ShaderAst::PrimitiveType::Float32 };
		}
		statements.push_back(Nz::ShaderBuilder::DeclareStruct(std::move(innerStructDesc)));

		Nz::ShaderAst::StructDescription outerStruct;
		{
			outerStruct.name = "outerStruct";
			auto& member = outerStruct.members.emplace_back();
			member.name = "s";
			member.type = Nz::ShaderAst::IdentifierType{ "innerStruct" };
		}
		statements.push_back(Nz::ShaderBuilder::DeclareStruct(std::move(outerStruct)));

		auto external = std::make_unique<Nz::ShaderAst::DeclareExternalStatement>();
		external->externalVars.push_back({
			std::nullopt,
			"ubo",
			Nz::ShaderAst::UniformType{ Nz::ShaderAst::IdentifierType{ "outerStruct" } }
		});
		statements.push_back(std::move(external));

		SECTION("Nested AccessMember")
		{
			auto ubo = Nz::ShaderBuilder::Identifier("ubo");
			auto firstAccess = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s" });
			auto secondAccess = Nz::ShaderBuilder::AccessMember(std::move(firstAccess), { "field" });

			auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(secondAccess), { Nz::ShaderAst::SwizzleComponent::Third });
			auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::PrimitiveType::Float32, std::move(swizzle));

			statements.push_back(Nz::ShaderBuilder::DeclareFunction("main", std::move(varDecl)));

			Nz::ShaderAst::StatementPtr shader = Nz::ShaderBuilder::MultiStatement(std::move(statements));

			SECTION("Generating GLSL")
			{
				ExpectingGLSL(shader, R"(
void main()
{
	float result = ubo.s.field.z;
}
)");
			}
			SECTION("Generating Spir-V")
			{
				ExpectingSpirV(shader, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
			}
		}

		SECTION("AccessMember with multiples fields")
		{
			auto ubo = Nz::ShaderBuilder::Identifier("ubo");
			auto access = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s", "field" });

			auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(access), { Nz::ShaderAst::SwizzleComponent::Third });
			auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::PrimitiveType::Float32, std::move(swizzle));

			statements.push_back(Nz::ShaderBuilder::DeclareFunction("main", std::move(varDecl)));

			Nz::ShaderAst::StatementPtr shader = Nz::ShaderBuilder::MultiStatement(std::move(statements));

			SECTION("Generating GLSL")
			{
				ExpectingGLSL(shader, R"(
void main()
{
	float result = ubo.s.field.z;
}
)");
			}
			SECTION("Generating Spir-V")
			{
				ExpectingSpirV(shader, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
			}
		}
	}
}
<|endoftext|>


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

#include <modules/autonavigation/avoidcollisioncurve.h>

#include <modules/autonavigation/helperfunctions.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/query/query.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/projection.hpp>
#include <algorithm>
#include <vector>

namespace {
    constexpr const char* _loggerCat = "AvoidCollisionCurve";
    const double Epsilon = 1E-7;

    // TODO: move this list somewhere else
    const std::vector<std::string> RelevantTags{
        "planet_solarSystem",
        "moon_solarSystem"
        // TODO
    };

} // namespace

namespace openspace::autonavigation {

AvoidCollisionCurve::AvoidCollisionCurve(const Waypoint& start, const Waypoint& end) {
    _points.push_back(start.position());

    // Add a point to first go straight out if starting close to planet
    double thresholdFactor = 3.0;
    glm::dvec3 startNodePos = start.node()->worldPosition();
    double startNodeRadius = start.nodeDetails.validBoundingSphere;
    glm::dvec3 startNodeToStartPos = start.position() - startNodePos;
    glm::dvec3 startViewDir = glm::normalize(start.rotation() * glm::dvec3(0.0, 0.0, -1.0));

    if (glm::length(startNodeToStartPos) < thresholdFactor * startNodeRadius) {
        double distance = startNodeRadius;

        glm::dvec3 newPos = start.position() - distance * startViewDir;
        _points.push_back(newPos);
    }

    // Add point for moving out if the end state is in opposite direction
    //TODO: determine if its best to compare to end orientation or position
    glm::dvec3 startToEnd = end.position() - start.position();
    double cosStartAngle = glm::dot(normalize(-startViewDir), normalize(startToEnd));
    if (cosStartAngle > 0.7) {
        glm::dquat middleRotation = glm::slerp(start.rotation(), end.rotation(), 0.5); // OBS! Rotation method is not necessarily slerp
        glm::dvec3 middleViewDir = glm::normalize(middleRotation * glm::dvec3(0.0, 0.0, -1.0));
        double distance = 0.4 * glm::length(startToEnd);

        glm::dvec3 newPos = start.position() + 0.2 * startToEnd - distance * middleViewDir;
        _points.push_back(newPos);
    }

    // TODO: Add a point to approach straigt towards a specific pose near planet
    // TODO: Calculate nice end pose if not defined

    _points.push_back(end.position());

    std::vector<SceneGraphNode*> relevantNodes = findRelevantNodes();

    // Create extra points to avoid collision
    removeCollisions(relevantNodes);

    int nrSegments = _points.size() - 1;

    // default values for the curve parameter - equally spaced
    for (double t = 0.0; t <= 1.0; t += 1.0 / (double)nrSegments) {
        _parameterIntervals.push_back(t);
    }

    _length = arcLength(1.0);

    initParameterIntervals();
}

// Interpolate a list of control points and knot times
glm::dvec3 AvoidCollisionCurve::positionAt(double u) {
    return interpolatePoints(u);
}

std::vector<SceneGraphNode*> AvoidCollisionCurve::findRelevantNodes() {
    // TODO: make more efficient
    const std::vector<SceneGraphNode*>& allNodes =
        global::renderEngine.scene()->allSceneGraphNodes();

    auto isRelevant = [](const SceneGraphNode* node) {
        // does the node match any of the relevant tags?
        const std::vector<std::string> tags = node->tags();
        auto result = std::find_first_of(RelevantTags.begin(), RelevantTags.end(), tags.begin(), tags.end());

        // does not match any tags => not interesting
        if (result == RelevantTags.end()) {
            return false;
        }

        return node->renderable() && (node->boundingSphere() > 0.0);
    };

    std::vector<SceneGraphNode*> result{};
    std::copy_if(allNodes.begin(), allNodes.end(), std::back_inserter(result), isRelevant);

    return result;
}

void AvoidCollisionCurve::removeCollisions(std::vector<SceneGraphNode*>& relevantNodes, int step) {
    int nrSegments = _points.size() - 1;
    int maxSteps = 10;

    // TODO: handle better / present warning if early out
    if (step > maxSteps) return;

    // go through all segments and check for collisions
    for (int i = 0; i < nrSegments; ++i) {
        const glm::dvec3 lineStart = _points[i];
        const glm::dvec3 lineEnd = _points[i + 1];

        for (SceneGraphNode* node : relevantNodes) {
            // do collision check in relative coordinates, to avoid huge numbers
            const glm::dmat4 modelTransform = node->modelTransform();
            glm::dvec3 p1 = glm::inverse(modelTransform) * glm::dvec4(lineStart, 1.0);
            glm::dvec3 p2 = glm::inverse(modelTransform) * glm::dvec4(lineEnd, 1.0);

            // sphere to check for collision
            double bs = node->boundingSphere();
            double radius = bs;
            // TODO: add a buffer (i.e. sue a little larger sphere), when we have
            // behaviour for leaving a target. Don't want to pass too close to objects
            glm::dvec3 center = glm::dvec3(0.0, 0.0, 0.0);
            glm::dvec3 point;

            bool collision = helpers::lineSphereIntersection(p1, p2, center, radius, point);

            // convert back to world coordinates
            glm::dvec3 pointWorld = modelTransform * glm::dvec4(point, 1.0);

            if (collision) {
                LINFO(fmt::format("Collision with node: {}!", node->identifier()));

                // to avoid collision, take a step in an orhtogonal direction of the
                // collision point and add a new point
                glm::dvec3 lineDir = glm::normalize(lineEnd - lineStart);
                glm::dvec3 nodePos = node->worldPosition();
                glm::dvec3 collisionPointToCenter = nodePos - pointWorld;
                glm::dvec3 parallell = glm::proj(collisionPointToCenter, lineDir);
                glm::dvec3 orthogonal = collisionPointToCenter - parallell;

                double distance = 3.0 * radius;
                glm::dvec3 extraKnot = pointWorld - distance * glm::normalize(orthogonal);
                _points.insert(_points.begin() + i + 1, extraKnot);

                // TODO: maybe make this more efficient, and make sure that we have a base case.
                removeCollisions(relevantNodes, ++step);
                break;
            }
        }
    }
}

// compute curve parameter intervals based on relative arc length
void AvoidCollisionCurve::initParameterIntervals() {
    std::vector<double> newIntervals;
    int nrSegments = _points.size() - 1;
    for (double t = 0.0; t <= 1.0; t += 1.0 / (double)nrSegments) {
        newIntervals.push_back(arcLength(t) / _length);
    }
    _parameterIntervals.swap(newIntervals);
}

// Catmull-Rom inspired interpolation of the curve points
glm::dvec3 AvoidCollisionCurve::interpolatePoints(double u)
{
    ghoul_assert(_points.size() == _parameterIntervals.size(),
        "Must have equal number of points and times!");
    ghoul_assert(_points.size() > 2, "Minimum of two control points needed for interpolation!");

    size_t nrSegments = _points.size() - 1;
    const glm::dvec3 start = _points.front();
    const glm::dvec3 end = _points.back();

    if (u <= 0.0) return _points.front();
    if (u >= 1.0) return _points.back();

    if (nrSegments == 1) {
        return roundedSpline(u, start, start, end, end);
    }

    // compute current segment index
    std::vector<double>::const_iterator segmentEndIt =
        std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), u);
    unsigned int idx = (segmentEndIt - 1) - _parameterIntervals.begin();

    double segmentStart = _parameterIntervals[idx];
    double segmentDuration = (_parameterIntervals[idx + 1] - _parameterIntervals[idx]);
    double tScaled = (u - segmentStart) / segmentDuration;

    glm::dvec3 first = (idx == 0) ? start : _points[idx - 1];
    glm::dvec3 last = (idx == (nrSegments - 1)) ? end : _points[idx + 2];

    return roundedSpline(tScaled, first, _points[idx], _points[idx + 1], last);
}

glm::dvec3 AvoidCollisionCurve::roundedSpline(double t, const glm::dvec3 &a,
              const glm::dvec3 &b, const glm::dvec3 &c, const glm::dvec3 &d)
{
    ghoul_assert(t >= 0 && t <= 1.0, "Interpolation variable out of range [0, 1]");

    if (t <= 0.0) return b;
    if (t >= 1.0) return c;

    auto isNormalizable = [](const glm::dvec3 v) {
        return !(abs(glm::length(v)) < Epsilon);
    };

    // find velocities at b and c
    glm::dvec3 cb = c - b;
    glm::dvec3 bc = -cb;

    if (!isNormalizable(cb)) {
        return b;
    }

    glm::dvec3 ab = a - b;

    // a and b are the same point
    if (!isNormalizable(ab)) {
        ab = bc;
    }

    glm::dvec3 bVelocity = glm::normalize(cb) - glm::normalize(ab);
    bVelocity = isNormalizable(bVelocity) ?
        glm::normalize(bVelocity) : glm::dvec3(0.0, 1.0, 0.0);

    glm::dvec3 dc = d - c;

    // d and c are the same point
    if (!isNormalizable(dc)) {
        dc = cb;
    }

    glm::dvec3 cVelocity = glm::normalize(dc) - glm::normalize(bc);
    cVelocity = isNormalizable(cVelocity) ?
        glm::normalize(cVelocity) : glm::dvec3(0.0, 1.0, 0.0);

    double cbDistance = glm::length(cb);
    double tangetLength = cbDistance;

    // Distances in space can be extremely large, so we dont want the tangents to always have the full length.
    const double tangentlengthThreshold = 1E10; // TODO: What's a good threshold?? ALSO make global
    if (tangetLength > tangentlengthThreshold)
        tangetLength = tangentlengthThreshold;

    // the resulting curve gets much better and more predictable when using a genric
    // hermite curve compared to the Catmull Rom implementation
    return interpolation::hermite(t, b, c, bVelocity * tangetLength, cVelocity * tangetLength);

    //return interpolation::catmullRom(t, b - bVelocity * cbDistance, b, c, c + cVelocity * cbDistance);
}
} // namespace openspace::autonavigation
<commit_msg>Use rounded catmull-rom interpolatior instead<commit_after>/*****************************************************************************************
*                                                                                *
* OpenSpace                                                                             *
*                                                                                *
* Copyright (c) 2014-2019                                                               *
*                                                                                *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify,    *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
* permit persons to whom the Software is furnished to do so, subject to the following   *
* conditions:                                                                           *
*                                                                                *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software.                                              *
*                                                                                *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
****************************************************************************************/

#include <modules/autonavigation/avoidcollisioncurve.h>

#include <modules/autonavigation/helperfunctions.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/query/query.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/projection.hpp>
#include <algorithm>
#include <vector>

namespace {
    constexpr const char* _loggerCat = "AvoidCollisionCurve";
    const double Epsilon = 1E-7;

    // TODO: move this list somewhere else
    const std::vector<std::string> RelevantTags{
        "planet_solarSystem",
        "moon_solarSystem"
        // TODO
    };

} // namespace

namespace openspace::autonavigation {

AvoidCollisionCurve::AvoidCollisionCurve(const Waypoint& start, const Waypoint& end) {
    double thresholdFactor = 3.0;

    glm::dvec3 startNodePos = start.node()->worldPosition();
    glm::dvec3 endNodePos = end.node()->worldPosition();
    double startNodeRadius = start.nodeDetails.validBoundingSphere;
    glm::dvec3 startNodeToStartPos = start.position() - startNodePos;
    glm::dvec3 startViewDir = glm::normalize(start.rotation() * glm::dvec3(0.0, 0.0, -1.0));

    // Add control points for a catmull-rom spline, first and last will no be intersected
    // TODO: test other first and last control points, ex positive or negative view dir
    _points.push_back(startNodePos);
    _points.push_back(start.position());

    // Add an extra point to first go backwards if starting close to planet
    if (glm::length(startNodeToStartPos) < thresholdFactor * startNodeRadius) {
        double distance = startNodeRadius;

        glm::dvec3 newPos = start.position() - distance * startViewDir;
        _points.push_back(newPos);
    }

    // Add point for moving out if the end state is in opposite direction
    //TODO: determine if its best to compare to end orientation or position
    glm::dvec3 startToEnd = end.position() - start.position();
    double cosStartAngle = glm::dot(normalize(-startViewDir), normalize(startToEnd));
    if (cosStartAngle > 0.7) {
        glm::dquat middleRotation = glm::slerp(start.rotation(), end.rotation(), 0.5); // OBS! Rotation method is not necessarily slerp
        glm::dvec3 middleViewDir = glm::normalize(middleRotation * glm::dvec3(0.0, 0.0, -1.0));
        double distance = 0.4 * glm::length(startToEnd);

        glm::dvec3 newPos = start.position() + 0.2 * startToEnd - distance * middleViewDir;
        _points.push_back(newPos);
    }

    // TODO: Add a point to approach straigt towards a specific pose near planet
    // TODO: Calculate nice end pose if not defined

    _points.push_back(end.position());
    _points.push_back(endNodePos);

    std::vector<SceneGraphNode*> relevantNodes = findRelevantNodes();

    // Create extra points to avoid collision
    removeCollisions(relevantNodes);

    int nrSegments = _points.size() - 3;

    // default values for the curve parameter - equally spaced
    for (double t = 0.0; t <= 1.0; t += 1.0 / (double)nrSegments) {
        _parameterIntervals.push_back(t);
    }

    _length = arcLength(1.0);

    initParameterIntervals();
}

// Interpolate a list of control points and knot times
glm::dvec3 AvoidCollisionCurve::positionAt(double u) {
    if (u < 0.000001)
        return _points[1];
    if (u > 0.999999)
        return *(_points.end() - 2);

    //distribute t by segment arc lenghts

    // compute current segment, by first finding iterator to the first value that is larger than t
    std::vector<double>::iterator segmentEndIt =
        std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), u);
    unsigned int idx = (segmentEndIt - 1) - _parameterIntervals.begin();

    double segmentStart = _parameterIntervals[idx];
    double segmentDuration = (_parameterIntervals[idx + 1] - _parameterIntervals[idx]);
    double uSegment = (u - segmentStart) / segmentDuration;

    return interpolation::catmullRom(uSegment, _points[idx], _points[idx + 1], _points[idx + 2], _points[idx + 3], 1.0);

   // return interpolatePoints(u);
}

std::vector<SceneGraphNode*> AvoidCollisionCurve::findRelevantNodes() {
    // TODO: make more efficient
    const std::vector<SceneGraphNode*>& allNodes =
        global::renderEngine.scene()->allSceneGraphNodes();

    auto isRelevant = [](const SceneGraphNode* node) {
        // does the node match any of the relevant tags?
        const std::vector<std::string> tags = node->tags();
        auto result = std::find_first_of(RelevantTags.begin(), RelevantTags.end(), tags.begin(), tags.end());

        // does not match any tags => not interesting
        if (result == RelevantTags.end()) {
            return false;
        }

        return node->renderable() && (node->boundingSphere() > 0.0);
    };

    std::vector<SceneGraphNode*> result{};
    std::copy_if(allNodes.begin(), allNodes.end(), std::back_inserter(result), isRelevant);

    return result;
}

void AvoidCollisionCurve::removeCollisions(std::vector<SceneGraphNode*>& relevantNodes, int step) {
    int nrSegments = _points.size() - 3;
    int maxSteps = 10;

    // TODO: handle better / present warning if early out
    if (step > maxSteps) return;

    // go through all segments and check for collisions
    for (int i = 0; i < nrSegments; ++i) {
        const glm::dvec3 lineStart = _points[i + 1];
        const glm::dvec3 lineEnd = _points[i + 2];

        for (SceneGraphNode* node : relevantNodes) {
            // do collision check in relative coordinates, to avoid huge numbers
            const glm::dmat4 modelTransform = node->modelTransform();
            glm::dvec3 p1 = glm::inverse(modelTransform) * glm::dvec4(lineStart, 1.0);
            glm::dvec3 p2 = glm::inverse(modelTransform) * glm::dvec4(lineEnd, 1.0);

            // sphere to check for collision
            double bs = node->boundingSphere();
            double radius = bs;
            // TODO: add a buffer (i.e. sue a little larger sphere), when we have
            // behaviour for leaving a target. Don't want to pass too close to objects
            glm::dvec3 center = glm::dvec3(0.0, 0.0, 0.0);
            glm::dvec3 point;

            bool collision = helpers::lineSphereIntersection(p1, p2, center, radius, point);

            // convert back to world coordinates
            glm::dvec3 pointWorld = modelTransform * glm::dvec4(point, 1.0);

            if (collision) {
                LINFO(fmt::format("Collision with node: {}!", node->identifier()));

                // to avoid collision, take a step in an orhtogonal direction of the
                // collision point and add a new point
                glm::dvec3 lineDir = glm::normalize(lineEnd - lineStart);
                glm::dvec3 nodePos = node->worldPosition();
                glm::dvec3 collisionPointToCenter = nodePos - pointWorld;
                glm::dvec3 parallell = glm::proj(collisionPointToCenter, lineDir);
                glm::dvec3 orthogonal = collisionPointToCenter - parallell;

                double distance = 3.0 * radius;
                glm::dvec3 extraKnot = pointWorld - distance * glm::normalize(orthogonal);
                _points.insert(_points.begin() + i + 2, extraKnot);

                // TODO: maybe make this more efficient, and make sure that we have a base case.
                removeCollisions(relevantNodes, ++step);
                break;
            }
        }
    }
}

// compute curve parameter intervals based on relative arc length
void AvoidCollisionCurve::initParameterIntervals() {
    std::vector<double> newIntervals;
    int nrSegments = _points.size() - 3;
    for (double t = 0.0; t <= 1.0; t += 1.0 / (double)nrSegments) {
        newIntervals.push_back(arcLength(t) / _length);
    }
    _parameterIntervals.swap(newIntervals);
}
} // namespace openspace::autonavigation
<|endoftext|>


<commit_before>/**
 * @file		MessageBuffers.hpp
 * @ingroup bliss::io
 * @author	tpan
 * @brief   MessageBuffers base class and SendMessageBuffers subclass for buffering data for MPI send/receive
 * @details SendMessageBuffers is a collection of in-memory Buffers that containing data that
 *            1. will be sent to remote destinations, such as a MPI process
 *            2. was received from remote destinations, such as a MPI process
 *
 *          In contrast to BufferPool, whose purpose is to prevent local process from blocking waiting for a buffer to clear,
 *          MessageBuffer's purpose is to batch the data to be transmitted, using BufferPool to prevent local process from blocking.
 *
 *          As there may be several different patterns of interactions between MessageBuffers and other MessageBuffers, MessageBuffers and
 *          local thread/process, there is a base class MessageBuffers that acts as proxy to the underlying BufferPool (memory management).
 *          Two subclasses are planned:  SendMessageBuffers and RecvMessageBuffers, but only SendMessageBuffers has been implemented.
 *
 *          Envisioned patterns of interaction:
 *            Send:     MessageBuffers -> mpi send
 *            Recv:     mpi recv -> MessageBuffers
 *            Forward:  mpi recv -> mpi send
 *            Local:    MessageBuffers -> MessageBuffers
 *
 *          The SendMessageBuffers accumulates data in its internal buffer, and notifies the caller when a buffer is full.
 *
 *          The (unimplemented) RecvMessageBuffers requires an event notification mechanism that works with callbacks to handle the received messages.
 *          This is currently implemented in CommunicationLayer.
 *
 *
 * Copyright (c) 2014 Georgia Institute of Technology.  All Rights Reserved.
 *
 * TODO add License
 */
#ifndef MESSAGEBUFFERS_HPP_
#define MESSAGEBUFFERS_HPP_

#include <vector>
#include <type_traits>
#include <typeinfo>
#include <iostream>
#include <sstream>
#include <mutex>
#include <condition_variable>
#include <xmmintrin.h>

#include "config.hpp"
#include "concurrent/buffer.hpp"
#include "concurrent/object_pool.hpp"
#include <atomic>

#include <iterator> // for ostream_iterator

namespace bliss
{
  namespace io
  {

    /**
     * @class			MessageBuffers
     * @brief     a data structure to buffering/batching messages for communication
     * @details   Templated base class for a collection of buffers that are used for communication.
     *
     *
     *            The class contains a BufferPool, which contains an array of reusable Buffer objects for in-memory storage.
     *
     * @tparam LockType   Indicates whether the class should be thread safe or not.
     */
    template<bliss::concurrent::LockType PoolLT, bliss::concurrent::LockType BufferLT = PoolLT, int64_t BufferCapacity = 8192>
    class MessageBuffers
    {
        // TODO: move consturctor and assignment operator to copy between thread safety levels.
      public:

        typedef Buffer<BufferLT, BufferCapacity> BufferType;

      protected:

        /// Internal BufferPool Type.  typedefed only to shorten the usage.
        typedef typename bliss::io::ObjectPool<PoolLT, BufferType>  BufferPoolType;

      public:
        /// IdType of a Buffer, aliased from BufferPool
        typedef typename BufferPoolType::ObjectPtrType            BufferPtrType;

      protected:
        /// a pool of in-memory Buffers for storage.
        BufferPoolType pool;

        /**
         * Gets the capacity of the Buffer instances.
         * @return    capacity of each buffer.
         */
        const size_t getBufferCapacity() const {
          return BufferCapacity;
        }

        /**
         * @brief Constructor.  creates a buffer given a specified per-buffer capacity, and the BufferPool capacity.
         * @note  Protected so base class function is not called directly.
         *
         *        limit BufferPool to have unlimited capacity, so as to ensure that we don't get nullptrs and thus don't need to
         *        check in code.
         *
         *
         * @param buffer_capacity   the Buffer's maximum capacity.  default to 8192.
         * @param pool_capacity     the BufferPool's capacity.  default to unlimited.
         */
        explicit MessageBuffers() : pool() {};  // unlimited size pool

        /**
         * @brief default copy constructor.  deleted.  since internal BufferPool does not allow copy construction/assignment.
         * @note Protected so base class function is not called directly.
         *
         * @param other     source MessageBuffers to copy from
         */
        explicit MessageBuffers(const MessageBuffers<PoolLT, BufferLT, BufferCapacity>& other) = delete;

        /**
         * @brief default copy assignment operator.  deleted.   since internal BufferPool does not allow copy construction/assignment.
         * @note Protected so base class function is not called directly.
         *
         * @param other     source MessageBuffers to copy from
         * @return          self.
         */
        MessageBuffers<PoolLT, BufferLT, BufferCapacity>& operator=(const MessageBuffers<PoolLT, BufferLT, BufferCapacity>& other) = delete;

        /**
         * @brief default move constructor.
         * @note Protected so base class function is not called directly.
         *
         * @param other     source MessageBuffers to move from
         */
        explicit MessageBuffers(MessageBuffers<PoolLT, BufferLT, BufferCapacity>&& other) : pool(std::move(other.pool)) {};

        /**
         * @brief default move assignment operator.
         * @note Protected so base class function is not called directly.
         *
         * @param other     source MessageBuffers to move from
         * @return          self.
         */
        MessageBuffers<PoolLT, BufferLT, BufferCapacity>& operator=(MessageBuffers<PoolLT, BufferLT, BufferCapacity>&& other) {
          pool = std::move(other.pool);
          return *this;
        }

      public:
        /**
         * @brief default destructor
         */
        virtual ~MessageBuffers() {};

        /**
         * @brief Releases a Buffer by it's BufferPool id, after the buffer is no longer needed.
         * @note  this should be called on a buffer that is not being used by MessageBuffers, i.e. flush does not satisfy this.
         *
         * @note  This to be called after the communication logic is done with the Send or Recv buffer.
         *
         * @param id    Buffer's id (assigned from BufferPool)
         */
        virtual void releaseBuffer(BufferPtrType ptr) {
          pool.releaseObject(ptr);
        }

      protected:
        /**
         * @brief Convenience method to release and clear all current Buffers in the pool.
         */
        virtual void reset() {
          pool.reset();
        }
    };

    /**
     * @class SendMessageBuffers
     * @brief SendMessageBuffers is a subclass of MessageBuffers designed to manage the actual buffering of data for a set of messaging targets.
     * @details
     *
     *  The class is designed with the following requirements in mind:
     *  1. data is destined for some remote process identifiable by a process id (e.g. rank)
     *  2. data is appended to buffer incrementally and with minimal blocking
     *  3. calling thread has a mechanism to process a full buffer. e.g. send it
     *  4. all data in a SendMessageBuffers are homogeneously typed.
     *
     *  The class is implemented to support the requirement:
     *  1. SendMessageBuffers is not aware of its data's type or metadata specifying its type.
     *  2. SendMessageBuffers uses the base MessageBuffers class' internal BufferPool to reuse memory
     *  3. SendMessageBuffers stores a vector of Buffer Ids (as assigned from BufferPool), thus mapping from process Rank to BufferId.
     *  4. SendMessageBuffers provides an Append function to incrementally add data to the Buffer object for the targt process rank.
     *  5. When a particular buffer is full, return the Buffer Id to the calling thread for it to process (send), and swap in an empty
     *      Buffer from BufferPool.
     *
     *  An internal vector is used to map from target process rank to Buffer pointer.  For multithreading flavors of message buffers,
     *    atomic<BufferType*> is used.
     *
     *  @note
     *  pool size is unlimited so we would not get nullptr, and therefore do not need to check it.
     *
     * @note
     *  For Buffers that are full, the calling thread will need to track the full buffers' Ids as this class evicts a full Buffer's id from it's
     *    vector of Buffer Ids.  The Calling Thread can do t his via a queue, as in the case of CommunicationLayer.
     *  Note that a Buffer is "in use" from the first call to the Buffer's append function to the completion of the send (e.g. via MPI Isend)
     *
     *  @tparam LockType    determines if this class should be thread safe
     */
    template<bliss::concurrent::LockType PoolLT, bliss::concurrent::LockType BufferLT = PoolLT, int64_t BufferCapacity = 8192>
    class SendMessageBuffers : public MessageBuffers<PoolLT, BufferLT, BufferCapacity>
    {
      public:
        /// Id type of the Buffers
        using BufferType = typename MessageBuffers<PoolLT, BufferLT, BufferCapacity>::BufferType;
        using BufferPtrType = typename MessageBuffers<PoolLT, BufferLT, BufferCapacity>::BufferPtrType;

        /// the BufferPoolType from parent class
        using BufferPoolType = typename MessageBuffers<PoolLT, BufferLT, BufferCapacity>::BufferPoolType;

      protected:

        /// Vector of pointers (atomic).  Provides a mapping from process id (0 to vector size), to bufferPtr (from BufferPool)
        /// using vector of atomic pointers allows atomic swap of each pointer, without array of mutex or atomic_flags.
        typedef typename std::conditional<PoolLT == bliss::concurrent::LockType::NONE, BufferType*, std::atomic<BufferType*> >::type BufferPtrTypeInternal;
        std::vector< BufferPtrTypeInternal > buffers;

        /// for synchronizing access to buffers (and pool).
        mutable std::mutex mutex;
        mutable std::atomic_flag spinlock;

      private:

        // private copy contructor
        SendMessageBuffers(SendMessageBuffers&& other, const std::lock_guard<std::mutex>&) :
          MessageBuffers<PoolLT, BufferLT, BufferCapacity>(std::move(other)), buffers(std::move(other.buffers))
          {};

        /**
         * @brief default copy constructor.  deleted.
         * @param other   source SendMessageBuffers to copy from.
         */
        explicit SendMessageBuffers(const SendMessageBuffers<PoolLT, BufferLT, BufferCapacity> &other) = delete;

        /**
         * @brief default copy assignment operator, deleted.
         * @param other   source SendMessageBuffers to copy from.
         * @return        self
         */
        SendMessageBuffers<PoolLT, BufferLT, BufferCapacity>& operator=(const SendMessageBuffers<PoolLT, BufferLT, BufferCapacity> &other) = delete;

        /**
         * @brief Default constructor, deleted
         */
        SendMessageBuffers() = delete;

      public:
        /**
         * @brief Constructor.
         * @param numDests         The number of messaging targets/destinations
         * @param bufferCapacity   The capacity of the individual buffers.  default 8192.
         * @param poolCapacity     The capacity of the pool.  default unbounded.
         */
        explicit SendMessageBuffers(const int & numDests) :
          MessageBuffers<PoolLT, BufferLT, BufferCapacity>(), buffers(numDests)
        {
          this->reset();
        };

        /**
         * default move constructor.  calls superclass move constructor first.
         * @param other   source SendMessageBuffers to move from.
         */
        explicit SendMessageBuffers(SendMessageBuffers<PoolLT, BufferLT, BufferCapacity> && other) :
            SendMessageBuffers(std::forward<SendMessageBuffers<PoolLT, BufferLT, BufferCapacity> >(other), std::lock_guard<std::mutex>(other.mutex)) {};

        /**
         * @brief default move assignment operator.
         * @param other   source SendMessageBuffers to move from.
         * @return        self
         */
        SendMessageBuffers<PoolLT, BufferLT, BufferCapacity>& operator=(SendMessageBuffers<PoolLT, BufferLT, BufferCapacity> && other) {
          std::unique_lock<std::mutex> mine(mutex, std::defer_lock),
                                          hers(other.mutex, std::defer_lock);
          std::lock(mine, hers);

          this->pool = std::move(other.pool);
          buffers.clear(); std::swap(buffers, other.buffers);

          return *this;
        }

        /**
         * @brief default destructor
         */
        virtual ~SendMessageBuffers() {
          buffers.clear();
        };

        /**
         * @brief get the number of buffers.  should be same as number of targets for messages
         * @return    number of buffers
         */
        const size_t getSize() const {
          return buffers.size();
        }

        /**
         * @brief get a raw pointer to the the BufferId that a messaging target maps to.
         * @details for simplicity, not distinguishing between thread safe and unsafe versions
         *
         * @param targetRank   the target id for the messages.
         * @return      reference to the unique_ptr.  when swapping, the content of the unique ptrs are swapped.
         */
//        template<bliss::concurrent::LockType LT = PoolLT>
//        const typename std::enable_if<LT != bliss::concurrent::LockType::NONE, BufferType*>::type at(const int targetRank) const {
//          return buffers.at(targetRank).load();   // can use reference since we made pool unlimited, so never get nullptr
//        }
        BufferType* at(const int targetRank) const {
          return (BufferType*)(buffers.at(targetRank));   // if atomic pointer, then will do load()
        }

        BufferType* operator[](const int targetRank) const {
          return at(targetRank);
        }

        /**
         * @brief Reset the current MessageBuffers instance by first clearing its list of Buffer Ids, then repopulate it from the pool.
         * @note  One thread only should call this.
         */
        virtual void reset() {

          std::lock_guard<std::mutex> lock(mutex);
          int count = buffers.size();
          // release all buffers back to pool
          for (int i = 0; i < count; ++i) {
            if (this->at(i)) {
              this->at(i)->block_and_flush();
              this->pool.releaseObject(this->at(i));
              buffers.at(i) = nullptr;
            }
          }
          // reset the pool. local vector should contain a bunch of nullptrs.
          this->pool.reset();
//          buffers.clear();
//          buffers.resize(count);

          // populate the buffers from the pool
          for (int i = 0; i < count; ++i) {
            printf("initializing buffer %p \n", this->at(i));
            swapInEmptyBuffer<PoolLT>(i);
            printf("initialized buffer %p blocked? %s, empty? %s\n", this->at(i), this->at(i)->is_read_only()? "y" : "n", this->at(i)->isEmpty() ? "y" : "n");

          }
        }

        /** thread safety:
         * called by commlayer's send/recv thread, so single threaded there
         * called by sendMessageBuffer's swapInEmptyBuffer
         *    if threadsafe version of sendMessageBuffer, then the pointer to buffer to be released will be unique.
         *    if not threadsafe version, then the sendMessageBuffer should be used by a single threaded program, so buffer to release should be unique.
         *
         *    overall, do not need to worry about multiple threads releasing the same buffer.
         */
        virtual void releaseBuffer(BufferPtrType ptr) {
          if (ptr) {
            ptr->block_and_flush();  // if already blocked and flushed, no op.

            MessageBuffers<PoolLT, BufferLT, BufferCapacity>::releaseBuffer(ptr);
          }
        }

        /**
         * @brief Appends data to the target message buffer.
         * @details   internally will try to swap in a new buffer when full, and notify the caller of the full buffer's id.
         * need to return 2 things:
         *  1. success/failure of current insert
         *  2. indicator that there is a full buffer (the full buffer's id).
         *
         * Notes:
         *  on left side -
         *    targetBufferId is obtained outside of CAS, so when CAS is called to update bufferIdForProcId[dest], it may be different already  (on left side)
         *  on right side -
         *  fullBufferId and bufferId[dest] are set atomically, but newTargetBufferId is obtained outside of CAS, so the id of the TargetBufferId
         *  may be more up to date than the CAS function results, which is okay.  Note that a fullBuffer will be marked as blocked to prevent further append.
         *
         * Table of values and compare exchange behavior (called when a buffer if full and an empty buffer is swapped in.)
         *  targetBufferId,     bufferIdForProcId[dest],    newBufferId (from pool) ->  fullBufferId,     bufferId[dest],   newTargetBufferId
         *  x                   x                   y                           x                 y                 y
         *  x                   x                   -1                          x                 -1                -1
         *  x                   z                   y                           -1                z                 z
         *  x                   z                   -1                          -1                z                 z
         *  x                   -1                  y                           -1                -1                -1
         *  x                   -1                  -1                          -1                -1                -1
         *  -1                  -1                  y                           -1                y                 y
         *  -1                  -1                  -1                          -1                -1                -1
         *  -1                  z                   y                           -1                -1                -1
         *  -1                  z                   -1                          -1                -1                -1
         *
         * @note              if failure, data to be inserted would need to be handled by the caller.
         *
         * @param[in] data    data to be inserted, as byteArray
         * @param[in] count   number of bytes to be inserted in the the Buffer
         * @param[in] dest    messaging target for the data, decides which buffer to append into.
         * @return            std::pair containing the status of the append (boolean success/fail), and the id of a full buffer if there is one.
         */
        std::pair<bool, BufferType*> append(const void* data, const size_t count, const int targetProc) {

          //== if data being set is null, throw error
          if (data == nullptr || count <= 0) {
            throw (std::invalid_argument("ERROR: calling MessageBuffer append with nullptr"));
          }

          //== if there is not enough room for the new data in even a new buffer, LOGIC ERROR IN CODE: throw exception
          if (count > this->getBufferCapacity()) {
            throw (std::invalid_argument("ERROR: messageBuffer append with count exceeding Buffer capacity"));
          }

          //== if targetProc is outside the valid range, throw an error
          if (targetProc < 0 || targetProc > getSize()) {
            throw (std::invalid_argument("ERROR: messageBuffer append with invalid targetProc"));
          }

          // NOTE: BufferPool is unlimited in size, so don't need to check for nullptr, can just append directly.   is append really atomic?
          // question is what happens in unique_ptr when dereferencing the internal pointer - if the dereference happens before a unique_ptr swap
          // from swapInEmptyBuffer, then append would use the old object.  however, it was probably swapped because it's full,
          // or flushing, so lock_read() would have been set for the full case.  now it depends on what happens in append.
          // if flush, then block is set just before swap, so it's more likely this thread enters append without a block.

          unsigned int appendResult = this->at(targetProc)->append(data, count);

//          printf("buffer blocked? %s, empty? %s\n", this->at(targetProc)->is_read_only()? "y" : "n", this->at(targetProc)->isEmpty() ? "y" : "n");

          // now if appendResult is false, then we return false, but also swap in a new buffer.
          // conditions are either full buffer, or blocked buffer.
          // this call will mark the fullBuffer as blocked to prevent multiple write attempts while flushing the buffer
          // ideally, we want it to be a reference that gets updated for all threads.

          if (appendResult & 0x2) { // only 1 thread gets 0x2 result for a buffer.  all other threads either writes successfully or fails.

            return std::move(std::make_pair(appendResult & 0x1, swapInEmptyBuffer<PoolLT>(targetProc) ) );

          } else {
            //if (preswap != postswap) printf("ERROR: NOSWAP: preswap %p and postswap %p are not the same.\n", preswap, postswap);
            return std::move(std::make_pair(appendResult & 0x1, nullptr));
          }

        }

        /**
         * flushes the buffer at rank targetProc.
         *
         * @note  only 1 thread should call this per proc.
         *        multiple calls to this may be needed to get all data that are written to the new empty buffer.
         *
         * @param targetProc
         * @return
         */
        BufferType* flushBufferForRank(const int targetProc) {
          //== if targetProc is outside the valid range, throw an error
          if (targetProc < 0 || targetProc > getSize()) {
            throw (std::invalid_argument("ERROR: messageBuffer append with invalid targetProc"));
          }

//          // block the old buffer (when append full, will block as well).  each proc has a unique buffer.
//          this->at(targetProc)->block_and_flush();  // this part can be concurrent.
//
          // passing in getBufferIdForRank result to ensure atomicity.
          // may return ABSENT if there is no available buffer to use.
          auto old = swapInEmptyBuffer<PoolLT>(targetProc);
          if (old) old->block_and_flush();
          return old;
        }

      protected:

        /**
         * @brief  Swap in an empty Buffer from BufferPool at the dest location in MessageBuffers.  The old buffer is returned.
         * @details The new buffer may be BufferPoolType::ABSENT (invalid buffer, when there is no available Buffer from pool)
         *
         * Effect:  bufferIdForProcId[dest] gets a new Buffer Id, full Buffer is set to "blocked" to prevent further append.
         *
         * This is the THREAD SAFE version.   Uses std::compare_exchange_strong to ensure that another thread hasn't swapped in a new Empty one already.
         *
         * @note we make the caller get the new bufferIdForProcId[dest] directly instead of returning by reference in the method parameter variable
         *  because this way there is less of a chance of race condition if a shared "old" variable was accidentally used.
         *    and the caller will likely prefer the most up to date bufferIdForProcId[dest] anyway.
         *
         * @note  below is now relevant any more now that message buffer hold buffer ptrs and pool is unlimited.. we only have line 5 here.
         * 	old is 		old assignment	action
         *    ABSENT      available     not a valid case
         *    ABSENT      at dest       swap, return ABS
         *    ABSENT      not at dest   dest is already swapped.  no op, return ABS
         *    not ABS     available     not used.  block old, no swap. return ABS
         *    not ABS     at dest       swap, block old.  return old
         *    not ABS     not at dest   being used.  no op. return ABS
         *
         *
         * @note:  exchanges buffer at dest atomically.
         * @note:  because only thread that just fills a buffer will do exchange, only 1 thread does exchange at a time.  can rely on pool's Lock to get one new buffer
         *          can be assured that each acquired new buffer will be  used, and can ascertain that no 2 threads will replace the same full buffer.
         *
         * @tparam        used to choose thread safe vs not verfsion of the method.
         * @param dest    position in bufferIdForProcId array to swap out
         * @return        the BufferId that was swapped out.
         */
        template<bliss::concurrent::LockType LT = PoolLT>
        typename std::enable_if<LT != bliss::concurrent::LockType::NONE, BufferType*>::type swapInEmptyBuffer(const int dest) {

          auto ptr = this->pool.tryAcquireObject();
          ptr->clear_and_unblock_writes();
          auto oldbuf = buffers.at(dest).exchange(ptr);
          if (oldbuf && oldbuf->isEmpty()) {
//            printf("oldbuf %p, blocked? %s\n", oldbuf, oldbuf->is_read_only() ? "y" : "n");
            releaseBuffer(oldbuf);
            return nullptr;
          }
          else return oldbuf;
        }

        /**
         * @brief Swap in an empty Buffer from BufferPool at the dest location in MessageBuffers.  The old buffer is returned.
         * @details The new buffer may be BufferPoolType::ABSENT (invalid buffer, when there is no available Buffer from pool)
         *
         * effect:  bufferIdForProcId[dest] gets a new Buffer Id, full Buffer is set to "blocked" to prevent further append.
         *
         * this is the THREAD UNSAFE version

         * @note we make the caller get the new bufferIdForProcId[dest] directly instead of returning by reference in the method parameter variable
         *  because this way there is less of a chance of race condition if a shared "old" variable was accidentally used.
         *    and the caller will likely prefer the most up to date bufferIdForProcId[dest] anyway.
         *
         * @note
         *    ABSENT      available     not a valid case
         *    ABSENT      at dest       swap, return ABS
         *    ABSENT      not at dest   dest is already swapped.  no op, return ABS
         *    not ABS     available     not used.  block old, no swap. return ABS
         *    not ABS     at dest       swap, block old.  return old
         *    not ABS     not at dest   being used.  no op. return ABS
         *
         * @note:  requires that the queue at dest to be blocked.
         *
         *
         * @tparam LT     used to choose thread safe vs not verfsion of the method.
         * @param dest    position in bufferIdForProcId array to swap out
         * @return        the BufferId that was swapped out.
         */
        template<bliss::concurrent::LockType LT = PoolLT>
        typename std::enable_if<LT == bliss::concurrent::LockType::NONE, BufferType*>::type swapInEmptyBuffer(const int dest) {

          auto oldbuf = this->at(dest);
          buffers.at(dest) = this->pool.tryAcquireObject();
          buffers.at(dest)->clear_and_unblock_writes();
          if (oldbuf && oldbuf->isEmpty()) {
//            printf("oldbuf %p, empty? %s\n", oldbuf, oldbuf->isEmpty() ? "y" : "n");

            releaseBuffer(oldbuf);
            return nullptr;
          }
          else return oldbuf;  // swap the pointer to Buffer object, not Buffer's internal "data" pointer
        }

    };

  } /* namespace io */
} /* namespace bliss */

#endif /* MESSAGEBUFFERS_HPP_ */
<commit_msg>DEL: no need for thread_local_message_buffers class<commit_after><|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* 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, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "RequiredPlugin.h"
#include <sofa/core/ObjectFactory.h>
#include <sofa/helper/system/PluginManager.h>

namespace sofa
{

namespace component
{

namespace misc
{

SOFA_DECL_CLASS(RequiredPlugin)

int RequiredPluginClass = core::RegisterObject("Load required plugin")
        .add< RequiredPlugin >();

RequiredPlugin::RequiredPlugin()
    : pluginName( initData(&pluginName, "pluginName", "Name of the plugin to loaded. If this is empty, the name of this component is used as plugin name."))
{
    this->f_printLog.setValue(true); // print log by default, to identify which pluging is responsible in case of a crash during loading
}

void RequiredPlugin::parse(sofa::core::objectmodel::BaseObjectDescription* arg)
{
    Inherit1::parse(arg);
    if (!pluginName.getValue().empty() || !name.getValue().empty())
        loadPlugin();
}

void RequiredPlugin::loadPlugin()
{
    std::string pluginPath = pluginName.getValue();
    if(pluginPath.empty()) pluginPath = name.getValue();

    std::string mainPluginPath = pluginPath;

    sout << "Loading " << pluginPath << sendl;
    if (sofa::helper::system::PluginManager::getInstance().loadPlugin(mainPluginPath)) // mainPluginPath is modified here...
    {
        sout << "Loaded " << mainPluginPath << sendl;
        sofa::helper::system::PluginManager::getInstance().init();
    }

    // try to load the eventual plugin gui
    pluginPath = pluginPath + "_gui";
    if (sofa::helper::system::PluginManager::getInstance().loadPlugin(pluginPath,NULL))
    {
        sout << "Loaded " << pluginPath << sendl;
        sofa::helper::system::PluginManager::getInstance().init();
    }
}

}

}

}
<commit_msg>r10514/sofa : plugin_gui cleaner fix<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* 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, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "RequiredPlugin.h"
#include <sofa/core/ObjectFactory.h>
#include <sofa/helper/system/PluginManager.h>

namespace sofa
{

namespace component
{

namespace misc
{

SOFA_DECL_CLASS(RequiredPlugin)

int RequiredPluginClass = core::RegisterObject("Load required plugin")
        .add< RequiredPlugin >();

RequiredPlugin::RequiredPlugin()
    : pluginName( initData(&pluginName, "pluginName", "Name of the plugin to loaded. If this is empty, the name of this component is used as plugin name."))
{
    this->f_printLog.setValue(true); // print log by default, to identify which pluging is responsible in case of a crash during loading
}

void RequiredPlugin::parse(sofa::core::objectmodel::BaseObjectDescription* arg)
{
    Inherit1::parse(arg);
    if (!pluginName.getValue().empty() || !name.getValue().empty())
        loadPlugin();
}

void RequiredPlugin::loadPlugin()
{
    if(pluginName.getValue().empty()) pluginName.setValue( name.getValue() );

    std::string pluginPath = pluginName.getValue();

    sout << "Loading " << pluginPath << sendl;
    if (sofa::helper::system::PluginManager::getInstance().loadPlugin(pluginPath)) // pluginPath is modified here
    {
        sout << "Loaded " << pluginPath << sendl;
        sofa::helper::system::PluginManager::getInstance().init();
    }

    // try to load the eventual plugin gui
    pluginPath = pluginName.getValue() + "_gui";
    if (sofa::helper::system::PluginManager::getInstance().loadPlugin(pluginPath,NULL))
    {
        sout << "Loaded " << pluginPath << sendl;
        sofa::helper::system::PluginManager::getInstance().init();
    }
}

}

}

}
<|endoftext|>