<commit_before>#include <io/network/tcpSocket.h>
#include <logging.h>

#ifdef __WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
static constexpr int flags = 0;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
static constexpr int flags = MSG_NOSIGNAL;
#endif

extern "C" {
    struct X509;
    struct X509_STORE;
    struct SSL_CTX;
    struct SSL_METHOD;
    struct SSL;

    static X509_STORE* (*X509_STORE_new)();
    static X509* (*d2i_X509)(X509**, const unsigned char **, long);
    static int (*X509_STORE_add_cert)(X509_STORE*, X509*);
    static void (*X509_free)(X509*);
    static SSL_CTX* (*SSL_CTX_new)(const SSL_METHOD*);
    static const SSL_METHOD* (*TLSv1_2_client_method)();
    static long (*SSL_CTX_set_options)(SSL_CTX*, long);
    static int (*SSL_CTX_set_default_verify_paths)(SSL_CTX*);
    static void (*SSL_CTX_set_cert_store)(SSL_CTX*, X509_STORE*);
    static SSL* (*SSL_new)(SSL_CTX*);
    static int (*SSL_set_fd)(SSL *ssl, int fd);
    static int (*SSL_connect)(SSL *ssl);
    static long (*SSL_get_verify_result)(const SSL *ssl);
    static int (*SSL_read)(SSL *ssl, void *buf, int num);
    static int (*SSL_write)(SSL *ssl, const void *buf, int num);
    static void (*SSL_free)(SSL *ssl);

    static SSL_CTX* ssl_context;
}

# define SSL_OP_NO_SSLv2                                 0x01000000L
# define SSL_OP_NO_SSLv3                                 0x02000000L
# define SSL_OP_NO_TLSv1                                 0x04000000L
# define SSL_OP_NO_TLSv1_2                               0x08000000L
# define SSL_OP_NO_TLSv1_1                               0x10000000L

#include "dynamicLibrary.h"

static std::unique_ptr<DynamicLibrary> libcrypto;
static std::unique_ptr<DynamicLibrary> libssl;

static void initializeLibSSL()
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

#ifdef __WIN32
    libcrypto = DynamicLibrary::open("libcrypto-1_1.dll");
    libssl = DynamicLibrary::open("libssl-1_1.dll");
#else
    libcrypto = DynamicLibrary::open("libcrypto.so.1.1");
    libssl = DynamicLibrary::open("libssl.so.1.1");
#endif
    if (!libcrypto || !libssl)
        return;

    X509_STORE_new = libcrypto->getFunction<X509_STORE*(*)()>("X509_STORE_new");
    d2i_X509 = libcrypto->getFunction<X509* (*)(X509**, const unsigned char **, long)>("d2i_X509");
    X509_STORE_add_cert = libcrypto->getFunction<int (*)(X509_STORE*, X509*)>("X509_STORE_add_cert");
    X509_free = libcrypto->getFunction<void (*)(X509*)>("X509_free");

    SSL_CTX_new = libssl->getFunction<SSL_CTX*(*)(const SSL_METHOD*)>("SSL_CTX_new");
    TLSv1_2_client_method = libssl->getFunction<const SSL_METHOD* (*)()>("TLSv1_2_client_method");
    SSL_CTX_set_options = libssl->getFunction<long (*)(SSL_CTX*, long)>("SSL_CTX_set_options");
    SSL_CTX_set_default_verify_paths = libssl->getFunction<int (*)(SSL_CTX*)>("SSL_CTX_set_default_verify_paths");
    SSL_CTX_set_cert_store = libssl->getFunction<void (*)(SSL_CTX*, X509_STORE*)>("SSL_CTX_set_cert_store");
    SSL_new = libssl->getFunction<SSL* (*)(SSL_CTX*)>("SSL_new");
    SSL_set_fd = libssl->getFunction<int (*)(SSL *ssl, int fd)>("SSL_set_fd");
    SSL_connect = libssl->getFunction<int (*)(SSL *ssl)>("SSL_connect");
    SSL_get_verify_result = libssl->getFunction<long (*)(const SSL *ssl)>("SSL_get_verify_result");
    SSL_read = libssl->getFunction<int (*)(SSL *ssl, void *buf, int num)>("SSL_read");
    SSL_write = libssl->getFunction<int (*)(SSL *ssl, const void *buf, int num)>("SSL_write");
    SSL_free = libssl->getFunction<void (*)(SSL *ssl)>("SSL_free");

#ifdef __WIN32
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = NULL;
    X509 *x509;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(0, "ROOT");
    while((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
    {
        const unsigned char* c = pContext->pbCertEncoded;
        x509 = d2i_X509(nullptr, &c, pContext->cbCertEncoded);
        if (x509)
        {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }
    CertFreeCertificateContext(pContext);
    CertCloseStore(hStore, 0);
#endif

    ssl_context = SSL_CTX_new(TLSv1_2_client_method());
    SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#ifdef __WIN32
    SSL_CTX_set_cert_store(ssl_context, store);
#else
    SSL_CTX_set_default_verify_paths(ssl_context);
#endif
}

namespace sp {
namespace io {
namespace network {

TcpSocket::TcpSocket()
: ssl_handle(nullptr)
{
}

TcpSocket::TcpSocket(TcpSocket&& socket)
{
    *this = std::move(socket);
}

TcpSocket::~TcpSocket()
{
    close();
}

TcpSocket& TcpSocket::operator=(TcpSocket&& socket)
{
    close();

    handle = socket.handle;
    ssl_handle = socket.ssl_handle;
    send_queue = std::move(socket.send_queue);
    blocking = socket.blocking;
    receive_buffer = std::move(socket.receive_buffer);
    received_size = socket.received_size;

    socket.handle = -1;
    socket.send_queue.clear();
    socket.receive_buffer.clear();
    socket.received_size = 0;
    socket.ssl_handle = nullptr;

    return *this;
}

bool TcpSocket::connect(const Address& host, int port)
{
    if (isConnected())
        close();

    for(const auto& addr_info : host.addr_info)
    {
        handle = ::socket(addr_info.family, SOCK_STREAM, 0);
        if (handle < 0)
            return false;
        if (addr_info.family == AF_INET && sizeof(struct sockaddr_in) == addr_info.addr.length())
        {
            struct sockaddr_in server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.length());
            server_addr.sin_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
            {
                setBlocking(blocking);
                return true;
            }
        }
        if (addr_info.family == AF_INET6 && sizeof(struct sockaddr_in6) == addr_info.addr.length())
        {
            struct sockaddr_in6 server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.length());
            server_addr.sin6_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
            {
                setBlocking(blocking);
                return true;
            }
        }
        close();
    }
    return false;
}

bool TcpSocket::connectSSL(const Address& host, int port)
{
    if (!connect(host, port))
        return false;
    initializeLibSSL();
    if (!SSL_new)
    {
        LOG(Warning, "Failed to connect SSL socket due to missing libssl/libcrypto v1.1");
        close();
        return false;
    }

    ssl_handle = SSL_new(ssl_context);
    SSL_set_fd(static_cast<SSL*>(ssl_handle), handle);
    if (!SSL_connect(static_cast<SSL*>(ssl_handle)))
    {
        LOG(Warning, "Failed to connect SSL socket due to SSL negotiation failure.");
        close();
        return false;
    }
    if (SSL_get_verify_result(static_cast<SSL*>(ssl_handle)) != 0)
    {
        LOG(Warning, "Failed to connect SSL socket due to certificate verfication failure.");
        close();
        return false;
    }
    return true;
}

void TcpSocket::setDelay(bool delay)
{
    int mode = delay ? 0 : 1;
    setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, (char*)&mode, sizeof(mode));
}

void TcpSocket::close()
{
    if (isConnected())
    {
#ifdef __WIN32
        closesocket(handle);
#else
        ::close(handle);
#endif
        handle = -1;
        send_queue.clear();
        if (ssl_handle)
            SSL_free(static_cast<SSL*>(ssl_handle));
        ssl_handle = nullptr;
    }
}

bool TcpSocket::isConnected()
{
    return handle != -1;
}

void TcpSocket::send(const void* data, size_t size)
{
    if (!isConnected())
        return;
    if (sendSendQueue())
    {
        queue(data, size);
        return;
    }

    for(size_t done = 0; done < size; )
    {
        int result;
        if (ssl_handle)
            result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(data) + done, size - done);
        else
            result = ::send(handle, static_cast<const char*>(data) + done, size - done, flags);
        if (result < 0)
        {
            if (!isLastErrorNonBlocking())
                close();
            else
                send_queue += std::string(static_cast<const char*>(data) + done, size - done);
            return;
        }
        done += result;
    }
}

void TcpSocket::queue(const void* data, size_t size)
{
    send_queue += std::string(static_cast<const char*>(data), size);
}

size_t TcpSocket::receive(void* data, size_t size)
{
    sendSendQueue();

    if (!isConnected())
        return 0;

    int result;
    if (ssl_handle)
        result = SSL_read(static_cast<SSL*>(ssl_handle), static_cast<char*>(data), size);
    else
        result = ::recv(handle, static_cast<char*>(data), size, flags);
    if (result < 0)
    {
        result = 0;
        if (!isLastErrorNonBlocking())
            close();
    }
    return result;
}

void TcpSocket::send(const io::DataBuffer& buffer)
{
    io::DataBuffer packet_size(uint32_t(buffer.getDataSize()));
    send(packet_size.getData(), packet_size.getDataSize());
    send(buffer.getData(), buffer.getDataSize());
}

void TcpSocket::queue(const io::DataBuffer& buffer)
{
    io::DataBuffer packet_size(uint32_t(buffer.getDataSize()));
    queue(packet_size.getData(), packet_size.getDataSize());
    queue(buffer.getData(), buffer.getDataSize());
}

bool TcpSocket::receive(io::DataBuffer& buffer)
{
    if (!isConnected())
        return 0;

    if (receive_buffer.size() == 0)
    {
        uint8_t size_buffer[sizeof(uint32_t)];
        size_t idx = 0;
        while(idx < sizeof(uint32_t))
        {
            //TOFIX: This blocks if we receive less then 4 bytes. Allows denial of service attack.
            int result;
            if (ssl_handle)
                result = SSL_read(static_cast<SSL*>(ssl_handle), reinterpret_cast<char*>(&size_buffer[idx]), sizeof(uint32_t) - idx);
            else
                result = ::recv(handle, reinterpret_cast<char*>(&size_buffer[idx]), sizeof(uint32_t) - idx, flags);
            if (result < 0)
            {
                result = 0;
                if (!isLastErrorNonBlocking())
                {
                    close();
                    return false;
                }
            }
            if (result == 0 && idx == 0)
                return false;
            idx += result;
        }
        uint32_t size = *reinterpret_cast<uint32_t*>(size_buffer);
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        size = __builtin_bswap32(size);
#endif
        receive_buffer.resize(size);
        received_size = 0;
    }

    while(true)
    {
        int result;
        if (ssl_handle)
            result = SSL_read(static_cast<SSL*>(ssl_handle), reinterpret_cast<char*>(&receive_buffer[received_size]), receive_buffer.size() - received_size);
        else
            result = ::recv(handle, reinterpret_cast<char*>(&receive_buffer[received_size]), receive_buffer.size() - received_size, flags);
        if (result < 0)
        {
            result = 0;
            if (!isLastErrorNonBlocking())
            {
                close();
                return false;
            }
        }
        received_size += result;
        if (received_size == receive_buffer.size())
        {
            buffer = std::move(receive_buffer);
            received_size = 0;
            return true;
        }
        if (result < 1)
            break;
    }

    return false;
}

bool TcpSocket::sendSendQueue()
{
    if (send_queue.size() < 1)
        return false;

    int result;
    do
    {
        if (ssl_handle)
            result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(send_queue.data()), send_queue.size());
        else
            result = ::send(handle, static_cast<const char*>(send_queue.data()), send_queue.size(), flags);
        if (result < 0)
        {
            result = 0;
            if (!isLastErrorNonBlocking())
                close();
        }
        if (result > 0)
            send_queue = send_queue.substr(result);
    } while(result > 0 && send_queue.size() > 0);

    return send_queue.size() > 0;
}

}//namespace network
}//namespace io
}//namespace sp
<commit_msg>Add missing header for TCP_NODELAY (#120)<commit_after>#include <io/network/tcpSocket.h>
#include <logging.h>

#ifdef __WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
static constexpr int flags = 0;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
static constexpr int flags = MSG_NOSIGNAL;
#endif

extern "C" {
    struct X509;
    struct X509_STORE;
    struct SSL_CTX;
    struct SSL_METHOD;
    struct SSL;

    static X509_STORE* (*X509_STORE_new)();
    static X509* (*d2i_X509)(X509**, const unsigned char **, long);
    static int (*X509_STORE_add_cert)(X509_STORE*, X509*);
    static void (*X509_free)(X509*);
    static SSL_CTX* (*SSL_CTX_new)(const SSL_METHOD*);
    static const SSL_METHOD* (*TLSv1_2_client_method)();
    static long (*SSL_CTX_set_options)(SSL_CTX*, long);
    static int (*SSL_CTX_set_default_verify_paths)(SSL_CTX*);
    static void (*SSL_CTX_set_cert_store)(SSL_CTX*, X509_STORE*);
    static SSL* (*SSL_new)(SSL_CTX*);
    static int (*SSL_set_fd)(SSL *ssl, int fd);
    static int (*SSL_connect)(SSL *ssl);
    static long (*SSL_get_verify_result)(const SSL *ssl);
    static int (*SSL_read)(SSL *ssl, void *buf, int num);
    static int (*SSL_write)(SSL *ssl, const void *buf, int num);
    static void (*SSL_free)(SSL *ssl);

    static SSL_CTX* ssl_context;
}

# define SSL_OP_NO_SSLv2                                 0x01000000L
# define SSL_OP_NO_SSLv3                                 0x02000000L
# define SSL_OP_NO_TLSv1                                 0x04000000L
# define SSL_OP_NO_TLSv1_2                               0x08000000L
# define SSL_OP_NO_TLSv1_1                               0x10000000L

#include "dynamicLibrary.h"

static std::unique_ptr<DynamicLibrary> libcrypto;
static std::unique_ptr<DynamicLibrary> libssl;

static void initializeLibSSL()
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

#ifdef __WIN32
    libcrypto = DynamicLibrary::open("libcrypto-1_1.dll");
    libssl = DynamicLibrary::open("libssl-1_1.dll");
#else
    libcrypto = DynamicLibrary::open("libcrypto.so.1.1");
    libssl = DynamicLibrary::open("libssl.so.1.1");
#endif
    if (!libcrypto || !libssl)
        return;

    X509_STORE_new = libcrypto->getFunction<X509_STORE*(*)()>("X509_STORE_new");
    d2i_X509 = libcrypto->getFunction<X509* (*)(X509**, const unsigned char **, long)>("d2i_X509");
    X509_STORE_add_cert = libcrypto->getFunction<int (*)(X509_STORE*, X509*)>("X509_STORE_add_cert");
    X509_free = libcrypto->getFunction<void (*)(X509*)>("X509_free");

    SSL_CTX_new = libssl->getFunction<SSL_CTX*(*)(const SSL_METHOD*)>("SSL_CTX_new");
    TLSv1_2_client_method = libssl->getFunction<const SSL_METHOD* (*)()>("TLSv1_2_client_method");
    SSL_CTX_set_options = libssl->getFunction<long (*)(SSL_CTX*, long)>("SSL_CTX_set_options");
    SSL_CTX_set_default_verify_paths = libssl->getFunction<int (*)(SSL_CTX*)>("SSL_CTX_set_default_verify_paths");
    SSL_CTX_set_cert_store = libssl->getFunction<void (*)(SSL_CTX*, X509_STORE*)>("SSL_CTX_set_cert_store");
    SSL_new = libssl->getFunction<SSL* (*)(SSL_CTX*)>("SSL_new");
    SSL_set_fd = libssl->getFunction<int (*)(SSL *ssl, int fd)>("SSL_set_fd");
    SSL_connect = libssl->getFunction<int (*)(SSL *ssl)>("SSL_connect");
    SSL_get_verify_result = libssl->getFunction<long (*)(const SSL *ssl)>("SSL_get_verify_result");
    SSL_read = libssl->getFunction<int (*)(SSL *ssl, void *buf, int num)>("SSL_read");
    SSL_write = libssl->getFunction<int (*)(SSL *ssl, const void *buf, int num)>("SSL_write");
    SSL_free = libssl->getFunction<void (*)(SSL *ssl)>("SSL_free");

#ifdef __WIN32
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = NULL;
    X509 *x509;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(0, "ROOT");
    while((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
    {
        const unsigned char* c = pContext->pbCertEncoded;
        x509 = d2i_X509(nullptr, &c, pContext->cbCertEncoded);
        if (x509)
        {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }
    CertFreeCertificateContext(pContext);
    CertCloseStore(hStore, 0);
#endif

    ssl_context = SSL_CTX_new(TLSv1_2_client_method());
    SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#ifdef __WIN32
    SSL_CTX_set_cert_store(ssl_context, store);
#else
    SSL_CTX_set_default_verify_paths(ssl_context);
#endif
}

namespace sp {
namespace io {
namespace network {

TcpSocket::TcpSocket()
: ssl_handle(nullptr)
{
}

TcpSocket::TcpSocket(TcpSocket&& socket)
{
    *this = std::move(socket);
}

TcpSocket::~TcpSocket()
{
    close();
}

TcpSocket& TcpSocket::operator=(TcpSocket&& socket)
{
    close();

    handle = socket.handle;
    ssl_handle = socket.ssl_handle;
    send_queue = std::move(socket.send_queue);
    blocking = socket.blocking;
    receive_buffer = std::move(socket.receive_buffer);
    received_size = socket.received_size;

    socket.handle = -1;
    socket.send_queue.clear();
    socket.receive_buffer.clear();
    socket.received_size = 0;
    socket.ssl_handle = nullptr;

    return *this;
}

bool TcpSocket::connect(const Address& host, int port)
{
    if (isConnected())
        close();

    for(const auto& addr_info : host.addr_info)
    {
        handle = ::socket(addr_info.family, SOCK_STREAM, 0);
        if (handle < 0)
            return false;
        if (addr_info.family == AF_INET && sizeof(struct sockaddr_in) == addr_info.addr.length())
        {
            struct sockaddr_in server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.length());
            server_addr.sin_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
            {
                setBlocking(blocking);
                return true;
            }
        }
        if (addr_info.family == AF_INET6 && sizeof(struct sockaddr_in6) == addr_info.addr.length())
        {
            struct sockaddr_in6 server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.length());
            server_addr.sin6_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
            {
                setBlocking(blocking);
                return true;
            }
        }
        close();
    }
    return false;
}

bool TcpSocket::connectSSL(const Address& host, int port)
{
    if (!connect(host, port))
        return false;
    initializeLibSSL();
    if (!SSL_new)
    {
        LOG(Warning, "Failed to connect SSL socket due to missing libssl/libcrypto v1.1");
        close();
        return false;
    }

    ssl_handle = SSL_new(ssl_context);
    SSL_set_fd(static_cast<SSL*>(ssl_handle), handle);
    if (!SSL_connect(static_cast<SSL*>(ssl_handle)))
    {
        LOG(Warning, "Failed to connect SSL socket due to SSL negotiation failure.");
        close();
        return false;
    }
    if (SSL_get_verify_result(static_cast<SSL*>(ssl_handle)) != 0)
    {
        LOG(Warning, "Failed to connect SSL socket due to certificate verfication failure.");
        close();
        return false;
    }
    return true;
}

void TcpSocket::setDelay(bool delay)
{
    int mode = delay ? 0 : 1;
    setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, (char*)&mode, sizeof(mode));
}

void TcpSocket::close()
{
    if (isConnected())
    {
#ifdef __WIN32
        closesocket(handle);
#else
        ::close(handle);
#endif
        handle = -1;
        send_queue.clear();
        if (ssl_handle)
            SSL_free(static_cast<SSL*>(ssl_handle));
        ssl_handle = nullptr;
    }
}

bool TcpSocket::isConnected()
{
    return handle != -1;
}

void TcpSocket::send(const void* data, size_t size)
{
    if (!isConnected())
        return;
    if (sendSendQueue())
    {
        queue(data, size);
        return;
    }

    for(size_t done = 0; done < size; )
    {
        int result;
        if (ssl_handle)
            result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(data) + done, size - done);
        else
            result = ::send(handle, static_cast<const char*>(data) + done, size - done, flags);
        if (result < 0)
        {
            if (!isLastErrorNonBlocking())
                close();
            else
                send_queue += std::string(static_cast<const char*>(data) + done, size - done);
            return;
        }
        done += result;
    }
}

void TcpSocket::queue(const void* data, size_t size)
{
    send_queue += std::string(static_cast<const char*>(data), size);
}

size_t TcpSocket::receive(void* data, size_t size)
{
    sendSendQueue();

    if (!isConnected())
        return 0;

    int result;
    if (ssl_handle)
        result = SSL_read(static_cast<SSL*>(ssl_handle), static_cast<char*>(data), size);
    else
        result = ::recv(handle, static_cast<char*>(data), size, flags);
    if (result < 0)
    {
        result = 0;
        if (!isLastErrorNonBlocking())
            close();
    }
    return result;
}

void TcpSocket::send(const io::DataBuffer& buffer)
{
    io::DataBuffer packet_size(uint32_t(buffer.getDataSize()));
    send(packet_size.getData(), packet_size.getDataSize());
    send(buffer.getData(), buffer.getDataSize());
}

void TcpSocket::queue(const io::DataBuffer& buffer)
{
    io::DataBuffer packet_size(uint32_t(buffer.getDataSize()));
    queue(packet_size.getData(), packet_size.getDataSize());
    queue(buffer.getData(), buffer.getDataSize());
}

bool TcpSocket::receive(io::DataBuffer& buffer)
{
    if (!isConnected())
        return 0;

    if (receive_buffer.size() == 0)
    {
        uint8_t size_buffer[sizeof(uint32_t)];
        size_t idx = 0;
        while(idx < sizeof(uint32_t))
        {
            //TOFIX: This blocks if we receive less then 4 bytes. Allows denial of service attack.
            int result;
            if (ssl_handle)
                result = SSL_read(static_cast<SSL*>(ssl_handle), reinterpret_cast<char*>(&size_buffer[idx]), sizeof(uint32_t) - idx);
            else
                result = ::recv(handle, reinterpret_cast<char*>(&size_buffer[idx]), sizeof(uint32_t) - idx, flags);
            if (result < 0)
            {
                result = 0;
                if (!isLastErrorNonBlocking())
                {
                    close();
                    return false;
                }
            }
            if (result == 0 && idx == 0)
                return false;
            idx += result;
        }
        uint32_t size = *reinterpret_cast<uint32_t*>(size_buffer);
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        size = __builtin_bswap32(size);
#endif
        receive_buffer.resize(size);
        received_size = 0;
    }

    while(true)
    {
        int result;
        if (ssl_handle)
            result = SSL_read(static_cast<SSL*>(ssl_handle), reinterpret_cast<char*>(&receive_buffer[received_size]), receive_buffer.size() - received_size);
        else
            result = ::recv(handle, reinterpret_cast<char*>(&receive_buffer[received_size]), receive_buffer.size() - received_size, flags);
        if (result < 0)
        {
            result = 0;
            if (!isLastErrorNonBlocking())
            {
                close();
                return false;
            }
        }
        received_size += result;
        if (received_size == receive_buffer.size())
        {
            buffer = std::move(receive_buffer);
            received_size = 0;
            return true;
        }
        if (result < 1)
            break;
    }

    return false;
}

bool TcpSocket::sendSendQueue()
{
    if (send_queue.size() < 1)
        return false;

    int result;
    do
    {
        if (ssl_handle)
            result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(send_queue.data()), send_queue.size());
        else
            result = ::send(handle, static_cast<const char*>(send_queue.data()), send_queue.size(), flags);
        if (result < 0)
        {
            result = 0;
            if (!isLastErrorNonBlocking())
                close();
        }
        if (result > 0)
            send_queue = send_queue.substr(result);
    } while(result > 0 && send_queue.size() > 0);

    return send_queue.size() > 0;
}

}//namespace network
}//namespace io
}//namespace sp
<|endoftext|>


<commit_before>#include "stack.hh"
#include "net.hh"
#include "packet.hh"
#include "api.hh"
#include "posix-stack.hh"

namespace net {

class posix_connected_socket_impl final : public connected_socket_impl {
    pollable_fd _fd;
private:
    explicit posix_connected_socket_impl(pollable_fd fd) : _fd(std::move(fd)) {}
public:
    virtual input_stream<char> input() override { return input_stream<char>(posix_data_source(_fd)); }
    virtual output_stream<char> output() override { return output_stream<char>(posix_data_sink(_fd), 8192); }
    friend class posix_server_socket_impl;
    friend class posix_ap_server_socket_impl;
};

future<connected_socket, socket_address>
posix_server_socket_impl::accept() {
    return _lfd.accept().then([this] (pollable_fd fd, socket_address sa) {
        static unsigned balance = 0;
        auto cpu = balance++ % smp::count;

        if (cpu == engine._id) {
            std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(fd)));
            return make_ready_future<connected_socket, socket_address>(
                    connected_socket(std::move(csi)), sa);
        } else {
            smp::submit_to(cpu, [this, fd = std::move(fd.get_file_desc()), sa] () mutable {
                posix_ap_server_socket_impl::move_connected_socket(_sa, pollable_fd(std::move(fd)), sa);
            });
            return accept();
        }
    });
}

future<connected_socket, socket_address> posix_ap_server_socket_impl::accept() {
    auto conni = conn_q.find(_sa.as_posix_sockaddr_in());
    if (conni != conn_q.end()) {
        connection c = std::move(conni->second);
        conn_q.erase(conni);
        std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(c.fd)));
        return make_ready_future<connected_socket, socket_address>(connected_socket(std::move(csi)), std::move(c.addr));
    } else {
        auto i = sockets.emplace(std::piecewise_construct, std::make_tuple(_sa.as_posix_sockaddr_in()), std::make_tuple());
        assert(i.second);
        return i.first->second.get_future();
    }
}

void  posix_ap_server_socket_impl::move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr) {
    auto i = sockets.find(sa.as_posix_sockaddr_in());
    if (i != sockets.end()) {
        std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(fd)));
        i->second.set_value(connected_socket(std::move(csi)), std::move(addr));
        sockets.erase(i);
    } else {
        conn_q.emplace(std::piecewise_construct, std::make_tuple(sa.as_posix_sockaddr_in()), std::make_tuple(std::move(fd), std::move(addr)));
    }
}

data_source posix_data_source(pollable_fd& fd) {
    return data_source(std::make_unique<posix_data_source_impl>(fd));
}

future<temporary_buffer<char>>
posix_data_source_impl::get() {
    return _fd.read_some(_buf.get_write(), _buf_size).then([this] (size_t size) {
        _buf.trim(size);
        auto ret = std::move(_buf);
        _buf = temporary_buffer<char>(_buf_size);
        return make_ready_future<temporary_buffer<char>>(std::move(ret));
    });
}

data_sink posix_data_sink(pollable_fd& fd) {
    return data_sink(std::make_unique<posix_data_sink_impl>(fd));
}

future<>
posix_data_sink_impl::put(std::vector<temporary_buffer<char>> data) {
    std::swap(data, _data);
    return do_write(0);
}

future<>
posix_data_sink_impl::do_write(size_t idx) {
    // FIXME: use writev
    return _fd.write_all(_data[idx].get(), _data[idx].size()).then([this, idx] (size_t size) mutable {
        assert(size == _data[idx].size()); // FIXME: exception? short write?
        if (++idx == _data.size()) {
            _data.clear();
            return make_ready_future<>();
        }
        return do_write(idx);
    });
}

server_socket
posix_network_stack::listen(socket_address sa, listen_options opt) {
    return server_socket(std::make_unique<posix_server_socket_impl>(sa, engine.posix_listen(sa, opt)));
}

thread_local std::unordered_map<::sockaddr_in, promise<connected_socket, socket_address>> posix_ap_server_socket_impl::sockets;
thread_local std::unordered_multimap<::sockaddr_in, posix_ap_server_socket_impl::connection> posix_ap_server_socket_impl::conn_q;

server_socket
posix_ap_network_stack::listen(socket_address sa, listen_options opt) {
    return server_socket(std::make_unique<posix_ap_server_socket_impl>(sa));
}

struct cmsg_with_pktinfo {
    struct cmsghdrcmh;
    struct in_pktinfo pktinfo;
};

std::vector<struct iovec> to_iovec(const packet& p) {
    std::vector<struct iovec> v;
    v.reserve(p.nr_frags());
    for (auto&& f : p.fragments()) {
        v.push_back({.iov_base = f.base, .iov_len = f.size});
    }
    return v;
}

class posix_udp_channel : public udp_channel_impl {
private:
    static constexpr int MAX_DATAGRAM_SIZE = 65507;
    struct recv_ctx {
        struct msghdr _hdr;
        struct iovec _iov;
        socket_address _src_addr;
        char* _buffer;
        cmsg_with_pktinfo _cmsg;

        recv_ctx() {
            memset(&_hdr, 0, sizeof(_hdr));
            _hdr.msg_iov = &_iov;
            _hdr.msg_iovlen = 1;
            _hdr.msg_name = &_src_addr.u.sa;
            _hdr.msg_namelen = sizeof(_src_addr.u.sas);
            memset(&_cmsg, 0, sizeof(_cmsg));
            _hdr.msg_control = &_cmsg;
            _hdr.msg_controllen = sizeof(_cmsg);
        }

        void prepare() {
            _buffer = new char[MAX_DATAGRAM_SIZE];
            _iov.iov_base = _buffer;
            _iov.iov_len = MAX_DATAGRAM_SIZE;
        }
    };
    struct send_ctx {
        struct msghdr _hdr;
        std::vector<struct iovec> _iovecs;
        socket_address _dst;
        packet _p;

        send_ctx() {
            memset(&_hdr, 0, sizeof(_hdr));
            _hdr.msg_name = &_dst.u.sa;
            _hdr.msg_namelen = sizeof(_dst.u.sas);
        }

        void prepare(ipv4_addr dst, packet p) {
            _dst = make_ipv4_address(dst);
            _p = std::move(p);
            _iovecs = std::move(to_iovec(_p));
            _hdr.msg_iov = _iovecs.data();
            _hdr.msg_iovlen = _iovecs.size();
        }
    };
    std::unique_ptr<pollable_fd> _fd;
    ipv4_addr _address;
    recv_ctx _recv;
    send_ctx _send;
    bool _closed;
public:
    posix_udp_channel(ipv4_addr bind_address)
            : _closed(false) {
        auto sa = make_ipv4_address(bind_address);
        file_desc fd = file_desc::socket(sa.u.sa.sa_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
        bool pktinfo_flag = true;
        ::setsockopt(fd.get(), SOL_IP, IP_PKTINFO, &pktinfo_flag, sizeof(pktinfo_flag));
        fd.bind(sa.u.sa, sizeof(sa.u.sas));
        _address = ipv4_addr(fd.get_address());
        _fd = std::make_unique<pollable_fd>(std::move(fd));
    }
    virtual ~posix_udp_channel() {};
    virtual future<udp_datagram> receive() override;
    virtual future<> send(ipv4_addr dst, const char *msg);
    virtual future<> send(ipv4_addr dst, packet p);
    virtual void close() override {
        _closed = true;
        _fd.reset();
    }
    virtual bool is_closed() const override { return _closed; }
};

future<> posix_udp_channel::send(ipv4_addr dst, const char *message) {
    auto len = strlen(message);
    return _fd->sendto(make_ipv4_address(dst), message, len)
            .then([len] (size_t size) { assert(size == len); });
}

future<> posix_udp_channel::send(ipv4_addr dst, packet p) {
    auto len = p.len();
    _send.prepare(dst, std::move(p));
    return _fd->sendmsg(&_send._hdr)
            .then([len] (size_t size) { assert(size == len); });
}

udp_channel
posix_network_stack::make_udp_channel(ipv4_addr addr) {
    return udp_channel(std::make_unique<posix_udp_channel>(addr));
}

class posix_datagram : public udp_datagram_impl {
private:
    ipv4_addr _src;
    ipv4_addr _dst;
    packet _p;
public:
    posix_datagram(ipv4_addr src, ipv4_addr dst, packet p) : _src(src), _dst(dst), _p(std::move(p)) {}
    virtual ipv4_addr get_src() override { return _src; }
    virtual ipv4_addr get_dst() override { return _dst; }
    virtual uint16_t get_dst_port() override { return _dst.port; }
    virtual packet& get_data() override { return _p; }
};

future<udp_datagram>
posix_udp_channel::receive() {
    _recv.prepare();
    return _fd->recvmsg(&_recv._hdr).then([this] (size_t size) {
        auto dst = ipv4_addr(_recv._cmsg.pktinfo.ipi_addr.s_addr, _address.port);
        return make_ready_future<udp_datagram>(udp_datagram(std::make_unique<posix_datagram>(
            _recv._src_addr, dst, packet(fragment{_recv._buffer, size}, [buf = _recv._buffer] { delete[] buf; }))));
    });
}

network_stack_registrator nsr_posix{"posix",
    boost::program_options::options_description(),
    [](boost::program_options::variables_map ops) {
        return smp::main_thread() ? posix_network_stack::create(ops) : posix_ap_network_stack::create(ops);
    },
    true
};

}
<commit_msg>posix-stack.cc: add missing copyright<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include "stack.hh"
#include "net.hh"
#include "packet.hh"
#include "api.hh"
#include "posix-stack.hh"

namespace net {

class posix_connected_socket_impl final : public connected_socket_impl {
    pollable_fd _fd;
private:
    explicit posix_connected_socket_impl(pollable_fd fd) : _fd(std::move(fd)) {}
public:
    virtual input_stream<char> input() override { return input_stream<char>(posix_data_source(_fd)); }
    virtual output_stream<char> output() override { return output_stream<char>(posix_data_sink(_fd), 8192); }
    friend class posix_server_socket_impl;
    friend class posix_ap_server_socket_impl;
};

future<connected_socket, socket_address>
posix_server_socket_impl::accept() {
    return _lfd.accept().then([this] (pollable_fd fd, socket_address sa) {
        static unsigned balance = 0;
        auto cpu = balance++ % smp::count;

        if (cpu == engine._id) {
            std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(fd)));
            return make_ready_future<connected_socket, socket_address>(
                    connected_socket(std::move(csi)), sa);
        } else {
            smp::submit_to(cpu, [this, fd = std::move(fd.get_file_desc()), sa] () mutable {
                posix_ap_server_socket_impl::move_connected_socket(_sa, pollable_fd(std::move(fd)), sa);
            });
            return accept();
        }
    });
}

future<connected_socket, socket_address> posix_ap_server_socket_impl::accept() {
    auto conni = conn_q.find(_sa.as_posix_sockaddr_in());
    if (conni != conn_q.end()) {
        connection c = std::move(conni->second);
        conn_q.erase(conni);
        std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(c.fd)));
        return make_ready_future<connected_socket, socket_address>(connected_socket(std::move(csi)), std::move(c.addr));
    } else {
        auto i = sockets.emplace(std::piecewise_construct, std::make_tuple(_sa.as_posix_sockaddr_in()), std::make_tuple());
        assert(i.second);
        return i.first->second.get_future();
    }
}

void  posix_ap_server_socket_impl::move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr) {
    auto i = sockets.find(sa.as_posix_sockaddr_in());
    if (i != sockets.end()) {
        std::unique_ptr<connected_socket_impl> csi(new posix_connected_socket_impl(std::move(fd)));
        i->second.set_value(connected_socket(std::move(csi)), std::move(addr));
        sockets.erase(i);
    } else {
        conn_q.emplace(std::piecewise_construct, std::make_tuple(sa.as_posix_sockaddr_in()), std::make_tuple(std::move(fd), std::move(addr)));
    }
}

data_source posix_data_source(pollable_fd& fd) {
    return data_source(std::make_unique<posix_data_source_impl>(fd));
}

future<temporary_buffer<char>>
posix_data_source_impl::get() {
    return _fd.read_some(_buf.get_write(), _buf_size).then([this] (size_t size) {
        _buf.trim(size);
        auto ret = std::move(_buf);
        _buf = temporary_buffer<char>(_buf_size);
        return make_ready_future<temporary_buffer<char>>(std::move(ret));
    });
}

data_sink posix_data_sink(pollable_fd& fd) {
    return data_sink(std::make_unique<posix_data_sink_impl>(fd));
}

future<>
posix_data_sink_impl::put(std::vector<temporary_buffer<char>> data) {
    std::swap(data, _data);
    return do_write(0);
}

future<>
posix_data_sink_impl::do_write(size_t idx) {
    // FIXME: use writev
    return _fd.write_all(_data[idx].get(), _data[idx].size()).then([this, idx] (size_t size) mutable {
        assert(size == _data[idx].size()); // FIXME: exception? short write?
        if (++idx == _data.size()) {
            _data.clear();
            return make_ready_future<>();
        }
        return do_write(idx);
    });
}

server_socket
posix_network_stack::listen(socket_address sa, listen_options opt) {
    return server_socket(std::make_unique<posix_server_socket_impl>(sa, engine.posix_listen(sa, opt)));
}

thread_local std::unordered_map<::sockaddr_in, promise<connected_socket, socket_address>> posix_ap_server_socket_impl::sockets;
thread_local std::unordered_multimap<::sockaddr_in, posix_ap_server_socket_impl::connection> posix_ap_server_socket_impl::conn_q;

server_socket
posix_ap_network_stack::listen(socket_address sa, listen_options opt) {
    return server_socket(std::make_unique<posix_ap_server_socket_impl>(sa));
}

struct cmsg_with_pktinfo {
    struct cmsghdrcmh;
    struct in_pktinfo pktinfo;
};

std::vector<struct iovec> to_iovec(const packet& p) {
    std::vector<struct iovec> v;
    v.reserve(p.nr_frags());
    for (auto&& f : p.fragments()) {
        v.push_back({.iov_base = f.base, .iov_len = f.size});
    }
    return v;
}

class posix_udp_channel : public udp_channel_impl {
private:
    static constexpr int MAX_DATAGRAM_SIZE = 65507;
    struct recv_ctx {
        struct msghdr _hdr;
        struct iovec _iov;
        socket_address _src_addr;
        char* _buffer;
        cmsg_with_pktinfo _cmsg;

        recv_ctx() {
            memset(&_hdr, 0, sizeof(_hdr));
            _hdr.msg_iov = &_iov;
            _hdr.msg_iovlen = 1;
            _hdr.msg_name = &_src_addr.u.sa;
            _hdr.msg_namelen = sizeof(_src_addr.u.sas);
            memset(&_cmsg, 0, sizeof(_cmsg));
            _hdr.msg_control = &_cmsg;
            _hdr.msg_controllen = sizeof(_cmsg);
        }

        void prepare() {
            _buffer = new char[MAX_DATAGRAM_SIZE];
            _iov.iov_base = _buffer;
            _iov.iov_len = MAX_DATAGRAM_SIZE;
        }
    };
    struct send_ctx {
        struct msghdr _hdr;
        std::vector<struct iovec> _iovecs;
        socket_address _dst;
        packet _p;

        send_ctx() {
            memset(&_hdr, 0, sizeof(_hdr));
            _hdr.msg_name = &_dst.u.sa;
            _hdr.msg_namelen = sizeof(_dst.u.sas);
        }

        void prepare(ipv4_addr dst, packet p) {
            _dst = make_ipv4_address(dst);
            _p = std::move(p);
            _iovecs = std::move(to_iovec(_p));
            _hdr.msg_iov = _iovecs.data();
            _hdr.msg_iovlen = _iovecs.size();
        }
    };
    std::unique_ptr<pollable_fd> _fd;
    ipv4_addr _address;
    recv_ctx _recv;
    send_ctx _send;
    bool _closed;
public:
    posix_udp_channel(ipv4_addr bind_address)
            : _closed(false) {
        auto sa = make_ipv4_address(bind_address);
        file_desc fd = file_desc::socket(sa.u.sa.sa_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
        bool pktinfo_flag = true;
        ::setsockopt(fd.get(), SOL_IP, IP_PKTINFO, &pktinfo_flag, sizeof(pktinfo_flag));
        fd.bind(sa.u.sa, sizeof(sa.u.sas));
        _address = ipv4_addr(fd.get_address());
        _fd = std::make_unique<pollable_fd>(std::move(fd));
    }
    virtual ~posix_udp_channel() {};
    virtual future<udp_datagram> receive() override;
    virtual future<> send(ipv4_addr dst, const char *msg);
    virtual future<> send(ipv4_addr dst, packet p);
    virtual void close() override {
        _closed = true;
        _fd.reset();
    }
    virtual bool is_closed() const override { return _closed; }
};

future<> posix_udp_channel::send(ipv4_addr dst, const char *message) {
    auto len = strlen(message);
    return _fd->sendto(make_ipv4_address(dst), message, len)
            .then([len] (size_t size) { assert(size == len); });
}

future<> posix_udp_channel::send(ipv4_addr dst, packet p) {
    auto len = p.len();
    _send.prepare(dst, std::move(p));
    return _fd->sendmsg(&_send._hdr)
            .then([len] (size_t size) { assert(size == len); });
}

udp_channel
posix_network_stack::make_udp_channel(ipv4_addr addr) {
    return udp_channel(std::make_unique<posix_udp_channel>(addr));
}

class posix_datagram : public udp_datagram_impl {
private:
    ipv4_addr _src;
    ipv4_addr _dst;
    packet _p;
public:
    posix_datagram(ipv4_addr src, ipv4_addr dst, packet p) : _src(src), _dst(dst), _p(std::move(p)) {}
    virtual ipv4_addr get_src() override { return _src; }
    virtual ipv4_addr get_dst() override { return _dst; }
    virtual uint16_t get_dst_port() override { return _dst.port; }
    virtual packet& get_data() override { return _p; }
};

future<udp_datagram>
posix_udp_channel::receive() {
    _recv.prepare();
    return _fd->recvmsg(&_recv._hdr).then([this] (size_t size) {
        auto dst = ipv4_addr(_recv._cmsg.pktinfo.ipi_addr.s_addr, _address.port);
        return make_ready_future<udp_datagram>(udp_datagram(std::make_unique<posix_datagram>(
            _recv._src_addr, dst, packet(fragment{_recv._buffer, size}, [buf = _recv._buffer] { delete[] buf; }))));
    });
}

network_stack_registrator nsr_posix{"posix",
    boost::program_options::options_description(),
    [](boost::program_options::variables_map ops) {
        return smp::main_thread() ? posix_network_stack::create(ops) : posix_ap_network_stack::create(ops);
    },
    true
};

}
<|endoftext|>


<commit_before>#include "restclient.h"
#include "exception.h"

namespace arg3
{
    namespace net
    {
        size_t curl_append_response_callback(void *ptr, size_t size, size_t nmemb, string *s)
        {
            if(s == NULL) return 0;

            const size_t new_len = size * nmemb;

            char buf[new_len + 1];

            memcpy(buf, ptr, size * nmemb);

            buf[new_len] = '\0';

            s->append(buf);

            return new_len;
        }

        RESTClient::RESTClient(const string &host, const string &version) : curl_(curl_easy_init()), protocol_(http::PROTOCOL), host_(host), version_(version)
        {
            if (curl_ == NULL)
            {
                throw RESTException("unable to initialize request");
            }

            curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curl_append_response_callback);
        }

        RESTClient::RESTClient() : curl_(curl_easy_init())
        {
            if (curl_ == NULL)
            {
                throw RESTException("unable to initialize request");
            }

            curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curl_append_response_callback);
        }

        void RESTClient::addHeader(const string &key, const string &value)
        {
            headers_[key] = value;
        }

        void RESTClient::removeHeader(const string &key)
        {
            headers_.erase(key);
        }

        string RESTClient::getHeader(const string &key)
        {
            return headers_[key];
        }

        string RESTClient::getVersion() const {
            return version_;
        }

        string RESTClient::getHost() const {
            return host_;
        }

        int RESTClient::getResponseCode() const {
            return responseCode_;
        }

        string RESTClient::getResponse() const {
            return response_;
        }

        void RESTClient::setHost(const string &host) {
            host_ = host;
        }

        void RESTClient::setVersion(const string &version) {
            version_ = version;
        }

        void RESTClient::setSecure(bool value) {
            protocol_ = value ? http::SECURE_PROTOCOL : http::PROTOCOL;
        }

        RESTClient &RESTClient::setPayload(const string &payload) {
            payload_ = payload;
            return *this;
        }

        RESTClient& RESTClient::request(http::Method method, const string& path)
        {
            struct curl_slist *headers = NULL;

            char buf[http::MAX_URL_LEN+1];

            snprintf(buf, http::MAX_URL_LEN, "%s://%s/%s/%s", protocol_.c_str(), host_.c_str(), version_.c_str(), path.c_str());

            curl_easy_setopt(curl_, CURLOPT_URL, buf);

            switch(method)
            {
            case http::Get:
                curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
                break;
            case http::Post:
                curl_easy_setopt(curl_, CURLOPT_POST, 1L);
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, payload_.c_str());
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, payload_.size());
                break;
            case http::Put:
                curl_easy_setopt(curl_, CURLOPT_PUT, 1L);
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, payload_.c_str());
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, payload_.size());
            case http::Delete:
                curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, http::DELETE);
                break;
            }

            for(auto &h : headers_)
            {
                snprintf(buf, http::MAX_URL_LEN, "%s: %s", h.first.c_str(), h.second.c_str());

                headers = curl_slist_append(headers, buf);
            }

            curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);

            curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response_);

            CURLcode res = curl_easy_perform(curl_);

            if (res != CURLE_OK)
                throw RESTException(curl_easy_strerror(res));

            curl_easy_getinfo (curl_, CURLINFO_RESPONSE_CODE, &responseCode_);

            /* always cleanup */
            curl_easy_cleanup(curl_);

            return *this;
        }

        RESTClient& RESTClient::get(const string &path)
        {
            return request(http::Get, path);
        }

        RESTClient& RESTClient::post(const string &path)
        {
            return request(http::Post, path);
        }

        RESTClient& RESTClient::put(const string &path)
        {
            return request(http::Put, path);
        }

        RESTClient& RESTClient::de1ete(const string &path)
        {
            curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, http::DELETE);

            return request(http::Delete, path);
        }
    }
}<commit_msg>add missing method in restclient<commit_after>#include "restclient.h"
#include "exception.h"

namespace arg3
{
    namespace net
    {
        size_t curl_append_response_callback(void *ptr, size_t size, size_t nmemb, string *s)
        {
            if(s == NULL) return 0;

            const size_t new_len = size * nmemb;

            char buf[new_len + 1];

            memcpy(buf, ptr, size * nmemb);

            buf[new_len] = '\0';

            s->append(buf);

            return new_len;
        }

        RESTClient::RESTClient(const string &host, const string &version) : curl_(curl_easy_init()), protocol_(http::PROTOCOL), host_(host), version_(version)
        {
            if (curl_ == NULL)
            {
                throw RESTException("unable to initialize request");
            }

            curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curl_append_response_callback);
        }

        RESTClient::RESTClient() : curl_(curl_easy_init())
        {
            if (curl_ == NULL)
            {
                throw RESTException("unable to initialize request");
            }

            curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curl_append_response_callback);
        }

        void RESTClient::addHeader(const string &key, const string &value)
        {
            headers_[key] = value;
        }

        void RESTClient::removeHeader(const string &key)
        {
            headers_.erase(key);
        }

        string RESTClient::getHeader(const string &key)
        {
            return headers_[key];
        }

        string RESTClient::getVersion() const {
            return version_;
        }

        string RESTClient::getHost() const {
            return host_;
        }

        int RESTClient::getResponseCode() const {
            return responseCode_;
        }

        string RESTClient::getPayload() const {
            return payload_;
        }

        string RESTClient::getResponse() const {
            return response_;
        }

        void RESTClient::setHost(const string &host) {
            host_ = host;
        }

        void RESTClient::setVersion(const string &version) {
            version_ = version;
        }

        void RESTClient::setSecure(bool value) {
            protocol_ = value ? http::SECURE_PROTOCOL : http::PROTOCOL;
        }

        RESTClient &RESTClient::setPayload(const string &payload) {
            payload_ = payload;
            return *this;
        }

        RESTClient& RESTClient::request(http::Method method, const string& path)
        {
            struct curl_slist *headers = NULL;

            char buf[http::MAX_URL_LEN+1];

            snprintf(buf, http::MAX_URL_LEN, "%s://%s/%s/%s", protocol_.c_str(), host_.c_str(), version_.c_str(), path.c_str());

            curl_easy_setopt(curl_, CURLOPT_URL, buf);

            switch(method)
            {
            case http::Get:
                curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
                break;
            case http::Post:
                curl_easy_setopt(curl_, CURLOPT_POST, 1L);
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, payload_.c_str());
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, payload_.size());
                break;
            case http::Put:
                curl_easy_setopt(curl_, CURLOPT_PUT, 1L);
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, payload_.c_str());
                curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, payload_.size());
            case http::Delete:
                curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, http::DELETE);
                break;
            }

            for(auto &h : headers_)
            {
                snprintf(buf, http::MAX_URL_LEN, "%s: %s", h.first.c_str(), h.second.c_str());

                headers = curl_slist_append(headers, buf);
            }

            curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);

            curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response_);

            CURLcode res = curl_easy_perform(curl_);

            if (res != CURLE_OK)
                throw RESTException(curl_easy_strerror(res));

            curl_easy_getinfo (curl_, CURLINFO_RESPONSE_CODE, &responseCode_);

            /* always cleanup */
            curl_easy_cleanup(curl_);

            return *this;
        }

        RESTClient& RESTClient::get(const string &path)
        {
            return request(http::Get, path);
        }

        RESTClient& RESTClient::post(const string &path)
        {
            return request(http::Post, path);
        }

        RESTClient& RESTClient::put(const string &path)
        {
            return request(http::Put, path);
        }

        RESTClient& RESTClient::de1ete(const string &path)
        {
            curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, http::DELETE);

            return request(http::Delete, path);
        }
    }
}<|endoftext|>


<commit_before>#include "network/network.h"
#include "skywalker/logging.h"
#include "paxos/node_util.h"
#include <string.h>

namespace skywalker {

Network::Network(uint64_t node_id)
    : my_node_id_(node_id),
      bg_loop_(),
      loop_(nullptr),
      server_(nullptr) {
  loop_ = bg_loop_.Loop();
}

Network::~Network() {
  loop_->Exit();
  delete server_;
}

void Network::StartServer(const std::function<void (const Slice&) >& cb) {
  IpPort i;
  ParseNodeId(my_node_id_, &i);
  voyager::SockAddr addr(i.ip, i.port);
  server_ = new voyager::TcpServer(loop_, addr);

  server_->SetMessageCallback(
      [cb](const voyager::TcpConnectionPtr&, voyager::Buffer* buf) {
    size_t size = sizeof(uint32_t);
    while (true) {
      if (buf->ReadableSize() > size) {
        uint32_t len;
        memcpy(&len, buf->Peek(), size);
        if (buf->ReadableSize() >= len) {
          cb(Slice(buf->Peek() + size, len));
          buf->Retrieve(len);
        } else {
          break;
        }
      } else {
        break;
      }
    }
  });

  server_->Start();
}

void Network::SendMessage(uint64_t node_id,
                          const std::shared_ptr<Content>& content_ptr) {
  loop_->QueueInLoop([node_id, content_ptr, this] () {
    size_t size = sizeof(uint32_t);
    char buf[size] = {0};
    std::string s(buf, size);
    bool res = content_ptr->AppendToString(&s);
    if (res) {
      uint32_t len = static_cast<uint32_t>(s.size());
      memcpy(buf, &len, size);
      s.replace(s.begin(), s.begin() + size, buf, size);
      SendMessageInLoop(node_id, s);
    } else {
      SWLog(ERROR,
            "Network::SendMessage - Content.SerializeToString error.\n");
    }
  });
}

void Network::SendMessage(const Membership& m,
                          const std::shared_ptr<Content>& content_ptr) {
  loop_->QueueInLoop([this, m, content_ptr] () {
    size_t size = sizeof(uint32_t);
    char buf[size] = {0};
    std::string s(buf, size);
    bool res = content_ptr->AppendToString(&s);
    if (res) {
      uint32_t len = static_cast<uint32_t>(s.size());
      memcpy(buf, &len, size);
      s.replace(s.begin(), s.begin() + size, buf, size);
      for (int i = 0; i < m.node_id_size(); ++i) {
        if (m.node_id(i) != my_node_id_) {
          SendMessageInLoop(m.node_id(i), s);
        }
      }
    } else {
      SWLog(ERROR,
            "Network::SendMessage - Content.SerializeToString error.\n");
    }
  });
}

void Network::SendMessageInLoop(uint64_t node_id,
                                const std::string& s) {
  auto it = connection_map_.find(node_id);
  if (it != connection_map_.end()) {
    it->second->SendMessage(s);
  } else {
    IpPort i;
    ParseNodeId(node_id, &i);
    voyager::SockAddr addr(i.ip, i.port);
    voyager::TcpClient* client(new voyager::TcpClient(loop_, addr));

    client->SetConnectionCallback(
        [node_id, s, this](const voyager::TcpConnectionPtr& p) {
      connection_map_.insert(std::make_pair(node_id, p));
      p->SendMessage(s);
    });

    client->SetConnectFailureCallback([client, this]() {
      delete client;
    });

    client->SetCloseCallback(
        [node_id, client, this](const voyager::TcpConnectionPtr& p) {
      connection_map_.erase(node_id);
      delete client;
    });

    client->Connect(false);
  }
}

}  // namespace skywalker
<commit_msg>fix message len<commit_after>#include "network/network.h"
#include "skywalker/logging.h"
#include "paxos/node_util.h"
#include <string.h>

namespace skywalker {

Network::Network(uint64_t node_id)
    : my_node_id_(node_id),
      bg_loop_(),
      loop_(nullptr),
      server_(nullptr) {
  loop_ = bg_loop_.Loop();
}

Network::~Network() {
  loop_->Exit();
  delete server_;
}

void Network::StartServer(const std::function<void (const Slice&) >& cb) {
  IpPort i;
  ParseNodeId(my_node_id_, &i);
  voyager::SockAddr addr(i.ip, i.port);
  server_ = new voyager::TcpServer(loop_, addr);

  server_->SetMessageCallback(
      [cb](const voyager::TcpConnectionPtr&, voyager::Buffer* buf) {
    size_t size = sizeof(uint32_t);
    while (true) {
      if (buf->ReadableSize() > size) {
        uint32_t len;
        memcpy(&len, buf->Peek(), size);
        if (buf->ReadableSize() >= len) {
          cb(Slice(buf->Peek() + size, len-size));
          buf->Retrieve(len);
        } else {
          break;
        }
      } else {
        break;
      }
    }
  });

  server_->Start();
}

void Network::SendMessage(uint64_t node_id,
                          const std::shared_ptr<Content>& content_ptr) {
  loop_->QueueInLoop([node_id, content_ptr, this] () {
    size_t size = sizeof(uint32_t);
    char buf[size] = {0};
    std::string s(buf, size);
    bool res = content_ptr->AppendToString(&s);
    if (res) {
      uint32_t len = static_cast<uint32_t>(s.size());
      memcpy(buf, &len, size);
      s.replace(s.begin(), s.begin() + size, buf, size);
      SendMessageInLoop(node_id, s);
    } else {
      SWLog(ERROR,
            "Network::SendMessage - Content.SerializeToString error.\n");
    }
  });
}

void Network::SendMessage(const Membership& m,
                          const std::shared_ptr<Content>& content_ptr) {
  loop_->QueueInLoop([this, m, content_ptr] () {
    size_t size = sizeof(uint32_t);
    char buf[size] = {0};
    std::string s(buf, size);
    bool res = content_ptr->AppendToString(&s);
    if (res) {
      uint32_t len = static_cast<uint32_t>(s.size());
      memcpy(buf, &len, size);
      s.replace(s.begin(), s.begin() + size, buf, size);
      for (int i = 0; i < m.node_id_size(); ++i) {
        if (m.node_id(i) != my_node_id_) {
          SendMessageInLoop(m.node_id(i), s);
        }
      }
    } else {
      SWLog(ERROR,
            "Network::SendMessage - Content.SerializeToString error.\n");
    }
  });
}

void Network::SendMessageInLoop(uint64_t node_id,
                                const std::string& s) {
  auto it = connection_map_.find(node_id);
  if (it != connection_map_.end()) {
    it->second->SendMessage(s);
  } else {
    IpPort i;
    ParseNodeId(node_id, &i);
    voyager::SockAddr addr(i.ip, i.port);
    voyager::TcpClient* client(new voyager::TcpClient(loop_, addr));

    client->SetConnectionCallback(
        [node_id, s, this](const voyager::TcpConnectionPtr& p) {
      connection_map_.insert(std::make_pair(node_id, p));
      p->SendMessage(s);
    });

    client->SetConnectFailureCallback([client, this]() {
      delete client;
    });

    client->SetCloseCallback(
        [node_id, client, this](const voyager::TcpConnectionPtr& p) {
      connection_map_.erase(node_id);
      delete client;
    });

    client->Connect(false);
  }
}

}  // namespace skywalker
<|endoftext|>


<commit_before>#ifndef Renderer_HPP
#define Renderer_HPP

#include "FBO.hpp"
#include "PresetFrameIO.hpp"
#include "BeatDetect.hpp"
#include <string>
#include <set>

#ifdef USE_GLES1
#include <GLES/gl.h>
#else
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#endif

#ifdef USE_FTGL
#ifdef WIN32
#include <FTGL.h>
#include <FTGLPixmapFont.h>
#include <FTGLExtrdFont.h>
#else
#include <FTGL/FTGL.h>
#include <FTGL/FTGLPixmapFont.h>
#include <FTGL/FTGLExtrdFont.h>
#endif
#endif /** USE_FTGL */

#include "Pipeline.hpp"
#include "PerPixelMesh.hpp"
#include "Transformation.hpp"
#include "ShaderEngine.hpp"

class UserTexture;
class BeatDetect;
class TextureManager;

class Renderer
{

public:

  bool showfps;
  bool showtitle;
  bool showpreset;
  bool showhelp;
  bool showstats;

  bool studio;
  bool correction;

  bool noSwitch;

  int totalframes;
  float realfps;

  std::string title;
  int drawtitle;
  int texsize;

  Renderer( int width, int height, int gx, int gy, int texsize,  BeatDetect *beatDetect, std::string presetURL, std::string title_fontURL, std::string menu_fontURL);
  ~Renderer();
  void RenderFrame(PresetOutputs &PresetOutputs, const PipelineContext &pipelineContext);
  void RenderFrame(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void ResetTextures();
  void reset(int w, int h);
  GLuint initRenderToTexture();

  void SetPipeline(Pipeline &pipeline);

  void setPresetName(const std::string& theValue)
  {
    m_presetName = theValue;
  }

  std::string presetName() const
  {
    return m_presetName;
  }

private:

	PerPixelMesh mesh;
  RenderTarget *renderTarget;
  BeatDetect *beatDetect;
  TextureManager *textureManager;
  static Pipeline* currentPipe;
  RenderContext renderContext;
  //per pixel equation variables
#ifdef USE_CG
  ShaderEngine shaderEngine;
#endif
  std::string m_presetName;

  float* p;
  char** t;

  int vw;
  int vh;

  float aspect;

  std::string title_fontURL;
  std::string menu_fontURL;
  std::string presetURL;

#ifdef USE_FTGL
  FTGLPixmapFont *title_font;
  FTGLPixmapFont *other_font;
  FTGLExtrdFont *poly_font;
#endif /** USE_FTGL */

  void SetupPass1(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void Interpolation(const Pipeline &pipeline);
  void RenderItems(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void FinishPass1();
  void Pass2 (const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void CompositeOutput(const Pipeline &pipeline, const PipelineContext &pipelineContext);

  inline static Point PerPixel(Point p, PerPixelContext &context)
  {
	  return currentPipe->PerPixel(p,context);
  }

  void rescale_per_pixel_matrices();

  void draw_fps( float realfps );
  void draw_stats();
  void draw_help();
  void draw_preset();
  void draw_title();
  void draw_title_to_screen(bool flip);
  void draw_title_to_texture();

};

#endif
<commit_msg>Perhaps I went to far.  glInterleavedArrays + glDrawElements in a huge (w * h * 5 * 2) array. Benefits: dont have to reassign vertex coords each frame, faster drawing. Disadvantages: memory usage<commit_after>#ifndef Renderer_HPP
#define Renderer_HPP

#include "FBO.hpp"
#include "PresetFrameIO.hpp"
#include "BeatDetect.hpp"
#include <string>
#include <set>

#ifdef USE_GLES1
#include <GLES/gl.h>
#else
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#endif

#ifdef USE_FTGL
#ifdef WIN32
#include <FTGL.h>
#include <FTGLPixmapFont.h>
#include <FTGLExtrdFont.h>
#else
#include <FTGL/FTGL.h>
#include <FTGL/FTGLPixmapFont.h>
#include <FTGL/FTGLExtrdFont.h>
#endif
#endif /** USE_FTGL */

#include "Pipeline.hpp"
#include "PerPixelMesh.hpp"
#include "Transformation.hpp"
#include "ShaderEngine.hpp"

class UserTexture;
class BeatDetect;
class TextureManager;

class Renderer
{

public:

  bool showfps;
  bool showtitle;
  bool showpreset;
  bool showhelp;
  bool showstats;

  bool studio;
  bool correction;

  bool noSwitch;

  int totalframes;
  float realfps;

  std::string title;
  int drawtitle;
  int texsize;

  Renderer( int width, int height, int gx, int gy, int texsize,  BeatDetect *beatDetect, std::string presetURL, std::string title_fontURL, std::string menu_fontURL);
  ~Renderer();
  void RenderFrame(PresetOutputs &PresetOutputs, const PipelineContext &pipelineContext);
  void RenderFrame(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void ResetTextures();
  void reset(int w, int h);
  GLuint initRenderToTexture();

  void SetPipeline(Pipeline &pipeline);

  void setPresetName(const std::string& theValue)
  {
    m_presetName = theValue;
  }

  std::string presetName() const
  {
    return m_presetName;
  }

private:

	PerPixelMesh mesh;
  RenderTarget *renderTarget;
  BeatDetect *beatDetect;
  TextureManager *textureManager;
  static Pipeline* currentPipe;
  RenderContext renderContext;
  //per pixel equation variables
#ifdef USE_CG
  ShaderEngine shaderEngine;
#endif
  std::string m_presetName;

  float* p;

  int vw;
  int vh;

  float aspect;

  std::string title_fontURL;
  std::string menu_fontURL;
  std::string presetURL;

#ifdef USE_FTGL
  FTGLPixmapFont *title_font;
  FTGLPixmapFont *other_font;
  FTGLExtrdFont *poly_font;
#endif /** USE_FTGL */

  void SetupPass1(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void Interpolation(const Pipeline &pipeline);
  void RenderItems(const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void FinishPass1();
  void Pass2 (const Pipeline &pipeline, const PipelineContext &pipelineContext);
  void CompositeOutput(const Pipeline &pipeline, const PipelineContext &pipelineContext);

  inline static Point PerPixel(Point p, PerPixelContext &context)
  {
	  return currentPipe->PerPixel(p,context);
  }

  void rescale_per_pixel_matrices();

  void draw_fps( float realfps );
  void draw_stats();
  void draw_help();
  void draw_preset();
  void draw_title();
  void draw_title_to_screen(bool flip);
  void draw_title_to_texture();

};

#endif
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------\
|                                                                      |
|                     _     _   _   _     __     _                     |
|                    | |   | | | \_/ |   /  \   | |                    |
|                    | |   | | | |_| |  / /\ \  | |                    |
|                    | |__ | | | | | | / ____ \ | |__                  |
|                    |____||_| |_| |_|/ /    \ \|____|                 |
|                                                                      |
|                             ca-mgm library                           |
|                                                                      |
|                                         (C) SUSE Linux Products GmbH |
\----------------------------------------------------------------------/

  File:       CRLData.hpp

  Author:     <Michael Calmer>     <mc@suse.de>
  Maintainer: <Michael Calmer>     <mc@suse.de>

  Purpose:

/-*/
#ifndef    LIMAL_CA_MGM_CRL_DATA_HPP
#define    LIMAL_CA_MGM_CRL_DATA_HPP

#include  <limal/ca-mgm/config.h>
#include  <limal/ca-mgm/CommonData.hpp>
#include  <limal/ca-mgm/CRLReason.hpp>
#include  <limal/ca-mgm/DNObject.hpp>
#include  <limal/ca-mgm/X509v3CRLExtensions.hpp>
#include  <limal/ByteBuffer.hpp>

namespace LIMAL_NAMESPACE {

namespace CA_MGM_NAMESPACE {

    class RevocationEntry {
    public:
        RevocationEntry();
        RevocationEntry(const RevocationEntry& entry);
        virtual ~RevocationEntry();

        RevocationEntry&
        operator=(const RevocationEntry& entry);

        String
        getSerial() const;

        time_t
        getRevocationDate() const;

        CRLReason
        getReason() const;

        virtual bool
        valid() const;

        virtual blocxx::StringArray
        verify() const;

        virtual blocxx::StringArray
        dump() const;

    protected:

        String      serial;
		String      fingerprint;
        time_t      revocationDate;
        CRLReason   revocationReason;

    };

    /**
     * @brief Read-only data representation of a CRL
     *
     * This class is a read-only data representation of a CRL.
     */
    class CRLData {
    public:
        CRLData(const CRLData& data);
        virtual ~CRLData();

        CRLData&
        operator=(const CRLData& data);

        blocxx::Int32
        getVersion() const;

		String
		getFingerprint() const;

		time_t
		getLastUpdateDate() const;

        time_t
        getNextUpdateDate() const;

        DNObject
        getIssuerDN() const;

        SigAlg
        getSignatureAlgorithm() const;

        String
        getSignatureAlgorithmAsString() const;

        limal::ByteBuffer
        getSignature() const;

        X509v3CRLExts
        getExtensions() const;

        blocxx::Map<String, RevocationEntry>
        getRevocationData() const;

        RevocationEntry
        getRevocationEntry(const String& oid);

    	/**
    	 * Return the CRL data as human readable text.
    	 * (Format may change)
    	 */
    	String
    	getCRLAsText() const;

    	/**
    	 * Return the CRL extensions as human readable text.
    	 * (Format may change)
    	 */
    	String
    	getExtensionsAsText() const;

        virtual bool
        valid() const;

        virtual blocxx::StringArray
        verify() const;

        virtual blocxx::StringArray
        dump() const;

    protected:
        CRLData();

        blocxx::Int32                        version;
		String                               fingerprint;
        time_t                               lastUpdate;
        time_t                               nextUpdate;

        DNObject                             issuer;

        SigAlg                               signatureAlgorithm;
        ByteBuffer                           signature;

        X509v3CRLExts                        extensions;

        blocxx::Map<String, RevocationEntry> revocationData;

    	String                               text;
    	String                               extText;

        blocxx::StringArray
        checkRevocationData(const blocxx::Map<String, RevocationEntry>& rd) const;

    };

}
}

#endif // LIMAL_CA_MGM_CRL_DATA_HPP
<commit_msg>remove unused member fingerprint<commit_after>/*---------------------------------------------------------------------\
|                                                                      |
|                     _     _   _   _     __     _                     |
|                    | |   | | | \_/ |   /  \   | |                    |
|                    | |   | | | |_| |  / /\ \  | |                    |
|                    | |__ | | | | | | / ____ \ | |__                  |
|                    |____||_| |_| |_|/ /    \ \|____|                 |
|                                                                      |
|                             ca-mgm library                           |
|                                                                      |
|                                         (C) SUSE Linux Products GmbH |
\----------------------------------------------------------------------/

  File:       CRLData.hpp

  Author:     <Michael Calmer>     <mc@suse.de>
  Maintainer: <Michael Calmer>     <mc@suse.de>

  Purpose:

/-*/
#ifndef    LIMAL_CA_MGM_CRL_DATA_HPP
#define    LIMAL_CA_MGM_CRL_DATA_HPP

#include  <limal/ca-mgm/config.h>
#include  <limal/ca-mgm/CommonData.hpp>
#include  <limal/ca-mgm/CRLReason.hpp>
#include  <limal/ca-mgm/DNObject.hpp>
#include  <limal/ca-mgm/X509v3CRLExtensions.hpp>
#include  <limal/ByteBuffer.hpp>

namespace LIMAL_NAMESPACE {

namespace CA_MGM_NAMESPACE {

    class RevocationEntry {
    public:
        RevocationEntry();
        RevocationEntry(const RevocationEntry& entry);
        virtual ~RevocationEntry();

        RevocationEntry&
        operator=(const RevocationEntry& entry);

        String
        getSerial() const;

        time_t
        getRevocationDate() const;

        CRLReason
        getReason() const;

        virtual bool
        valid() const;

        virtual blocxx::StringArray
        verify() const;

        virtual blocxx::StringArray
        dump() const;

    protected:

        String      serial;
        time_t      revocationDate;
        CRLReason   revocationReason;

    };

    /**
     * @brief Read-only data representation of a CRL
     *
     * This class is a read-only data representation of a CRL.
     */
    class CRLData {
    public:
        CRLData(const CRLData& data);
        virtual ~CRLData();

        CRLData&
        operator=(const CRLData& data);

        blocxx::Int32
        getVersion() const;

		String
		getFingerprint() const;

		time_t
		getLastUpdateDate() const;

        time_t
        getNextUpdateDate() const;

        DNObject
        getIssuerDN() const;

        SigAlg
        getSignatureAlgorithm() const;

        String
        getSignatureAlgorithmAsString() const;

        limal::ByteBuffer
        getSignature() const;

        X509v3CRLExts
        getExtensions() const;

        blocxx::Map<String, RevocationEntry>
        getRevocationData() const;

        RevocationEntry
        getRevocationEntry(const String& oid);

    	/**
    	 * Return the CRL data as human readable text.
    	 * (Format may change)
    	 */
    	String
    	getCRLAsText() const;

    	/**
    	 * Return the CRL extensions as human readable text.
    	 * (Format may change)
    	 */
    	String
    	getExtensionsAsText() const;

        virtual bool
        valid() const;

        virtual blocxx::StringArray
        verify() const;

        virtual blocxx::StringArray
        dump() const;

    protected:
        CRLData();

        blocxx::Int32                        version;
		String                               fingerprint;
        time_t                               lastUpdate;
        time_t                               nextUpdate;

        DNObject                             issuer;

        SigAlg                               signatureAlgorithm;
        ByteBuffer                           signature;

        X509v3CRLExts                        extensions;

        blocxx::Map<String, RevocationEntry> revocationData;

    	String                               text;
    	String                               extText;

        blocxx::StringArray
        checkRevocationData(const blocxx::Map<String, RevocationEntry>& rd) const;

    };

}
}

#endif // LIMAL_CA_MGM_CRL_DATA_HPP
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright (C) 2017 ScyllaDB
 */

#pragma once

#include <linux/aio_abi.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <cstdint>

namespace seastar {

namespace internal {

::iocb make_read_iocb(int fd, uint64_t offset, void* buffer, size_t len);
::iocb make_write_iocb(int fd, uint64_t offset, const void* buffer, size_t len);
::iocb make_readv_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov);
::iocb make_writev_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov);

void set_user_data(::iocb& iocb, void* data);
void* get_user_data(const ::iocb& iocb);

void set_eventfd_notification(::iocb& iocb, int eventfd);

::iocb* get_iocb(const ::io_event& ioev);

int io_setup(int nr_events, ::aio_context_t* io_context);
int io_destroy(::aio_context_t io_context);
int io_submit(::aio_context_t io_context, long nr, ::iocb** iocbs);
int io_cancel(::aio_context_t io_context, ::iocb* iocb, ::io_event* result);
int io_getevents(::aio_context_t io_context, long min_nr, long nr, ::io_event* events, const ::timespec* timeout);

}

namespace internal {

inline
::iocb
make_read_iocb(int fd, uint64_t offset, void* buffer, size_t len) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PREAD;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(buffer);
    iocb.aio_nbytes = len;
    return iocb;
}

inline
::iocb
make_write_iocb(int fd, uint64_t offset, const void* buffer, size_t len) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(buffer);
    iocb.aio_nbytes = len;
    return iocb;
}

inline
::iocb
make_readv_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PREADV;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(iov);
    iocb.aio_nbytes = niov;
    return iocb;
}

inline
::iocb
make_writev_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PWRITEV;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(iov);
    iocb.aio_nbytes = niov;
    return iocb;
}

inline
void
set_user_data(::iocb& iocb, void* data) {
    iocb.aio_data = reinterpret_cast<uintptr_t>(data);
}

inline
void*
get_user_data(const ::iocb& iocb) {
    return reinterpret_cast<void*>(uintptr_t(iocb.aio_data));
}

inline
void
set_eventfd_notification(::iocb& iocb, int eventfd) {
    iocb.aio_flags |= IOCB_FLAG_RESFD;
    iocb.aio_resfd = eventfd;
}

inline
::iocb*
get_iocb(const ::io_event& ev) {
    return reinterpret_cast<::iocb*>(uintptr_t(ev.obj));
}

}

}

<commit_msg>linux-aio: add support for NOWAIT aio<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright (C) 2017 ScyllaDB
 */

#pragma once

#include <linux/aio_abi.h>
#include <linux/fs.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <cstdint>

namespace seastar {

namespace internal {

::iocb make_read_iocb(int fd, uint64_t offset, void* buffer, size_t len);
::iocb make_write_iocb(int fd, uint64_t offset, const void* buffer, size_t len);
::iocb make_readv_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov);
::iocb make_writev_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov);

void set_user_data(::iocb& iocb, void* data);
void* get_user_data(const ::iocb& iocb);
void set_nowait(::iocb& iocb, bool nowait);

void set_eventfd_notification(::iocb& iocb, int eventfd);

::iocb* get_iocb(const ::io_event& ioev);

int io_setup(int nr_events, ::aio_context_t* io_context);
int io_destroy(::aio_context_t io_context);
int io_submit(::aio_context_t io_context, long nr, ::iocb** iocbs);
int io_cancel(::aio_context_t io_context, ::iocb* iocb, ::io_event* result);
int io_getevents(::aio_context_t io_context, long min_nr, long nr, ::io_event* events, const ::timespec* timeout);

}

namespace internal {

inline
::iocb
make_read_iocb(int fd, uint64_t offset, void* buffer, size_t len) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PREAD;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(buffer);
    iocb.aio_nbytes = len;
    return iocb;
}

inline
::iocb
make_write_iocb(int fd, uint64_t offset, const void* buffer, size_t len) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(buffer);
    iocb.aio_nbytes = len;
    return iocb;
}

inline
::iocb
make_readv_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PREADV;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(iov);
    iocb.aio_nbytes = niov;
    return iocb;
}

inline
::iocb
make_writev_iocb(int fd, uint64_t offset, const ::iovec* iov, size_t niov) {
    ::iocb iocb{};
    iocb.aio_lio_opcode = IOCB_CMD_PWRITEV;
    iocb.aio_fildes = fd;
    iocb.aio_offset = offset;
    iocb.aio_buf = reinterpret_cast<uintptr_t>(iov);
    iocb.aio_nbytes = niov;
    return iocb;
}

inline
void
set_user_data(::iocb& iocb, void* data) {
    iocb.aio_data = reinterpret_cast<uintptr_t>(data);
}

inline
void*
get_user_data(const ::iocb& iocb) {
    return reinterpret_cast<void*>(uintptr_t(iocb.aio_data));
}

inline
void
set_eventfd_notification(::iocb& iocb, int eventfd) {
    iocb.aio_flags |= IOCB_FLAG_RESFD;
    iocb.aio_resfd = eventfd;
}

inline
::iocb*
get_iocb(const ::io_event& ev) {
    return reinterpret_cast<::iocb*>(uintptr_t(ev.obj));
}

inline
void
set_nowait(::iocb& iocb, bool nowait) {
#ifdef RWF_NOWAIT
    if (nowait) {
        iocb.aio_rw_flags |= RWF_NOWAIT;
    } else {
        iocb.aio_rw_flags &= ~RWF_NOWAIT;
    }
#endif
}

}

}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

// Includes needed for tests.
#include <functional>
#include <vector>

namespace choreograph
{
// A motion describes a one-dimensional change through time.
// These can be ease equations, always return the same value, or sample some data. Whatever you want.
// For classic motion, EaseFn( 0 ) = 0, EaseFn( 1 ) = 1.
// set( 0.0f ).move( )
// then again, might just make a curve struct that is callable and have a way to set its initial and final derivatives.
typedef std::function<float (float)> EaseFn;

namespace
{
  //! Default ease function for holds. Keeps constant value output.
  inline float easeHold( float t ) { return 0.0f; }
  //! Default ease function for ramps.
  inline float easeNone( float t ) { return t; }

} // namespace

//! A Position describes a point in time.
template<typename T>
struct Position
{
  Position() = default;

  Position( const T &value, float time = 0.0f ):
  time( time ),
  value( value )
  {}

  float time = 0.0f;
  T     value;
};

//! The default templated lerp function.
template<typename T>
T lerpT( const T &a, const T &b, float t )
{
  return a + (b - a) * t;
}

/**
 A Phrase is a part of a Sequence.
 It describes the motion between two positions.
 This is the essence of a Tween, with all values held internally.
 */
template<typename T>
class Phrase
{
public:
  using LerpFn = std::function<T (const T&, const T&, float)>;

  Phrase( const T &end, float duration, const EaseFn &easeFn = &easeNone ):
  end( end, duration ),
  motion( easeFn )
  {}

  Phrase( const Position<T> &start, const Position<T> &end, const EaseFn &easeFn = &easeNone ):
  start( start ),
  end( end ),
  motion( easeFn )
  {}

  ~Phrase() = default;

  //! Set start time to \a time. End time is adjusted to preserve duration.
  void shiftStartTimeTo( float time ) {
    float delta = time - start.time;
    start.time = time;
    end.time += delta;
  }

  //! Change start value to \a value.
  void setStartValue( const T &value )
  {
    start.value = value;
  }

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return start.value; }
  //! Returns the value at the end of this phrase.
  inline const T& getEndValue() const { return end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return start.time; }
  //! Returns the time at the end of this phrase.
  inline float getEndTime() const { return end.time; }

  //! Returns normalized time if t is in range [start.time, end.time].
  inline float normalizeTime( float t ) const { return (t - start.time) / (end.time - start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return end.time - start.time; }

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const
  {
    return lerpFn( start.value, end.value, motion( normalizeTime( atTime ) ) );
  }
private:
  Position<T> start;
  Position<T> end;
  EaseFn      motion;
  LerpFn      lerpFn = &lerpT<T>;
};

/**
 Phrase with separately-interpolated components.
 Allows for the use of separate ease functions per component.
 All components must be of the same type.
 */
template<typename T>
class Phrase2
{
public:
  Phrase2( const T &end, float duration, const EaseFn &ease_x, const EaseFn &ease_y ):
    _end( end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  Phrase2( const Position<T> &start, const Position<T> &end, const EaseFn &ease_x, const EaseFn &ease_y ):
    _start( start ),
    _end( end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return _start.value; }
  //! Returns the value at the end of this phrase.
  inline const T& getEndValue() const { return _end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return _start.time; }
  //! Returns the time at the end of this phrase.
  inline float getEndTime() const { return _end.time; }

  //! Returns normalized time if t is in range [start.time, end.time].
  inline float normalizeTime( float t ) const { return (t - _start.time) / (_end.time - _start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return _end.time - _start.time; }

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const
  {
    float t = normalizeTime( atTime );
    return T( componentLerpFn( getStartValue().x, getEndValue().x, motion_x( t ) ),
             componentLerpFn( getStartValue().y, getEndValue().y, motion_y( t ) ) );
  }

  //! Set ease functions for first and second components.
  void setEase( const EaseFn &component_0, const EaseFn &component_1 ) {
    motion_x = component_0;
    motion_y = component_1;
  }

private:
  using ComponentT = decltype( T().x ); // get the type of the x component;
  using ComponentLerpFn = std::function<ComponentT (const ComponentT&, const ComponentT&, float)>;

  ComponentLerpFn componentLerpFn = &lerpT<ComponentT>;
  Position<T>     _start;
  Position<T>     _end;
  EaseFn          motion_x;
  EaseFn          motion_y;
};

} // namespace atlantic
<commit_msg>Variable names.<commit_after>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

// Includes needed for tests.
#include <functional>
#include <vector>

namespace choreograph
{
// A motion describes a one-dimensional change through time.
// These can be ease equations, always return the same value, or sample some data. Whatever you want.
// For classic motion, EaseFn( 0 ) = 0, EaseFn( 1 ) = 1.
// set( 0.0f ).move( )
// then again, might just make a curve struct that is callable and have a way to set its initial and final derivatives.
typedef std::function<float (float)> EaseFn;

namespace
{
  //! Default ease function for holds. Keeps constant value output.
  inline float easeHold( float t ) { return 0.0f; }
  //! Default ease function for ramps.
  inline float easeNone( float t ) { return t; }

} // namespace

//! A Position describes a point in time.
template<typename T>
struct Position
{
  Position() = default;

  Position( const T &value, float time = 0.0f ):
  time( time ),
  value( value )
  {}

  float time = 0.0f;
  T     value;
};

//! The default templated lerp function.
template<typename T>
T lerpT( const T &a, const T &b, float t )
{
  return a + (b - a) * t;
}

/**
 A Phrase is a part of a Sequence.
 It describes the motion between two positions.
 This is the essence of a Tween, with all values held internally.
 */
template<typename T>
class Phrase
{
public:
  using LerpFn = std::function<T (const T&, const T&, float)>;

  Phrase( const T &_end, float duration, const EaseFn &easeFn = &easeNone ):
  _end( _end, duration ),
  _easeFn( easeFn )
  {}

  Phrase( const Position<T> &start, const Position<T> &_end, const EaseFn &easeFn = &easeNone ):
  _start( start ),
  _end( _end ),
  _easeFn( easeFn )
  {}

  ~Phrase() = default;

  //! Set start time to \a time. End time is adjusted to preserve duration.
  void shiftStartTimeTo( float time ) {
    float delta = time - _start.time;
    _start.time = time;
    _end.time += delta;
  }

  //! Change start value to \a value.
  void setStartValue( const T &value )
  {
    _start.value = value;
  }

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return _start.value; }
  //! Returns the value at the end of this phrase.
  inline const T& getEndValue() const { return _end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return _start.time; }
  //! Returns the time at the end of this phrase.
  inline float getEndTime() const { return _end.time; }

  //! Returns normalized time if t is in range [_start.time, _end.time].
  inline float normalizeTime( float t ) const { return (t - _start.time) / (_end.time - _start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return _end.time - _start.time; }

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const
  {
    return _lerpFn( _start.value, _end.value, _easeFn( normalizeTime( atTime ) ) );
  }
private:
  Position<T> _start;
  Position<T> _end;
  EaseFn      _easeFn;
  LerpFn      _lerpFn = &lerpT<T>;
};

/**
 Phrase with separately-interpolated components.
 Allows for the use of separate ease functions per component.
 All components must be of the same type.
 */
template<typename T>
class Phrase2
{
public:
  Phrase2( const T &_end, float duration, const EaseFn &ease_x, const EaseFn &ease_y ):
    _end( _end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  Phrase2( const Position<T> &start, const Position<T> &_end, const EaseFn &ease_x, const EaseFn &ease_y ):
    _start( start ),
    _end( _end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return _start.value; }
  //! Returns the value at the _end of this phrase.
  inline const T& getEndValue() const { return _end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return _start.time; }
  //! Returns the time at the _end of this phrase.
  inline float getEndTime() const { return _end.time; }

  //! Returns normalized time if t is in range [start.time, _end.time].
  inline float normalizeTime( float t ) const { return (t - _start.time) / (_end.time - _start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return _end.time - _start.time; }

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const
  {
    float t = normalizeTime( atTime );
    return T( componentLerpFn( getStartValue().x, getEndValue().x, motion_x( t ) ),
             componentLerpFn( getStartValue().y, getEndValue().y, motion_y( t ) ) );
  }

  //! Set ease functions for first and second components.
  void setEase( const EaseFn &component_0, const EaseFn &component_1 ) {
    motion_x = component_0;
    motion_y = component_1;
  }

private:
  using ComponentT = decltype( T().x ); // get the type of the x component;
  using ComponentLerpFn = std::function<ComponentT (const ComponentT&, const ComponentT&, float)>;

  ComponentLerpFn componentLerpFn = &lerpT<ComponentT>;
  Position<T>     _start;
  Position<T>     _end;
  EaseFn          motion_x;
  EaseFn          motion_y;
};

} // namespace atlantic
<|endoftext|>